Configuration interface
This commit is contained in:
2
README
2
README
@@ -128,6 +128,8 @@ Bauteilliste
|
||||
------------
|
||||
|
||||
1x ESP32-S3 Nano (Waveshare)
|
||||
berrybase.de
|
||||
eckstein-shop.de
|
||||
5x Taster schwarz
|
||||
2x Taster gelb
|
||||
1x M12 Einbaubuchse
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[platformio]
|
||||
default_envs=
|
||||
esp32-s3-nano
|
||||
esp32-s3-nano
|
||||
|
||||
[env]
|
||||
platform = espressif32
|
||||
@@ -9,18 +9,19 @@ lib_deps =
|
||||
Preferences
|
||||
Wifi
|
||||
Wire
|
||||
ArduinoJson @ 6.18.5
|
||||
ESP32Async/AsyncTCP@3.4.9
|
||||
ESP32Async/ESPAsyncWebServer@3.9.1
|
||||
ttlappalainen/NMEA2000-library@4.24
|
||||
robtillaart/SHT31@^0.5.2
|
||||
# adafruit/Adafruit NeoPixel
|
||||
|
||||
# only these files will be emedded into firmware
|
||||
board_build.embed_files =
|
||||
lib/generated/index.html.gz
|
||||
lib/generated/index.js.gz
|
||||
lib/generated/index.css.gz
|
||||
lib/generated/config.json.gz
|
||||
lib/generated/index.html.gz
|
||||
lib/generated/index.js.gz
|
||||
lib/generated/sha256.js.gz
|
||||
lib/generated/index.css.gz
|
||||
lib/generated/config.json.gz
|
||||
|
||||
extra_scripts =
|
||||
pre:extra_pre.py
|
||||
|
||||
171
src/main.cpp
171
src/main.cpp
@@ -1,5 +1,6 @@
|
||||
#include <Arduino.h>
|
||||
#include <Preferences.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <WiFi.h>
|
||||
#include <AsyncTCP.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
@@ -12,6 +13,40 @@
|
||||
#include "Nmea2kTwai.h"
|
||||
#include <map>
|
||||
|
||||
|
||||
#include "mbedtls/md.h" // for SHA256
|
||||
#include <esp32/clk.h> // for cpu frequency
|
||||
|
||||
String get_sha256(String payload) {
|
||||
byte shaResult[32];
|
||||
mbedtls_md_context_t ctx;
|
||||
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
|
||||
mbedtls_md_init(&ctx);
|
||||
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0);
|
||||
mbedtls_md_starts(&ctx);
|
||||
mbedtls_md_update(&ctx, (const unsigned char *) payload.c_str(), payload.length());
|
||||
mbedtls_md_finish(&ctx, shaResult);
|
||||
mbedtls_md_free(&ctx);
|
||||
// convert to hex string
|
||||
char buffer[sizeof(shaResult)*2 + 1];
|
||||
const char hexmap[] = "0123456789abcdef";
|
||||
for (int i = 0; i < sizeof(shaResult); i++) {
|
||||
buffer[i*2] = hexmap[(shaResult[i] >> 4) & 0x0F];
|
||||
buffer[i*2+1] = hexmap[shaResult[i] & 0x0F];
|
||||
}
|
||||
buffer[sizeof(buffer) - 1] = '\0';
|
||||
String hash = String(buffer);
|
||||
|
||||
Serial.print("SHA256 payload: ");
|
||||
Serial.print(payload);
|
||||
Serial.println();
|
||||
Serial.print("SHA256-Hash: ");
|
||||
Serial.print(hash);
|
||||
Serial.println();
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
// Logging
|
||||
static const char* TAG = "main.cpp";
|
||||
|
||||
@@ -51,10 +86,13 @@ void send_embedded_file(String name, AsyncWebServerRequest *request)
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t chipid = ESP.getEfuseMac();
|
||||
|
||||
const char* wifi_ssid = "OBPKP61";
|
||||
const char* wifi_pass = "keypad61";
|
||||
AsyncWebServer server(80);
|
||||
|
||||
unsigned long lastSensor = 0;
|
||||
unsigned long lastPrint = 0;
|
||||
unsigned long counter = 0;
|
||||
|
||||
@@ -70,6 +108,8 @@ uint buzzerpower = 50; // TBD make use of this
|
||||
|
||||
SHT31 sht(SHT31_ADDRESS);
|
||||
bool sht_available = false;
|
||||
float temp = 0.0;
|
||||
float hum = 0.0;
|
||||
|
||||
int nodeid; // NMEA2000 id on bus
|
||||
Nmea2kTwai &NMEA2000=*(new Nmea2kTwai(CAN_TX, CAN_RX, CAN_RECOVERY_PERIOD));
|
||||
@@ -80,45 +120,26 @@ String processor(const String& var) {
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Low level wifi setup (alternative)
|
||||
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
// printf("Event nr: %ld!\n", event_id);
|
||||
String uptime_with_unit() {
|
||||
int64_t uptime = esp_timer_get_time() / 1000000;
|
||||
String uptime_unit;
|
||||
if (uptime < 120) {
|
||||
uptime_unit = " seconds";
|
||||
} else {
|
||||
if (uptime < 2 * 3600) {
|
||||
uptime /= 60;
|
||||
uptime_unit = " minutes";
|
||||
} else if (uptime < 2 * 3600 * 24) {
|
||||
uptime /= 3600;
|
||||
uptime_unit = " hours";
|
||||
} else {
|
||||
uptime /= 86400;
|
||||
uptime_unit = " days";
|
||||
}
|
||||
}
|
||||
return String(uptime) + " " + uptime_unit;
|
||||
}
|
||||
|
||||
void wifi_init_softap()
|
||||
{
|
||||
esp_netif_init();
|
||||
esp_event_loop_create_default();
|
||||
esp_netif_create_default_wifi_ap();
|
||||
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL);
|
||||
esp_wifi_init(&cfg);
|
||||
wifi_config_t wifi_config = {
|
||||
.ap = {
|
||||
.ssid = wifi_ssid,
|
||||
.ssid_len = strlen(wifi_ssid),
|
||||
.channel = WIFI_CHANNEL,
|
||||
.password = wifi_pass,
|
||||
.max_connection = WIFI_MAX_STA,
|
||||
.authmode = WIFI_AUTH_WPA2_PSK,
|
||||
.pmf_cfg = {
|
||||
.required = true,
|
||||
},
|
||||
},
|
||||
};
|
||||
esp_wifi_set_mode(WIFI_MODE_AP);
|
||||
esp_wifi_set_config(WIFI_IF_AP, &wifi_config);
|
||||
esp_wifi_start();
|
||||
|
||||
ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
|
||||
ESP_WIFI_SSID, ESP_WIFI_PASS, ESP_WIFI_CHANNEL);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
@@ -167,16 +188,65 @@ void setup() {
|
||||
send_embedded_file(it->first, request);
|
||||
});
|
||||
}
|
||||
// WIP: API
|
||||
/*
|
||||
server.on("/api/status", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
// API fast hack
|
||||
server.on("/api/capabilities", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
StaticJsonDocument<100> doc;
|
||||
doc["temp"] = 22.3;
|
||||
doc["ip"] = WiFi.localIP().toString();
|
||||
doc["apPwChange"] = "true";
|
||||
String out;
|
||||
serializeJson(doc, out);
|
||||
request->send(200, "application/json", out);
|
||||
}); */
|
||||
});
|
||||
server.on("/api/checkpass", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
StaticJsonDocument<100> doc;
|
||||
doc["status"] = "FAILED";
|
||||
String out;
|
||||
serializeJson(doc, out);
|
||||
request->send(200, "application/json", out);
|
||||
});
|
||||
server.on("/api/config", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
StaticJsonDocument<100> doc;
|
||||
doc["systemName"] = "Keypad1";
|
||||
doc["version"] = "0.0";
|
||||
doc["fwtype"] = "unknown";
|
||||
doc["salt"] = "secret";
|
||||
String out;
|
||||
serializeJson(doc, out);
|
||||
request->send(200, "application/json", out);
|
||||
});
|
||||
server.on("/api/resetconfig", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
StaticJsonDocument<100> doc;
|
||||
doc["status"] = "FAILED";
|
||||
String out;
|
||||
serializeJson(doc, out);
|
||||
request->send(200, "application/json", out);
|
||||
});
|
||||
server.on("/api/status", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
StaticJsonDocument<200> doc;
|
||||
|
||||
int cpu_freq = esp_clk_cpu_freq() / 1000000;
|
||||
doc["cpuspeed"] = String(cpu_freq) + "MHz";
|
||||
|
||||
char ssid[13];
|
||||
snprintf(ssid, 13, "%04X%08X", (uint16_t)(chipid >> 32), (uint32_t)chipid);
|
||||
doc["chipid"] = String(ssid);
|
||||
|
||||
doc["uptime"] = uptime_with_unit();
|
||||
doc["heap"]=(long)xPortGetFreeHeapSize();
|
||||
doc["temp"] = String(temp, 1);
|
||||
doc["hum"] = String(hum, 1);
|
||||
doc["status"] = "OK";
|
||||
String out;
|
||||
serializeJson(doc, out);
|
||||
request->send(200, "application/json", out);
|
||||
});
|
||||
server.on("/api/setconfig", HTTP_POST, [](AsyncWebServerRequest *request){
|
||||
StaticJsonDocument<100> doc;
|
||||
doc["status"] = "FAILED";
|
||||
String out;
|
||||
serializeJson(doc, out);
|
||||
request->send(200, "application/json", out);
|
||||
});
|
||||
|
||||
// TODO POST vom Client entgegennehmen
|
||||
|
||||
server.begin();
|
||||
@@ -299,6 +369,9 @@ void setup() {
|
||||
Serial.print(stat, HEX);
|
||||
Serial.println();
|
||||
|
||||
// Additional tests
|
||||
String passhash = get_sha256("secretTEST");
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
@@ -390,10 +463,9 @@ void loop() {
|
||||
}
|
||||
|
||||
if (button > 0) {
|
||||
sht.read();
|
||||
Serial.print(sht.getTemperature(), 1);
|
||||
Serial.print(temp, 1);
|
||||
Serial.print("\t");
|
||||
Serial.println(sht.getHumidity(), 1);
|
||||
Serial.println(hum, 1);
|
||||
// Debounce delay to avoid multiple triggers
|
||||
delay(200);
|
||||
}
|
||||
@@ -402,6 +474,13 @@ void loop() {
|
||||
// NMEA2000.loop();
|
||||
// NMEA2000.ParseMessages();
|
||||
|
||||
if (millis() - lastSensor >= 5000) {
|
||||
lastSensor = millis();
|
||||
sht.read();
|
||||
temp = sht.getTemperature();
|
||||
hum = sht.getHumidity();
|
||||
}
|
||||
|
||||
// development heartbeat
|
||||
if (millis() - lastPrint >= 1000) {
|
||||
lastPrint = millis();
|
||||
|
||||
119
web/config.json
119
web/config.json
@@ -1,20 +1,129 @@
|
||||
[
|
||||
{
|
||||
"name": "systemName",
|
||||
"label": "system name",
|
||||
"label": "System name",
|
||||
"type": "string",
|
||||
"default": "OBPkeypad61",
|
||||
"check": "checkSystemName",
|
||||
"description": "system name, used for the access point and for services",
|
||||
"category": "system"
|
||||
"description": "System name, used also for the access point SSID.",
|
||||
"category": "System"
|
||||
},
|
||||
{
|
||||
"name": "logLevel",
|
||||
"label": "Log level",
|
||||
"type": "list",
|
||||
"default": "0",
|
||||
"list": [
|
||||
{"l":"Off (0)","v":0},
|
||||
{"l":"Error (1)","v":1},
|
||||
{"l":"Warning (2)","v":2},
|
||||
{"l":"Info (3)","v":3},
|
||||
{"l":"Debug (4)","v":4},
|
||||
{"l":"Verbose (5)","v":5}
|
||||
],
|
||||
"description": "Log level at the USB port.\nHigher level means more output.",
|
||||
"category": "System"
|
||||
},
|
||||
{
|
||||
"name": "adminPassword",
|
||||
"label": "Admin Password",
|
||||
"type": "password",
|
||||
"default": "esp32admin",
|
||||
"check": "checkAdminPass",
|
||||
"description": "Set the password for config modifications",
|
||||
"category": "System"
|
||||
},
|
||||
{
|
||||
"name": "useAdminPass",
|
||||
"label": "Use Admin-Pass",
|
||||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "A password for config modifications is required.",
|
||||
"category": "System"
|
||||
},
|
||||
{
|
||||
"name": "apPassword",
|
||||
"label": "Wifi password",
|
||||
"type": "password",
|
||||
"default": "keypad61",
|
||||
"check": "checkApPass",
|
||||
"description": "set the password for the Wifi access point",
|
||||
"category": "system",
|
||||
"description": "Set the password for the Wifi access point.",
|
||||
"category": "Wifi",
|
||||
"capabilities":{"apPwChange":["true"]}
|
||||
},
|
||||
{
|
||||
"name": "apIp",
|
||||
"label": "AP IP-Address",
|
||||
"type": "string",
|
||||
"default":"192.168.15.1",
|
||||
"check": "checkApIp",
|
||||
"description": "The IP address for the wifi access point.\nClients will get addresses within the same subnet.",
|
||||
"category":"Wifi"
|
||||
},
|
||||
{
|
||||
"name": "apMask",
|
||||
"label": "AP Net-Mask",
|
||||
"type": "string",
|
||||
"default": "255.255.255.0",
|
||||
"check": "checkNetMask",
|
||||
"description": "The network mask for the access point.",
|
||||
"category": "Wifi"
|
||||
},
|
||||
{
|
||||
"name": "stopApTime",
|
||||
"label": "AP time off",
|
||||
"type": "number",
|
||||
"default": "0",
|
||||
"min": 0,
|
||||
"max": 60,
|
||||
"check": "checkMinMax",
|
||||
"description": "Stop the access point after that many minutes if not used.\n1 to 60 minutes.\n\n'0' means that the access point is permanently enabled.",
|
||||
"category": "Wifi"
|
||||
},
|
||||
{
|
||||
"name": "cpuSpeed",
|
||||
"label": "CPU Speed [MHz]",
|
||||
"type": "list",
|
||||
"default": "160",
|
||||
"list": [
|
||||
"80",
|
||||
"160",
|
||||
"240"
|
||||
],
|
||||
"description": "CPU speed in MHz [80|160|240].\nSlower speed means less power consumption.",
|
||||
"category": "Hardware"
|
||||
},
|
||||
{
|
||||
"name": "ledBrightness",
|
||||
"label": "LED brightness",
|
||||
"type": "number",
|
||||
"default": 64,
|
||||
"min": 0,
|
||||
"max": 255,
|
||||
"description": "The brightness of the destination leds (0..255).",
|
||||
"category": "Hardware"
|
||||
},
|
||||
{
|
||||
"name": "ledRgbBrightness",
|
||||
"label": "RGB-LED brightness",
|
||||
"type": "number",
|
||||
"default": 64,
|
||||
"min": 0,
|
||||
"max": 255,
|
||||
"description": "The brightness of the rgb status led (0..255).",
|
||||
"category": "Hardware"
|
||||
},
|
||||
{
|
||||
"name": "tempFormat",
|
||||
"label": "Temperature Format",
|
||||
"type": "list",
|
||||
"default": "C",
|
||||
"list": [
|
||||
"K",
|
||||
"C",
|
||||
"F"
|
||||
],
|
||||
"description": "Temperature format: Kelvin, Celsius or Fahrenheit [K|C|F].",
|
||||
"category": "Units"
|
||||
}
|
||||
]
|
||||
|
||||
291
web/index.css
291
web/index.css
@@ -0,0 +1,291 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0.2em;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#tabs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
border-bottom: 1px solid grey;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
#tabs .tab {
|
||||
background-color: lightgray;
|
||||
padding: 0.5em;
|
||||
/*border: 1px;
|
||||
border-color: grey;
|
||||
border-style: solid; */
|
||||
border: 1px solid grey;
|
||||
opacity: 0.6;
|
||||
}
|
||||
#tabs .tab.active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#tabPages {
|
||||
overflow: auto;
|
||||
padding-bottom: 1ex;
|
||||
border-bottom: 1px solid grey;
|
||||
}
|
||||
|
||||
.configForm {
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
.configForm .buttons {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.configForm .content>div:nth-child(even) {
|
||||
background-color: rgb(211 211 211 / 43%);
|
||||
}
|
||||
#statusPage .even {
|
||||
background-color: rgb(211 211 211 / 43%);
|
||||
}
|
||||
#statusPageContent {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.counter-row .value{
|
||||
text-align: right;
|
||||
width: 6em;
|
||||
}
|
||||
.icon-row .label{
|
||||
width: 8.7em;
|
||||
}
|
||||
.category .title .label {
|
||||
opacity: 1;
|
||||
margin-left: 1em;
|
||||
}
|
||||
.changed input{
|
||||
color: green;
|
||||
}
|
||||
.changed select{
|
||||
color: green;
|
||||
}
|
||||
.category.changed{
|
||||
color: green;
|
||||
}
|
||||
span.label {
|
||||
width: 10em;
|
||||
display: inline-block;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.configForm .value {
|
||||
width: 21em;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 0.2em;
|
||||
}
|
||||
span#connected {
|
||||
display: inline-block;
|
||||
background-color: red;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
border-radius: 50%;
|
||||
}
|
||||
span#connected.ok{
|
||||
background-color: #13ac13;
|
||||
}
|
||||
.row {
|
||||
padding: 0.5em;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
input,select {
|
||||
border: 1px solid #808080a1;
|
||||
font-size: 0.9em;
|
||||
padding: 0.2em;
|
||||
}
|
||||
.filter {
|
||||
display: inline-block;
|
||||
}
|
||||
button.infoButton {
|
||||
margin-left: 1em;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
.category .title {
|
||||
padding-left: 0.5em;
|
||||
background-color: lightgray;
|
||||
padding-top: 0.3em;
|
||||
padding-bottom: 0.5em;
|
||||
border-bottom: 1px solid darkgray;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.value button {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
.hidden{
|
||||
display: none !important;
|
||||
}
|
||||
.dash.invalid{
|
||||
display: none;
|
||||
}
|
||||
button.addunassigned {
|
||||
margin-left: 1em;
|
||||
}
|
||||
.msgDetails .value {
|
||||
width: 5em;
|
||||
text-align: right;
|
||||
}
|
||||
.msgDetails .label {
|
||||
width: 5em;
|
||||
}
|
||||
.overlayContainer {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #80808070;
|
||||
display: flex;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
margin: auto;
|
||||
background-color: white;
|
||||
padding: 0.5em;
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.overlayContent {
|
||||
padding: 0.5em;
|
||||
}
|
||||
div#overlayContent.text{
|
||||
white-space: pre-line;
|
||||
}
|
||||
.overlayButtons {
|
||||
border-top: 1px solid grey;
|
||||
padding-top: 0.5em;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: end;
|
||||
}
|
||||
.buttons button {
|
||||
margin: 0.2ex 0;
|
||||
padding: 0.5em;
|
||||
}
|
||||
.overlayButtons button {
|
||||
padding: 0.5em;
|
||||
margin-left: 0.3em;
|
||||
}
|
||||
button#reset {
|
||||
padding: 0.5em;
|
||||
}
|
||||
h1 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.icon-eye {
|
||||
background-image: url("data:image/svg+xml;utf-8,<svg width='24' height='24' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'> <g class='layer'> <title>Layer 1</title> <path d='m0,0l24,0l0,24l-24,0l0,-24z' fill='none' id='svg_1'/> <path d='m12,6c3.79,0 7.17,2.13 8.82,5.5c-1.65,3.37 -5.03,5.5 -8.82,5.5s-7.17,-2.13 -8.82,-5.5c1.65,-3.37 5.03,-5.5 8.82,-5.5m0,-2c-5,0 -9.27,3.11 -11,7.5c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zm0,5c1.38,0 2.5,1.12 2.5,2.5s-1.12,2.5 -2.5,2.5s-2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5m0,-2c-2.48,0 -4.5,2.02 -4.5,4.5s2.02,4.5 4.5,4.5s4.5,-2.02 4.5,-4.5s-2.02,-4.5 -4.5,-4.5z' id='svg_2'/> </g> </svg>");
|
||||
margin-left: 0.5em;
|
||||
opacity: 0.3;
|
||||
}
|
||||
.icon{
|
||||
height: 1.5em;
|
||||
width: 1.5em;
|
||||
display: inline-block;
|
||||
}
|
||||
.icon-more{
|
||||
background-image: url("data:image/svg+xml;utf-8,<svg width='24' height='24' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'> <g class='layer'> <title>Layer 1</title> <path d='m24,24l-24,0l0,-24l24,0l0,24z' fill='none' id='svg_1' opacity='0.87'/> <path d='m16.59,8.59l-4.59,4.58l-4.59,-4.58l-1.41,1.41l6,6l6,-6l-1.41,-1.41z' id='svg_2'/> </g> </svg>");
|
||||
}
|
||||
.icon-less{
|
||||
background-image: url("data:image/svg+xml;utf-8,<svg width='24' height='24' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'> <g class='layer'> <title>Layer 1</title> <path d='m0,0l24,0l0,24l-24,0l0,-24z' fill='none' id='svg_1'/> <path d='m12,8l-6,6l1.41,1.41l4.59,-4.58l4.59,4.58l1.41,-1.41l-6,-6z' id='svg_2'/> </g> </svg>");
|
||||
}
|
||||
.icon-eye.active{
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.dash {
|
||||
width: 6.5em;
|
||||
height: 4em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid grey;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
font-size: 1.2em;
|
||||
justify-content: space-between;
|
||||
}
|
||||
div#dashboardPage {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
.dashTitle {
|
||||
font-size: 0.8em;
|
||||
background-color: lightgray;
|
||||
}
|
||||
.dashValue{
|
||||
font-size: 1.6em;
|
||||
text-align: center;
|
||||
}
|
||||
.dashValue.formatLatitude {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
.dashValue.formatLongitude {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
.dashValue.formatDate {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.footer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 0.1em;
|
||||
background-color: lightgray;
|
||||
font-size: 0.7em;
|
||||
}
|
||||
.footer .unit{
|
||||
}
|
||||
.footer .source{
|
||||
flex: 1;
|
||||
}
|
||||
#adminPassInput {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
input#uploadFile {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
div#uploadProgress {
|
||||
width: 100%;
|
||||
max-width: 20em;
|
||||
height: 1em;
|
||||
margin-left: 1em;
|
||||
/* margin-right: auto; */
|
||||
border: 1px solid grey;
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
#uploadDone{
|
||||
background-color: blue;
|
||||
width: 0px;
|
||||
height: 100%;
|
||||
}
|
||||
.error{
|
||||
color: red;
|
||||
}
|
||||
input.error{
|
||||
background-color: rgba(255, 0, 0, 0.329);
|
||||
}
|
||||
|
||||
163
web/index.html
163
web/index.html
@@ -1,17 +1,166 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>OBPkeypad 6/1</title>
|
||||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>OBPkeyboard 6/1</title>
|
||||
<link rel="icon" href="data:,">
|
||||
<script>
|
||||
if (!window.isSecureContext) {
|
||||
const s = document.createElement('script');
|
||||
s.src = 'sha256.js';
|
||||
document.head.appendChild(s);
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript" src="index.js"></script>
|
||||
<link rel="stylesheet" href="index.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="main">
|
||||
<h1>OBPkeypad 6/1</h1>
|
||||
<p>Work in progress</p>
|
||||
<p><a href="https://computerclub.hoogi.de/obpkeypad/">Furter Information</a></p>
|
||||
<h1 id="headline">OBPkb</h1>
|
||||
<div class="row">
|
||||
<span class="label" id="conn_label">disconnected</span>
|
||||
<span class="value" id="connected"></span>
|
||||
</div>
|
||||
<div id="tabs">
|
||||
<div class="tab active" data-page="statusPage">Status</div>
|
||||
<div class="tab" data-page="configPage">Config</div>
|
||||
<div class="tab" data-page="updatePage">Update</div>
|
||||
<div class="tab" data-url="https://computerclub.hoogi.de/obpkeypad" data-window="help" id="helpButton">Help</div>
|
||||
</div>
|
||||
<div id="tabPages">
|
||||
|
||||
<div id="statusPage" class="tabPage">
|
||||
<div id="statusPageContent">
|
||||
<div class="row">
|
||||
<span class="label">Firmware</span>
|
||||
<span class="value" id="version">---</span>
|
||||
<button class="infoButton" id="converterInfo">?</button>
|
||||
</div>
|
||||
<div class="row even">
|
||||
<span class="label">MCU-ID</span>
|
||||
<span class="value" id="chipid">---</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label">CPU speed</span>
|
||||
<span class="value" id="cpuspeed">---</span>
|
||||
</div>
|
||||
<div class="row even">
|
||||
<span class="label">Free heap</span>
|
||||
<span class="value" id="heap">---</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label">Uptime</span>
|
||||
<span class="value" id="uptime">---</span>
|
||||
</div>
|
||||
<div class="row even">
|
||||
<span class="label">NMEA2000 State</span>
|
||||
[<span class="value" id="n2knode">---</span>]
|
||||
<span class="value" id="n2kstate">UNKNOWN</span>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<span class="label">Dest A</span>
|
||||
<span class="value" id="destA">---</span>
|
||||
</div>
|
||||
<div class="row even">
|
||||
<span class="label">Dest B</span>
|
||||
<span class="value" id="destB">---</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label">Dest C</span>
|
||||
<span class="value" id="destC">---</span>
|
||||
</div>
|
||||
|
||||
<div class="row even">
|
||||
<span class="label">Sensor: Temperature</span>
|
||||
<span class="value" id="temp">---</span>°C
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label">Sensor: Humidity</span>
|
||||
<span class="value" id="hum">---</span>%
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<button id="reset">Reset</button>
|
||||
</div>
|
||||
|
||||
<div class="configForm tabPage hidden" id="configPage">
|
||||
<div class="buttons">
|
||||
<button id="resetForm">ReloadConfig</button>
|
||||
<button id="forgetPass">ForgetPass</button>
|
||||
<button id="changeConfig">Save&Restart</button>
|
||||
<button id="exportConfig">Export</button>
|
||||
<button id="importConfig">Import</button>
|
||||
<button id="factoryReset">FactoryReset</button>
|
||||
</div>
|
||||
<div class="configFormRows">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tabPage hidden" id="updatePage">
|
||||
<div class="row">
|
||||
<span class="label">Firmware type</span>
|
||||
<span class="value status-fwtype">---</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label">Chip type</span>
|
||||
<span class="value status-chipid">---</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label">Current version</span>
|
||||
<span class="value status-version">---</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label">New Firmware</span>
|
||||
<input type="file" name="file1" id="uploadFile">
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label"></span>
|
||||
<span id="imageProperties" class="value"></span>
|
||||
</div>
|
||||
<div id="uploadProgress">
|
||||
<div id="uploadDone"></div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button id="uploadBin">Upload</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="overlayContainer hidden" id="overlayContainer">
|
||||
<div id="overlay" class="overlay">
|
||||
<div id="overlayContent" class="overlayContent">
|
||||
AHA
|
||||
</div>
|
||||
<div class="overlayButtons">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overlayContainer hidden" id="adminPassOverlay">
|
||||
<div id="adminPassOverlay" class="overlay">
|
||||
<div id="adminPassOverlayContent" class="overlayContent">
|
||||
<h2>Admin Password</h2>
|
||||
<div id="adminPassHint"></div>
|
||||
<div id="adminPassError" ></div>
|
||||
<input id="adminPassInput" type="password">
|
||||
<div class="row">
|
||||
<span class="label">remember me</span>
|
||||
<select id="adminPassKeep">
|
||||
<option value="true">on</option>
|
||||
<option value="false" selected>off</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="overlayButtons">
|
||||
<button id="adminPassCancel">Cancel</button>
|
||||
<button id="adminPassOk">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
1643
web/index.js
1643
web/index.js
File diff suppressed because it is too large
Load Diff
526
web/sha256.js
Normal file
526
web/sha256.js
Normal file
@@ -0,0 +1,526 @@
|
||||
/**
|
||||
* [js-sha256]{@link https://github.com/emn178/js-sha256}
|
||||
*
|
||||
* @version 0.11.1
|
||||
* @author Chen, Yi-Cyuan [emn178@gmail.com]
|
||||
* @copyright Chen, Yi-Cyuan 2014-2025
|
||||
* @license MIT
|
||||
*/
|
||||
/*jslint bitwise: true */
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var ERROR = 'input is invalid type';
|
||||
var WINDOW = typeof window === 'object';
|
||||
var root = WINDOW ? window : {};
|
||||
if (root.JS_SHA256_NO_WINDOW) {
|
||||
WINDOW = false;
|
||||
}
|
||||
var WEB_WORKER = !WINDOW && typeof self === 'object';
|
||||
var NODE_JS = !root.JS_SHA256_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node && process.type != 'renderer';
|
||||
if (NODE_JS) {
|
||||
root = global;
|
||||
} else if (WEB_WORKER) {
|
||||
root = self;
|
||||
}
|
||||
var COMMON_JS = !root.JS_SHA256_NO_COMMON_JS && typeof module === 'object' && module.exports;
|
||||
var AMD = typeof define === 'function' && define.amd;
|
||||
var ARRAY_BUFFER = !root.JS_SHA256_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined';
|
||||
var HEX_CHARS = '0123456789abcdef'.split('');
|
||||
var EXTRA = [-2147483648, 8388608, 32768, 128];
|
||||
var SHIFT = [24, 16, 8, 0];
|
||||
var K = [
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
||||
];
|
||||
var OUTPUT_TYPES = ['hex', 'array', 'digest', 'arrayBuffer'];
|
||||
|
||||
var blocks = [];
|
||||
|
||||
if (root.JS_SHA256_NO_NODE_JS || !Array.isArray) {
|
||||
Array.isArray = function (obj) {
|
||||
return Object.prototype.toString.call(obj) === '[object Array]';
|
||||
};
|
||||
}
|
||||
|
||||
if (ARRAY_BUFFER && (root.JS_SHA256_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) {
|
||||
ArrayBuffer.isView = function (obj) {
|
||||
return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer;
|
||||
};
|
||||
}
|
||||
|
||||
var createOutputMethod = function (outputType, is224) {
|
||||
return function (message) {
|
||||
return new Sha256(is224, true).update(message)[outputType]();
|
||||
};
|
||||
};
|
||||
|
||||
var createMethod = function (is224) {
|
||||
var method = createOutputMethod('hex', is224);
|
||||
if (NODE_JS) {
|
||||
method = nodeWrap(method, is224);
|
||||
}
|
||||
method.create = function () {
|
||||
return new Sha256(is224);
|
||||
};
|
||||
method.update = function (message) {
|
||||
return method.create().update(message);
|
||||
};
|
||||
for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
|
||||
var type = OUTPUT_TYPES[i];
|
||||
method[type] = createOutputMethod(type, is224);
|
||||
}
|
||||
return method;
|
||||
};
|
||||
|
||||
var nodeWrap = function (method, is224) {
|
||||
var crypto = require('crypto')
|
||||
var Buffer = require('buffer').Buffer;
|
||||
var algorithm = is224 ? 'sha224' : 'sha256';
|
||||
var bufferFrom;
|
||||
if (Buffer.from && !root.JS_SHA256_NO_BUFFER_FROM) {
|
||||
bufferFrom = Buffer.from;
|
||||
} else {
|
||||
bufferFrom = function (message) {
|
||||
return new Buffer(message);
|
||||
};
|
||||
}
|
||||
var nodeMethod = function (message) {
|
||||
if (typeof message === 'string') {
|
||||
return crypto.createHash(algorithm).update(message, 'utf8').digest('hex');
|
||||
} else {
|
||||
if (message === null || message === undefined) {
|
||||
throw new Error(ERROR);
|
||||
} else if (message.constructor === ArrayBuffer) {
|
||||
message = new Uint8Array(message);
|
||||
}
|
||||
}
|
||||
if (Array.isArray(message) || ArrayBuffer.isView(message) ||
|
||||
message.constructor === Buffer) {
|
||||
return crypto.createHash(algorithm).update(bufferFrom(message)).digest('hex');
|
||||
} else {
|
||||
return method(message);
|
||||
}
|
||||
};
|
||||
return nodeMethod;
|
||||
};
|
||||
|
||||
var createHmacOutputMethod = function (outputType, is224) {
|
||||
return function (key, message) {
|
||||
return new HmacSha256(key, is224, true).update(message)[outputType]();
|
||||
};
|
||||
};
|
||||
|
||||
var createHmacMethod = function (is224) {
|
||||
var method = createHmacOutputMethod('hex', is224);
|
||||
method.create = function (key) {
|
||||
return new HmacSha256(key, is224);
|
||||
};
|
||||
method.update = function (key, message) {
|
||||
return method.create(key).update(message);
|
||||
};
|
||||
for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
|
||||
var type = OUTPUT_TYPES[i];
|
||||
method[type] = createHmacOutputMethod(type, is224);
|
||||
}
|
||||
return method;
|
||||
};
|
||||
|
||||
function Sha256(is224, sharedMemory) {
|
||||
if (sharedMemory) {
|
||||
blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] =
|
||||
blocks[4] = blocks[5] = blocks[6] = blocks[7] =
|
||||
blocks[8] = blocks[9] = blocks[10] = blocks[11] =
|
||||
blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
|
||||
this.blocks = blocks;
|
||||
} else {
|
||||
this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
}
|
||||
|
||||
if (is224) {
|
||||
this.h0 = 0xc1059ed8;
|
||||
this.h1 = 0x367cd507;
|
||||
this.h2 = 0x3070dd17;
|
||||
this.h3 = 0xf70e5939;
|
||||
this.h4 = 0xffc00b31;
|
||||
this.h5 = 0x68581511;
|
||||
this.h6 = 0x64f98fa7;
|
||||
this.h7 = 0xbefa4fa4;
|
||||
} else { // 256
|
||||
this.h0 = 0x6a09e667;
|
||||
this.h1 = 0xbb67ae85;
|
||||
this.h2 = 0x3c6ef372;
|
||||
this.h3 = 0xa54ff53a;
|
||||
this.h4 = 0x510e527f;
|
||||
this.h5 = 0x9b05688c;
|
||||
this.h6 = 0x1f83d9ab;
|
||||
this.h7 = 0x5be0cd19;
|
||||
}
|
||||
|
||||
this.block = this.start = this.bytes = this.hBytes = 0;
|
||||
this.finalized = this.hashed = false;
|
||||
this.first = true;
|
||||
this.is224 = is224;
|
||||
}
|
||||
|
||||
Sha256.prototype.update = function (message) {
|
||||
if (this.finalized) {
|
||||
return;
|
||||
}
|
||||
var notString, type = typeof message;
|
||||
if (type !== 'string') {
|
||||
if (type === 'object') {
|
||||
if (message === null) {
|
||||
throw new Error(ERROR);
|
||||
} else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) {
|
||||
message = new Uint8Array(message);
|
||||
} else if (!Array.isArray(message)) {
|
||||
if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) {
|
||||
throw new Error(ERROR);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new Error(ERROR);
|
||||
}
|
||||
notString = true;
|
||||
}
|
||||
var code, index = 0, i, length = message.length, blocks = this.blocks;
|
||||
while (index < length) {
|
||||
if (this.hashed) {
|
||||
this.hashed = false;
|
||||
blocks[0] = this.block;
|
||||
this.block = blocks[16] = blocks[1] = blocks[2] = blocks[3] =
|
||||
blocks[4] = blocks[5] = blocks[6] = blocks[7] =
|
||||
blocks[8] = blocks[9] = blocks[10] = blocks[11] =
|
||||
blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
|
||||
}
|
||||
|
||||
if (notString) {
|
||||
for (i = this.start; index < length && i < 64; ++index) {
|
||||
blocks[i >>> 2] |= message[index] << SHIFT[i++ & 3];
|
||||
}
|
||||
} else {
|
||||
for (i = this.start; index < length && i < 64; ++index) {
|
||||
code = message.charCodeAt(index);
|
||||
if (code < 0x80) {
|
||||
blocks[i >>> 2] |= code << SHIFT[i++ & 3];
|
||||
} else if (code < 0x800) {
|
||||
blocks[i >>> 2] |= (0xc0 | (code >>> 6)) << SHIFT[i++ & 3];
|
||||
blocks[i >>> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
|
||||
} else if (code < 0xd800 || code >= 0xe000) {
|
||||
blocks[i >>> 2] |= (0xe0 | (code >>> 12)) << SHIFT[i++ & 3];
|
||||
blocks[i >>> 2] |= (0x80 | ((code >>> 6) & 0x3f)) << SHIFT[i++ & 3];
|
||||
blocks[i >>> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
|
||||
} else {
|
||||
code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
|
||||
blocks[i >>> 2] |= (0xf0 | (code >>> 18)) << SHIFT[i++ & 3];
|
||||
blocks[i >>> 2] |= (0x80 | ((code >>> 12) & 0x3f)) << SHIFT[i++ & 3];
|
||||
blocks[i >>> 2] |= (0x80 | ((code >>> 6) & 0x3f)) << SHIFT[i++ & 3];
|
||||
blocks[i >>> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.lastByteIndex = i;
|
||||
this.bytes += i - this.start;
|
||||
if (i >= 64) {
|
||||
this.block = blocks[16];
|
||||
this.start = i - 64;
|
||||
this.hash();
|
||||
this.hashed = true;
|
||||
} else {
|
||||
this.start = i;
|
||||
}
|
||||
}
|
||||
if (this.bytes > 4294967295) {
|
||||
this.hBytes += this.bytes / 4294967296 << 0;
|
||||
this.bytes = this.bytes % 4294967296;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
Sha256.prototype.finalize = function () {
|
||||
if (this.finalized) {
|
||||
return;
|
||||
}
|
||||
this.finalized = true;
|
||||
var blocks = this.blocks, i = this.lastByteIndex;
|
||||
blocks[16] = this.block;
|
||||
blocks[i >>> 2] |= EXTRA[i & 3];
|
||||
this.block = blocks[16];
|
||||
if (i >= 56) {
|
||||
if (!this.hashed) {
|
||||
this.hash();
|
||||
}
|
||||
blocks[0] = this.block;
|
||||
blocks[16] = blocks[1] = blocks[2] = blocks[3] =
|
||||
blocks[4] = blocks[5] = blocks[6] = blocks[7] =
|
||||
blocks[8] = blocks[9] = blocks[10] = blocks[11] =
|
||||
blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
|
||||
}
|
||||
blocks[14] = this.hBytes << 3 | this.bytes >>> 29;
|
||||
blocks[15] = this.bytes << 3;
|
||||
this.hash();
|
||||
};
|
||||
|
||||
Sha256.prototype.hash = function () {
|
||||
var a = this.h0, b = this.h1, c = this.h2, d = this.h3, e = this.h4, f = this.h5, g = this.h6,
|
||||
h = this.h7, blocks = this.blocks, j, s0, s1, maj, t1, t2, ch, ab, da, cd, bc;
|
||||
|
||||
for (j = 16; j < 64; ++j) {
|
||||
// rightrotate
|
||||
t1 = blocks[j - 15];
|
||||
s0 = ((t1 >>> 7) | (t1 << 25)) ^ ((t1 >>> 18) | (t1 << 14)) ^ (t1 >>> 3);
|
||||
t1 = blocks[j - 2];
|
||||
s1 = ((t1 >>> 17) | (t1 << 15)) ^ ((t1 >>> 19) | (t1 << 13)) ^ (t1 >>> 10);
|
||||
blocks[j] = blocks[j - 16] + s0 + blocks[j - 7] + s1 << 0;
|
||||
}
|
||||
|
||||
bc = b & c;
|
||||
for (j = 0; j < 64; j += 4) {
|
||||
if (this.first) {
|
||||
if (this.is224) {
|
||||
ab = 300032;
|
||||
t1 = blocks[0] - 1413257819;
|
||||
h = t1 - 150054599 << 0;
|
||||
d = t1 + 24177077 << 0;
|
||||
} else {
|
||||
ab = 704751109;
|
||||
t1 = blocks[0] - 210244248;
|
||||
h = t1 - 1521486534 << 0;
|
||||
d = t1 + 143694565 << 0;
|
||||
}
|
||||
this.first = false;
|
||||
} else {
|
||||
s0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10));
|
||||
s1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7));
|
||||
ab = a & b;
|
||||
maj = ab ^ (a & c) ^ bc;
|
||||
ch = (e & f) ^ (~e & g);
|
||||
t1 = h + s1 + ch + K[j] + blocks[j];
|
||||
t2 = s0 + maj;
|
||||
h = d + t1 << 0;
|
||||
d = t1 + t2 << 0;
|
||||
}
|
||||
s0 = ((d >>> 2) | (d << 30)) ^ ((d >>> 13) | (d << 19)) ^ ((d >>> 22) | (d << 10));
|
||||
s1 = ((h >>> 6) | (h << 26)) ^ ((h >>> 11) | (h << 21)) ^ ((h >>> 25) | (h << 7));
|
||||
da = d & a;
|
||||
maj = da ^ (d & b) ^ ab;
|
||||
ch = (h & e) ^ (~h & f);
|
||||
t1 = g + s1 + ch + K[j + 1] + blocks[j + 1];
|
||||
t2 = s0 + maj;
|
||||
g = c + t1 << 0;
|
||||
c = t1 + t2 << 0;
|
||||
s0 = ((c >>> 2) | (c << 30)) ^ ((c >>> 13) | (c << 19)) ^ ((c >>> 22) | (c << 10));
|
||||
s1 = ((g >>> 6) | (g << 26)) ^ ((g >>> 11) | (g << 21)) ^ ((g >>> 25) | (g << 7));
|
||||
cd = c & d;
|
||||
maj = cd ^ (c & a) ^ da;
|
||||
ch = (g & h) ^ (~g & e);
|
||||
t1 = f + s1 + ch + K[j + 2] + blocks[j + 2];
|
||||
t2 = s0 + maj;
|
||||
f = b + t1 << 0;
|
||||
b = t1 + t2 << 0;
|
||||
s0 = ((b >>> 2) | (b << 30)) ^ ((b >>> 13) | (b << 19)) ^ ((b >>> 22) | (b << 10));
|
||||
s1 = ((f >>> 6) | (f << 26)) ^ ((f >>> 11) | (f << 21)) ^ ((f >>> 25) | (f << 7));
|
||||
bc = b & c;
|
||||
maj = bc ^ (b & d) ^ cd;
|
||||
ch = (f & g) ^ (~f & h);
|
||||
t1 = e + s1 + ch + K[j + 3] + blocks[j + 3];
|
||||
t2 = s0 + maj;
|
||||
e = a + t1 << 0;
|
||||
a = t1 + t2 << 0;
|
||||
this.chromeBugWorkAround = true;
|
||||
}
|
||||
|
||||
this.h0 = this.h0 + a << 0;
|
||||
this.h1 = this.h1 + b << 0;
|
||||
this.h2 = this.h2 + c << 0;
|
||||
this.h3 = this.h3 + d << 0;
|
||||
this.h4 = this.h4 + e << 0;
|
||||
this.h5 = this.h5 + f << 0;
|
||||
this.h6 = this.h6 + g << 0;
|
||||
this.h7 = this.h7 + h << 0;
|
||||
};
|
||||
|
||||
Sha256.prototype.hex = function () {
|
||||
this.finalize();
|
||||
|
||||
var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4, h5 = this.h5,
|
||||
h6 = this.h6, h7 = this.h7;
|
||||
|
||||
var hex = HEX_CHARS[(h0 >>> 28) & 0x0F] + HEX_CHARS[(h0 >>> 24) & 0x0F] +
|
||||
HEX_CHARS[(h0 >>> 20) & 0x0F] + HEX_CHARS[(h0 >>> 16) & 0x0F] +
|
||||
HEX_CHARS[(h0 >>> 12) & 0x0F] + HEX_CHARS[(h0 >>> 8) & 0x0F] +
|
||||
HEX_CHARS[(h0 >>> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] +
|
||||
HEX_CHARS[(h1 >>> 28) & 0x0F] + HEX_CHARS[(h1 >>> 24) & 0x0F] +
|
||||
HEX_CHARS[(h1 >>> 20) & 0x0F] + HEX_CHARS[(h1 >>> 16) & 0x0F] +
|
||||
HEX_CHARS[(h1 >>> 12) & 0x0F] + HEX_CHARS[(h1 >>> 8) & 0x0F] +
|
||||
HEX_CHARS[(h1 >>> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] +
|
||||
HEX_CHARS[(h2 >>> 28) & 0x0F] + HEX_CHARS[(h2 >>> 24) & 0x0F] +
|
||||
HEX_CHARS[(h2 >>> 20) & 0x0F] + HEX_CHARS[(h2 >>> 16) & 0x0F] +
|
||||
HEX_CHARS[(h2 >>> 12) & 0x0F] + HEX_CHARS[(h2 >>> 8) & 0x0F] +
|
||||
HEX_CHARS[(h2 >>> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] +
|
||||
HEX_CHARS[(h3 >>> 28) & 0x0F] + HEX_CHARS[(h3 >>> 24) & 0x0F] +
|
||||
HEX_CHARS[(h3 >>> 20) & 0x0F] + HEX_CHARS[(h3 >>> 16) & 0x0F] +
|
||||
HEX_CHARS[(h3 >>> 12) & 0x0F] + HEX_CHARS[(h3 >>> 8) & 0x0F] +
|
||||
HEX_CHARS[(h3 >>> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] +
|
||||
HEX_CHARS[(h4 >>> 28) & 0x0F] + HEX_CHARS[(h4 >>> 24) & 0x0F] +
|
||||
HEX_CHARS[(h4 >>> 20) & 0x0F] + HEX_CHARS[(h4 >>> 16) & 0x0F] +
|
||||
HEX_CHARS[(h4 >>> 12) & 0x0F] + HEX_CHARS[(h4 >>> 8) & 0x0F] +
|
||||
HEX_CHARS[(h4 >>> 4) & 0x0F] + HEX_CHARS[h4 & 0x0F] +
|
||||
HEX_CHARS[(h5 >>> 28) & 0x0F] + HEX_CHARS[(h5 >>> 24) & 0x0F] +
|
||||
HEX_CHARS[(h5 >>> 20) & 0x0F] + HEX_CHARS[(h5 >>> 16) & 0x0F] +
|
||||
HEX_CHARS[(h5 >>> 12) & 0x0F] + HEX_CHARS[(h5 >>> 8) & 0x0F] +
|
||||
HEX_CHARS[(h5 >>> 4) & 0x0F] + HEX_CHARS[h5 & 0x0F] +
|
||||
HEX_CHARS[(h6 >>> 28) & 0x0F] + HEX_CHARS[(h6 >>> 24) & 0x0F] +
|
||||
HEX_CHARS[(h6 >>> 20) & 0x0F] + HEX_CHARS[(h6 >>> 16) & 0x0F] +
|
||||
HEX_CHARS[(h6 >>> 12) & 0x0F] + HEX_CHARS[(h6 >>> 8) & 0x0F] +
|
||||
HEX_CHARS[(h6 >>> 4) & 0x0F] + HEX_CHARS[h6 & 0x0F];
|
||||
if (!this.is224) {
|
||||
hex += HEX_CHARS[(h7 >>> 28) & 0x0F] + HEX_CHARS[(h7 >>> 24) & 0x0F] +
|
||||
HEX_CHARS[(h7 >>> 20) & 0x0F] + HEX_CHARS[(h7 >>> 16) & 0x0F] +
|
||||
HEX_CHARS[(h7 >>> 12) & 0x0F] + HEX_CHARS[(h7 >>> 8) & 0x0F] +
|
||||
HEX_CHARS[(h7 >>> 4) & 0x0F] + HEX_CHARS[h7 & 0x0F];
|
||||
}
|
||||
return hex;
|
||||
};
|
||||
|
||||
Sha256.prototype.toString = Sha256.prototype.hex;
|
||||
|
||||
Sha256.prototype.digest = function () {
|
||||
this.finalize();
|
||||
|
||||
var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4, h5 = this.h5,
|
||||
h6 = this.h6, h7 = this.h7;
|
||||
|
||||
var arr = [
|
||||
(h0 >>> 24) & 0xFF, (h0 >>> 16) & 0xFF, (h0 >>> 8) & 0xFF, h0 & 0xFF,
|
||||
(h1 >>> 24) & 0xFF, (h1 >>> 16) & 0xFF, (h1 >>> 8) & 0xFF, h1 & 0xFF,
|
||||
(h2 >>> 24) & 0xFF, (h2 >>> 16) & 0xFF, (h2 >>> 8) & 0xFF, h2 & 0xFF,
|
||||
(h3 >>> 24) & 0xFF, (h3 >>> 16) & 0xFF, (h3 >>> 8) & 0xFF, h3 & 0xFF,
|
||||
(h4 >>> 24) & 0xFF, (h4 >>> 16) & 0xFF, (h4 >>> 8) & 0xFF, h4 & 0xFF,
|
||||
(h5 >>> 24) & 0xFF, (h5 >>> 16) & 0xFF, (h5 >>> 8) & 0xFF, h5 & 0xFF,
|
||||
(h6 >>> 24) & 0xFF, (h6 >>> 16) & 0xFF, (h6 >>> 8) & 0xFF, h6 & 0xFF
|
||||
];
|
||||
if (!this.is224) {
|
||||
arr.push((h7 >>> 24) & 0xFF, (h7 >>> 16) & 0xFF, (h7 >>> 8) & 0xFF, h7 & 0xFF);
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
Sha256.prototype.array = Sha256.prototype.digest;
|
||||
|
||||
Sha256.prototype.arrayBuffer = function () {
|
||||
this.finalize();
|
||||
|
||||
var buffer = new ArrayBuffer(this.is224 ? 28 : 32);
|
||||
var dataView = new DataView(buffer);
|
||||
dataView.setUint32(0, this.h0);
|
||||
dataView.setUint32(4, this.h1);
|
||||
dataView.setUint32(8, this.h2);
|
||||
dataView.setUint32(12, this.h3);
|
||||
dataView.setUint32(16, this.h4);
|
||||
dataView.setUint32(20, this.h5);
|
||||
dataView.setUint32(24, this.h6);
|
||||
if (!this.is224) {
|
||||
dataView.setUint32(28, this.h7);
|
||||
}
|
||||
return buffer;
|
||||
};
|
||||
|
||||
function HmacSha256(key, is224, sharedMemory) {
|
||||
var i, type = typeof key;
|
||||
if (type === 'string') {
|
||||
var bytes = [], length = key.length, index = 0, code;
|
||||
for (i = 0; i < length; ++i) {
|
||||
code = key.charCodeAt(i);
|
||||
if (code < 0x80) {
|
||||
bytes[index++] = code;
|
||||
} else if (code < 0x800) {
|
||||
bytes[index++] = (0xc0 | (code >>> 6));
|
||||
bytes[index++] = (0x80 | (code & 0x3f));
|
||||
} else if (code < 0xd800 || code >= 0xe000) {
|
||||
bytes[index++] = (0xe0 | (code >>> 12));
|
||||
bytes[index++] = (0x80 | ((code >>> 6) & 0x3f));
|
||||
bytes[index++] = (0x80 | (code & 0x3f));
|
||||
} else {
|
||||
code = 0x10000 + (((code & 0x3ff) << 10) | (key.charCodeAt(++i) & 0x3ff));
|
||||
bytes[index++] = (0xf0 | (code >>> 18));
|
||||
bytes[index++] = (0x80 | ((code >>> 12) & 0x3f));
|
||||
bytes[index++] = (0x80 | ((code >>> 6) & 0x3f));
|
||||
bytes[index++] = (0x80 | (code & 0x3f));
|
||||
}
|
||||
}
|
||||
key = bytes;
|
||||
} else {
|
||||
if (type === 'object') {
|
||||
if (key === null) {
|
||||
throw new Error(ERROR);
|
||||
} else if (ARRAY_BUFFER && key.constructor === ArrayBuffer) {
|
||||
key = new Uint8Array(key);
|
||||
} else if (!Array.isArray(key)) {
|
||||
if (!ARRAY_BUFFER || !ArrayBuffer.isView(key)) {
|
||||
throw new Error(ERROR);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new Error(ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
if (key.length > 64) {
|
||||
key = (new Sha256(is224, true)).update(key).array();
|
||||
}
|
||||
|
||||
var oKeyPad = [], iKeyPad = [];
|
||||
for (i = 0; i < 64; ++i) {
|
||||
var b = key[i] || 0;
|
||||
oKeyPad[i] = 0x5c ^ b;
|
||||
iKeyPad[i] = 0x36 ^ b;
|
||||
}
|
||||
|
||||
Sha256.call(this, is224, sharedMemory);
|
||||
|
||||
this.update(iKeyPad);
|
||||
this.oKeyPad = oKeyPad;
|
||||
this.inner = true;
|
||||
this.sharedMemory = sharedMemory;
|
||||
}
|
||||
HmacSha256.prototype = new Sha256();
|
||||
|
||||
HmacSha256.prototype.finalize = function () {
|
||||
Sha256.prototype.finalize.call(this);
|
||||
if (this.inner) {
|
||||
this.inner = false;
|
||||
var innerHash = this.array();
|
||||
Sha256.call(this, this.is224, this.sharedMemory);
|
||||
this.update(this.oKeyPad);
|
||||
this.update(innerHash);
|
||||
Sha256.prototype.finalize.call(this);
|
||||
}
|
||||
};
|
||||
|
||||
var exports = createMethod();
|
||||
exports.sha256 = exports;
|
||||
exports.sha224 = createMethod(true);
|
||||
exports.sha256.hmac = createHmacMethod();
|
||||
exports.sha224.hmac = createHmacMethod(true);
|
||||
|
||||
if (COMMON_JS) {
|
||||
module.exports = exports;
|
||||
} else {
|
||||
root.sha256 = exports.sha256;
|
||||
root.sha224 = exports.sha224;
|
||||
if (AMD) {
|
||||
define(function () {
|
||||
return exports;
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user