Repository files navigation

Knot

Knot is a compact ESP32 password hashing library with a bcrypt-like self-contained hash string format, backed by PBKDF2-HMAC-SHA256.

Knot helps Arduino ESP32 projects create and verify self-contained password hashes with secure salt generation, cost-based PBKDF2-HMAC-SHA256 hashing, constant-time comparison, cooperative verification, and bounded result buffers.

CIReleaseLicense: MIT

Why use Knot?

  • Password-focused - generate salts, hash passwords, compare stored hashes, and detect old costs.
  • Self-contained hashes - the encoded hash stores the algorithm marker, version, cost, salt, and derived key.
  • Cooperative verification - split PBKDF2 comparison into bounded steps for FreeRTOS and Worker integration.
  • ESP32-friendly - fixed public buffers, no exceptions, result-based errors, and optional mutex protection.
  • Familiar API shape - genSalt(), hash(), compare(), and getRounds() helpers without bcrypt compatibility claims.
  • Clear compatibility - Knot hashes are not bcrypt hashes and never use bcrypt $2a$, $2b$, or $2y$ prefixes.

Install

PlatformIO

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/knot.git
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Knot is not published to Arduino Library Manager yet.

Install it by downloading the repository ZIP or cloning it into your Arduino libraries folder.

Arduino/libraries/Knot

Quick start

#include<Arduino.h>
#include<Knot.h>
Knot knot;
voidsetup() {
Serial.begin(115200);
KnotResult init = knot.init();
if (!init) {
Serial.println(init.message);
return;
}
KnotHashResult hash = knot.hash("my-password");
if (!hash) {
Serial.println(hash.message);
return;
}
KnotCompareResult check = knot.compare("my-password", hash.value);
if (!check) {
Serial.println(check.message);
return;
}
Serial.println(check.match ? "valid" : "invalid");
}
voidloop() {
delay(1000);
}

Cooperative compare

Use a stateful operation when verification must return control to FreeRTOS between bounded PBKDF2 slices.

KnotCompareOperation operation;
KnotResult begin = knot.beginCompare(operation, password, encodedHash);
KnotStepResult step = operation.step(iterationBudget);
while (step.inProgress()) {
vTaskDelay(1);
step = operation.step(iterationBudget);
}
if (step.completed() && step.match) {
login();
}

Benchmark iterationBudget on the target and aim for the application's responsiveness window, normally around 5-20 ms per step(). Call operation.cancel() between steps when a request or Worker job is cancelled.

Important notes

Important

Knot uses PBKDF2-HMAC-SHA256, not bcrypt. Store and verify Knot hashes only with Knot.

  • v0.1 hashes use $knot$v1$c<cost>$<salt>$<hash>.
  • v1 means PBKDF2-HMAC-SHA256 with a 16-byte salt and 32-byte derived key.
  • Password input is limited to KNOT_MAX_PASSWORD_LENGTH bytes by default.
  • Cost 14 is the secure default, not the demo setting. Tune cost on real target hardware.
  • hash() and compare() are synchronous convenience APIs. Use beginCompare() and step() for cooperative verification.

Examples

ExampleDescription
BasicHashMinimal init, hash, and compare.
CooperativeCompareVerify a hash through bounded PBKDF2 steps.
ExplicitCostChoose a cost and read rounds/cost.
ManualSaltGenerate a salt and pass it into hash().
CallerBufferUse caller-owned buffers.
NeedsRehashUpgrade stored hashes after login.
HashInfoRead algorithm, version, and cost metadata.
ConfigurationConfigure password length and cost limits.
ClassUsageUse Knot from an application class.
BenchmarkPrint target, cost, iterations, timing, and heap measurements.

Start with:

examples/BasicHash

Documentation

Detailed documentation is available in the docs/ folder.

DocumentDescription
docs/getting-started.mdSetup and first password flow.
docs/configuration.mdConfig options and defaults.
docs/api.mdPublic classes, methods, and result types.
docs/examples.mdExplanation of all included examples.
docs/security.mdPassword hashing and storage notes.
docs/memory.mdBuffers, mutexes, and synchronous behavior.
docs/troubleshooting.mdCommon issues and fixes.
docs/bcrypt-positioning.mdBcrypt-like format and compatibility limits.

API overview

Knot knot;
knot.init();
KnotSaltResult salt = knot.genSalt(14);
KnotHashResult hash = knot.hash("password", salt.value);
KnotCompareResult check = knot.compare("password", hash.value);
KnotRoundsResult cost = knot.getRounds(hash.value);
bool upgrade = knot.needsRehash(hash.value, 14);

For the full API, see docs/api.md.

Compatibility

ItemSupport
FrameworkArduino ESP32
Platformespressif32
LanguageC++20
Filesystemnone
PSRAMnot used in v0.1
DependenciesmbedTLS from ESP32 platform
ExceptionsNot used
StatusEarly-stage 0.1.0

Configuration

KnotConfig config;
config.defaultCost = 14;
config.minCost = 4;
config.maxCost = 16;
config.maxPasswordLength = 72;
KnotResult result = knot.init(config);

For all options, see docs/configuration.md.

Existing $knot$v1$c10$... hashes remain verifiable after the default cost change. They are treated as rehash candidates when checked against the new default cost 14.

Error handling

Knot reports operation status through result objects.

KnotHashResult hash = knot.hash("password");
if (!hash) {
Serial.println(hash.message);
return;
}

For result fields and status codes, see docs/api.md.

License

MIT - see LICENSE.md.

ZekStack

Part of the ZekStack ESP32 library stack.

About

Knot is a bcrypt-style password hashing library

Topics

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Knot

Knot is a compact ESP32 password hashing library with a bcrypt-like self-contained hash string format, backed by PBKDF2-HMAC-SHA256.

Knot helps Arduino ESP32 projects create and verify self-contained password hashes with secure salt generation, cost-based PBKDF2-HMAC-SHA256 hashing, constant-time comparison, cooperative verification, and bounded result buffers.

CIReleaseLicense: MIT

Why use Knot?

  • Password-focused - generate salts, hash passwords, compare stored hashes, and detect old costs.
  • Self-contained hashes - the encoded hash stores the algorithm marker, version, cost, salt, and derived key.
  • Cooperative verification - split PBKDF2 comparison into bounded steps for FreeRTOS and Worker integration.
  • ESP32-friendly - fixed public buffers, no exceptions, result-based errors, and optional mutex protection.
  • Familiar API shape - genSalt(), hash(), compare(), and getRounds() helpers without bcrypt compatibility claims.
  • Clear compatibility - Knot hashes are not bcrypt hashes and never use bcrypt $2a$, $2b$, or $2y$ prefixes.

Install

PlatformIO

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/knot.git
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Knot is not published to Arduino Library Manager yet.

Install it by downloading the repository ZIP or cloning it into your Arduino libraries folder.

Arduino/libraries/Knot

Quick start

#include<Arduino.h>
#include<Knot.h>
Knot knot;
voidsetup() {
Serial.begin(115200);
KnotResult init = knot.init();
if (!init) {
Serial.println(init.message);
return;
}
KnotHashResult hash = knot.hash("my-password");
if (!hash) {
Serial.println(hash.message);
return;
}
KnotCompareResult check = knot.compare("my-password", hash.value);
if (!check) {
Serial.println(check.message);
return;
}
Serial.println(check.match ? "valid" : "invalid");
}
voidloop() {
delay(1000);
}

Cooperative compare

Use a stateful operation when verification must return control to FreeRTOS between bounded PBKDF2 slices.

KnotCompareOperation operation;
KnotResult begin = knot.beginCompare(operation, password, encodedHash);
KnotStepResult step = operation.step(iterationBudget);
while (step.inProgress()) {
vTaskDelay(1);
step = operation.step(iterationBudget);
}
if (step.completed() && step.match) {
login();
}

Benchmark iterationBudget on the target and aim for the application's responsiveness window, normally around 5-20 ms per step(). Call operation.cancel() between steps when a request or Worker job is cancelled.

Important notes

Important

Knot uses PBKDF2-HMAC-SHA256, not bcrypt. Store and verify Knot hashes only with Knot.

  • v0.1 hashes use $knot$v1$c<cost>$<salt>$<hash>.
  • v1 means PBKDF2-HMAC-SHA256 with a 16-byte salt and 32-byte derived key.
  • Password input is limited to KNOT_MAX_PASSWORD_LENGTH bytes by default.
  • Cost 14 is the secure default, not the demo setting. Tune cost on real target hardware.
  • hash() and compare() are synchronous convenience APIs. Use beginCompare() and step() for cooperative verification.

Examples

ExampleDescription
BasicHashMinimal init, hash, and compare.
CooperativeCompareVerify a hash through bounded PBKDF2 steps.
ExplicitCostChoose a cost and read rounds/cost.
ManualSaltGenerate a salt and pass it into hash().
CallerBufferUse caller-owned buffers.
NeedsRehashUpgrade stored hashes after login.
HashInfoRead algorithm, version, and cost metadata.
ConfigurationConfigure password length and cost limits.
ClassUsageUse Knot from an application class.
BenchmarkPrint target, cost, iterations, timing, and heap measurements.

Start with:

examples/BasicHash

Documentation

Detailed documentation is available in the docs/ folder.

DocumentDescription
docs/getting-started.mdSetup and first password flow.
docs/configuration.mdConfig options and defaults.
docs/api.mdPublic classes, methods, and result types.
docs/examples.mdExplanation of all included examples.
docs/security.mdPassword hashing and storage notes.
docs/memory.mdBuffers, mutexes, and synchronous behavior.
docs/troubleshooting.mdCommon issues and fixes.
docs/bcrypt-positioning.mdBcrypt-like format and compatibility limits.

API overview

Knot knot;
knot.init();
KnotSaltResult salt = knot.genSalt(14);
KnotHashResult hash = knot.hash("password", salt.value);
KnotCompareResult check = knot.compare("password", hash.value);
KnotRoundsResult cost = knot.getRounds(hash.value);
bool upgrade = knot.needsRehash(hash.value, 14);

For the full API, see docs/api.md.

Compatibility

ItemSupport
FrameworkArduino ESP32
Platformespressif32
LanguageC++20
Filesystemnone
PSRAMnot used in v0.1
DependenciesmbedTLS from ESP32 platform
ExceptionsNot used
StatusEarly-stage 0.1.0

Configuration

KnotConfig config;
config.defaultCost = 14;
config.minCost = 4;
config.maxCost = 16;
config.maxPasswordLength = 72;
KnotResult result = knot.init(config);

For all options, see docs/configuration.md.

Existing $knot$v1$c10$... hashes remain verifiable after the default cost change. They are treated as rehash candidates when checked against the new default cost 14.

Error handling

Knot reports operation status through result objects.

KnotHashResult hash = knot.hash("password");
if (!hash) {
Serial.println(hash.message);
return;
}

For result fields and status codes, see docs/api.md.

License

MIT - see LICENSE.md.

ZekStack

Part of the ZekStack ESP32 library stack.

About

Knot is a bcrypt-style password hashing library

Topics

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Knot

Knot is a compact ESP32 password hashing library with a bcrypt-like self-contained hash string format, backed by PBKDF2-HMAC-SHA256.

Knot helps Arduino ESP32 projects create and verify self-contained password hashes with secure salt generation, cost-based PBKDF2-HMAC-SHA256 hashing, constant-time comparison, cooperative verification, and bounded result buffers.

CIReleaseLicense: MIT

Why use Knot?

  • Password-focused - generate salts, hash passwords, compare stored hashes, and detect old costs.
  • Self-contained hashes - the encoded hash stores the algorithm marker, version, cost, salt, and derived key.
  • Cooperative verification - split PBKDF2 comparison into bounded steps for FreeRTOS and Worker integration.
  • ESP32-friendly - fixed public buffers, no exceptions, result-based errors, and optional mutex protection.
  • Familiar API shape - genSalt(), hash(), compare(), and getRounds() helpers without bcrypt compatibility claims.
  • Clear compatibility - Knot hashes are not bcrypt hashes and never use bcrypt $2a$, $2b$, or $2y$ prefixes.

Install

PlatformIO

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/knot.git
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Knot is not published to Arduino Library Manager yet.

Install it by downloading the repository ZIP or cloning it into your Arduino libraries folder.

Arduino/libraries/Knot

Quick start

#include<Arduino.h>
#include<Knot.h>
Knot knot;
voidsetup() {
Serial.begin(115200);
KnotResult init = knot.init();
if (!init) {
Serial.println(init.message);
return;
}
KnotHashResult hash = knot.hash("my-password");
if (!hash) {
Serial.println(hash.message);
return;
}
KnotCompareResult check = knot.compare("my-password", hash.value);
if (!check) {
Serial.println(check.message);
return;
}
Serial.println(check.match ? "valid" : "invalid");
}
voidloop() {
delay(1000);
}

Cooperative compare

Use a stateful operation when verification must return control to FreeRTOS between bounded PBKDF2 slices.

KnotCompareOperation operation;
KnotResult begin = knot.beginCompare(operation, password, encodedHash);
KnotStepResult step = operation.step(iterationBudget);
while (step.inProgress()) {
vTaskDelay(1);
step = operation.step(iterationBudget);
}
if (step.completed() && step.match) {
login();
}

Benchmark iterationBudget on the target and aim for the application's responsiveness window, normally around 5-20 ms per step(). Call operation.cancel() between steps when a request or Worker job is cancelled.

Important notes

Important

Knot uses PBKDF2-HMAC-SHA256, not bcrypt. Store and verify Knot hashes only with Knot.

  • v0.1 hashes use $knot$v1$c<cost>$<salt>$<hash>.
  • v1 means PBKDF2-HMAC-SHA256 with a 16-byte salt and 32-byte derived key.
  • Password input is limited to KNOT_MAX_PASSWORD_LENGTH bytes by default.
  • Cost 14 is the secure default, not the demo setting. Tune cost on real target hardware.
  • hash() and compare() are synchronous convenience APIs. Use beginCompare() and step() for cooperative verification.

Examples

ExampleDescription
BasicHashMinimal init, hash, and compare.
CooperativeCompareVerify a hash through bounded PBKDF2 steps.
ExplicitCostChoose a cost and read rounds/cost.
ManualSaltGenerate a salt and pass it into hash().
CallerBufferUse caller-owned buffers.
NeedsRehashUpgrade stored hashes after login.
HashInfoRead algorithm, version, and cost metadata.
ConfigurationConfigure password length and cost limits.
ClassUsageUse Knot from an application class.
BenchmarkPrint target, cost, iterations, timing, and heap measurements.

Start with:

examples/BasicHash

Documentation

Detailed documentation is available in the docs/ folder.

DocumentDescription
docs/getting-started.mdSetup and first password flow.
docs/configuration.mdConfig options and defaults.
docs/api.mdPublic classes, methods, and result types.
docs/examples.mdExplanation of all included examples.
docs/security.mdPassword hashing and storage notes.
docs/memory.mdBuffers, mutexes, and synchronous behavior.
docs/troubleshooting.mdCommon issues and fixes.
docs/bcrypt-positioning.mdBcrypt-like format and compatibility limits.

API overview

Knot knot;
knot.init();
KnotSaltResult salt = knot.genSalt(14);
KnotHashResult hash = knot.hash("password", salt.value);
KnotCompareResult check = knot.compare("password", hash.value);
KnotRoundsResult cost = knot.getRounds(hash.value);
bool upgrade = knot.needsRehash(hash.value, 14);

For the full API, see docs/api.md.

Compatibility

ItemSupport
FrameworkArduino ESP32
Platformespressif32
LanguageC++20
Filesystemnone
PSRAMnot used in v0.1
DependenciesmbedTLS from ESP32 platform
ExceptionsNot used
StatusEarly-stage 0.1.0

Configuration

KnotConfig config;
config.defaultCost = 14;
config.minCost = 4;
config.maxCost = 16;
config.maxPasswordLength = 72;
KnotResult result = knot.init(config);

For all options, see docs/configuration.md.

Existing $knot$v1$c10$... hashes remain verifiable after the default cost change. They are treated as rehash candidates when checked against the new default cost 14.

Error handling

Knot reports operation status through result objects.

KnotHashResult hash = knot.hash("password");
if (!hash) {
Serial.println(hash.message);
return;
}

For result fields and status codes, see docs/api.md.

License

MIT - see LICENSE.md.

ZekStack

Part of the ZekStack ESP32 library stack.

About

Knot is a bcrypt-style password hashing library

Topics

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Knot

Knot is a compact ESP32 password hashing library with a bcrypt-like self-contained hash string format, backed by PBKDF2-HMAC-SHA256.

Knot helps Arduino ESP32 projects create and verify self-contained password hashes with secure salt generation, cost-based PBKDF2-HMAC-SHA256 hashing, constant-time comparison, cooperative verification, and bounded result buffers.

CIReleaseLicense: MIT

Why use Knot?

  • Password-focused - generate salts, hash passwords, compare stored hashes, and detect old costs.
  • Self-contained hashes - the encoded hash stores the algorithm marker, version, cost, salt, and derived key.
  • Cooperative verification - split PBKDF2 comparison into bounded steps for FreeRTOS and Worker integration.
  • ESP32-friendly - fixed public buffers, no exceptions, result-based errors, and optional mutex protection.
  • Familiar API shape - genSalt(), hash(), compare(), and getRounds() helpers without bcrypt compatibility claims.
  • Clear compatibility - Knot hashes are not bcrypt hashes and never use bcrypt $2a$, $2b$, or $2y$ prefixes.

Install

PlatformIO

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/knot.git
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Knot is not published to Arduino Library Manager yet.

Install it by downloading the repository ZIP or cloning it into your Arduino libraries folder.

Arduino/libraries/Knot

Quick start

#include<Arduino.h>
#include<Knot.h>
Knot knot;
voidsetup() {
Serial.begin(115200);
KnotResult init = knot.init();
if (!init) {
Serial.println(init.message);
return;
}
KnotHashResult hash = knot.hash("my-password");
if (!hash) {
Serial.println(hash.message);
return;
}
KnotCompareResult check = knot.compare("my-password", hash.value);
if (!check) {
Serial.println(check.message);
return;
}
Serial.println(check.match ? "valid" : "invalid");
}
voidloop() {
delay(1000);
}

Cooperative compare

Use a stateful operation when verification must return control to FreeRTOS between bounded PBKDF2 slices.

KnotCompareOperation operation;
KnotResult begin = knot.beginCompare(operation, password, encodedHash);
KnotStepResult step = operation.step(iterationBudget);
while (step.inProgress()) {
vTaskDelay(1);
step = operation.step(iterationBudget);
}
if (step.completed() && step.match) {
login();
}

Benchmark iterationBudget on the target and aim for the application's responsiveness window, normally around 5-20 ms per step(). Call operation.cancel() between steps when a request or Worker job is cancelled.

Important notes

Important

Knot uses PBKDF2-HMAC-SHA256, not bcrypt. Store and verify Knot hashes only with Knot.

  • v0.1 hashes use $knot$v1$c<cost>$<salt>$<hash>.
  • v1 means PBKDF2-HMAC-SHA256 with a 16-byte salt and 32-byte derived key.
  • Password input is limited to KNOT_MAX_PASSWORD_LENGTH bytes by default.
  • Cost 14 is the secure default, not the demo setting. Tune cost on real target hardware.
  • hash() and compare() are synchronous convenience APIs. Use beginCompare() and step() for cooperative verification.

Examples

ExampleDescription
BasicHashMinimal init, hash, and compare.
CooperativeCompareVerify a hash through bounded PBKDF2 steps.
ExplicitCostChoose a cost and read rounds/cost.
ManualSaltGenerate a salt and pass it into hash().
CallerBufferUse caller-owned buffers.
NeedsRehashUpgrade stored hashes after login.
HashInfoRead algorithm, version, and cost metadata.
ConfigurationConfigure password length and cost limits.
ClassUsageUse Knot from an application class.
BenchmarkPrint target, cost, iterations, timing, and heap measurements.

Start with:

examples/BasicHash

Documentation

Detailed documentation is available in the docs/ folder.

DocumentDescription
docs/getting-started.mdSetup and first password flow.
docs/configuration.mdConfig options and defaults.
docs/api.mdPublic classes, methods, and result types.
docs/examples.mdExplanation of all included examples.
docs/security.mdPassword hashing and storage notes.
docs/memory.mdBuffers, mutexes, and synchronous behavior.
docs/troubleshooting.mdCommon issues and fixes.
docs/bcrypt-positioning.mdBcrypt-like format and compatibility limits.

API overview

Knot knot;
knot.init();
KnotSaltResult salt = knot.genSalt(14);
KnotHashResult hash = knot.hash("password", salt.value);
KnotCompareResult check = knot.compare("password", hash.value);
KnotRoundsResult cost = knot.getRounds(hash.value);
bool upgrade = knot.needsRehash(hash.value, 14);

For the full API, see docs/api.md.

Compatibility

ItemSupport
FrameworkArduino ESP32
Platformespressif32
LanguageC++20
Filesystemnone
PSRAMnot used in v0.1
DependenciesmbedTLS from ESP32 platform
ExceptionsNot used
StatusEarly-stage 0.1.0

Configuration

KnotConfig config;
config.defaultCost = 14;
config.minCost = 4;
config.maxCost = 16;
config.maxPasswordLength = 72;
KnotResult result = knot.init(config);

For all options, see docs/configuration.md.

Existing $knot$v1$c10$... hashes remain verifiable after the default cost change. They are treated as rehash candidates when checked against the new default cost 14.

Error handling

Knot reports operation status through result objects.

KnotHashResult hash = knot.hash("password");
if (!hash) {
Serial.println(hash.message);
return;
}

For result fields and status codes, see docs/api.md.

License

MIT - see LICENSE.md.

ZekStack

Part of the ZekStack ESP32 library stack.

About

Knot is a bcrypt-style password hashing library

Topics

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Knot

Knot is a compact ESP32 password hashing library with a bcrypt-like self-contained hash string format, backed by PBKDF2-HMAC-SHA256.

Knot helps Arduino ESP32 projects create and verify self-contained password hashes with secure salt generation, cost-based PBKDF2-HMAC-SHA256 hashing, constant-time comparison, cooperative verification, and bounded result buffers.

CIReleaseLicense: MIT

Why use Knot?

  • Password-focused - generate salts, hash passwords, compare stored hashes, and detect old costs.
  • Self-contained hashes - the encoded hash stores the algorithm marker, version, cost, salt, and derived key.
  • Cooperative verification - split PBKDF2 comparison into bounded steps for FreeRTOS and Worker integration.
  • ESP32-friendly - fixed public buffers, no exceptions, result-based errors, and optional mutex protection.
  • Familiar API shape - genSalt(), hash(), compare(), and getRounds() helpers without bcrypt compatibility claims.
  • Clear compatibility - Knot hashes are not bcrypt hashes and never use bcrypt $2a$, $2b$, or $2y$ prefixes.

Install

PlatformIO

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/knot.git
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Knot is not published to Arduino Library Manager yet.

Install it by downloading the repository ZIP or cloning it into your Arduino libraries folder.

Arduino/libraries/Knot

Quick start

#include<Arduino.h>
#include<Knot.h>
Knot knot;
voidsetup() {
Serial.begin(115200);
KnotResult init = knot.init();
if (!init) {
Serial.println(init.message);
return;
}
KnotHashResult hash = knot.hash("my-password");
if (!hash) {
Serial.println(hash.message);
return;
}
KnotCompareResult check = knot.compare("my-password", hash.value);
if (!check) {
Serial.println(check.message);
return;
}
Serial.println(check.match ? "valid" : "invalid");
}
voidloop() {
delay(1000);
}

Cooperative compare

Use a stateful operation when verification must return control to FreeRTOS between bounded PBKDF2 slices.

KnotCompareOperation operation;
KnotResult begin = knot.beginCompare(operation, password, encodedHash);
KnotStepResult step = operation.step(iterationBudget);
while (step.inProgress()) {
vTaskDelay(1);
step = operation.step(iterationBudget);
}
if (step.completed() && step.match) {
login();
}

Benchmark iterationBudget on the target and aim for the application's responsiveness window, normally around 5-20 ms per step(). Call operation.cancel() between steps when a request or Worker job is cancelled.

Important notes

Important

Knot uses PBKDF2-HMAC-SHA256, not bcrypt. Store and verify Knot hashes only with Knot.

  • v0.1 hashes use $knot$v1$c<cost>$<salt>$<hash>.
  • v1 means PBKDF2-HMAC-SHA256 with a 16-byte salt and 32-byte derived key.
  • Password input is limited to KNOT_MAX_PASSWORD_LENGTH bytes by default.
  • Cost 14 is the secure default, not the demo setting. Tune cost on real target hardware.
  • hash() and compare() are synchronous convenience APIs. Use beginCompare() and step() for cooperative verification.

Examples

ExampleDescription
BasicHashMinimal init, hash, and compare.
CooperativeCompareVerify a hash through bounded PBKDF2 steps.
ExplicitCostChoose a cost and read rounds/cost.
ManualSaltGenerate a salt and pass it into hash().
CallerBufferUse caller-owned buffers.
NeedsRehashUpgrade stored hashes after login.
HashInfoRead algorithm, version, and cost metadata.
ConfigurationConfigure password length and cost limits.
ClassUsageUse Knot from an application class.
BenchmarkPrint target, cost, iterations, timing, and heap measurements.

Start with:

examples/BasicHash

Documentation

Detailed documentation is available in the docs/ folder.

DocumentDescription
docs/getting-started.mdSetup and first password flow.
docs/configuration.mdConfig options and defaults.
docs/api.mdPublic classes, methods, and result types.
docs/examples.mdExplanation of all included examples.
docs/security.mdPassword hashing and storage notes.
docs/memory.mdBuffers, mutexes, and synchronous behavior.
docs/troubleshooting.mdCommon issues and fixes.
docs/bcrypt-positioning.mdBcrypt-like format and compatibility limits.

API overview

Knot knot;
knot.init();
KnotSaltResult salt = knot.genSalt(14);
KnotHashResult hash = knot.hash("password", salt.value);
KnotCompareResult check = knot.compare("password", hash.value);
KnotRoundsResult cost = knot.getRounds(hash.value);
bool upgrade = knot.needsRehash(hash.value, 14);

For the full API, see docs/api.md.

Compatibility

ItemSupport
FrameworkArduino ESP32
Platformespressif32
LanguageC++20
Filesystemnone
PSRAMnot used in v0.1
DependenciesmbedTLS from ESP32 platform
ExceptionsNot used
StatusEarly-stage 0.1.0

Configuration

KnotConfig config;
config.defaultCost = 14;
config.minCost = 4;
config.maxCost = 16;
config.maxPasswordLength = 72;
KnotResult result = knot.init(config);

For all options, see docs/configuration.md.

Existing $knot$v1$c10$... hashes remain verifiable after the default cost change. They are treated as rehash candidates when checked against the new default cost 14.

Error handling

Knot reports operation status through result objects.

KnotHashResult hash = knot.hash("password");
if (!hash) {
Serial.println(hash.message);
return;
}

For result fields and status codes, see docs/api.md.

License

MIT - see LICENSE.md.

ZekStack

Part of the ZekStack ESP32 library stack.

About

Knot is a bcrypt-style password hashing library

Topics

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Knot

Knot is a compact ESP32 password hashing library with a bcrypt-like self-contained hash string format, backed by PBKDF2-HMAC-SHA256.

Knot helps Arduino ESP32 projects create and verify self-contained password hashes with secure salt generation, cost-based PBKDF2-HMAC-SHA256 hashing, constant-time comparison, cooperative verification, and bounded result buffers.

CIReleaseLicense: MIT

Why use Knot?

  • Password-focused - generate salts, hash passwords, compare stored hashes, and detect old costs.
  • Self-contained hashes - the encoded hash stores the algorithm marker, version, cost, salt, and derived key.
  • Cooperative verification - split PBKDF2 comparison into bounded steps for FreeRTOS and Worker integration.
  • ESP32-friendly - fixed public buffers, no exceptions, result-based errors, and optional mutex protection.
  • Familiar API shape - genSalt(), hash(), compare(), and getRounds() helpers without bcrypt compatibility claims.
  • Clear compatibility - Knot hashes are not bcrypt hashes and never use bcrypt $2a$, $2b$, or $2y$ prefixes.

Install

PlatformIO

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/knot.git
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Knot is not published to Arduino Library Manager yet.

Install it by downloading the repository ZIP or cloning it into your Arduino libraries folder.

Arduino/libraries/Knot

Quick start

#include<Arduino.h>
#include<Knot.h>
Knot knot;
voidsetup() {
Serial.begin(115200);
KnotResult init = knot.init();
if (!init) {
Serial.println(init.message);
return;
}
KnotHashResult hash = knot.hash("my-password");
if (!hash) {
Serial.println(hash.message);
return;
}
KnotCompareResult check = knot.compare("my-password", hash.value);
if (!check) {
Serial.println(check.message);
return;
}
Serial.println(check.match ? "valid" : "invalid");
}
voidloop() {
delay(1000);
}

Cooperative compare

Use a stateful operation when verification must return control to FreeRTOS between bounded PBKDF2 slices.

KnotCompareOperation operation;
KnotResult begin = knot.beginCompare(operation, password, encodedHash);
KnotStepResult step = operation.step(iterationBudget);
while (step.inProgress()) {
vTaskDelay(1);
step = operation.step(iterationBudget);
}
if (step.completed() && step.match) {
login();
}

Benchmark iterationBudget on the target and aim for the application's responsiveness window, normally around 5-20 ms per step(). Call operation.cancel() between steps when a request or Worker job is cancelled.

Important notes

Important

Knot uses PBKDF2-HMAC-SHA256, not bcrypt. Store and verify Knot hashes only with Knot.

  • v0.1 hashes use $knot$v1$c<cost>$<salt>$<hash>.
  • v1 means PBKDF2-HMAC-SHA256 with a 16-byte salt and 32-byte derived key.
  • Password input is limited to KNOT_MAX_PASSWORD_LENGTH bytes by default.
  • Cost 14 is the secure default, not the demo setting. Tune cost on real target hardware.
  • hash() and compare() are synchronous convenience APIs. Use beginCompare() and step() for cooperative verification.

Examples

ExampleDescription
BasicHashMinimal init, hash, and compare.
CooperativeCompareVerify a hash through bounded PBKDF2 steps.
ExplicitCostChoose a cost and read rounds/cost.
ManualSaltGenerate a salt and pass it into hash().
CallerBufferUse caller-owned buffers.
NeedsRehashUpgrade stored hashes after login.
HashInfoRead algorithm, version, and cost metadata.
ConfigurationConfigure password length and cost limits.
ClassUsageUse Knot from an application class.
BenchmarkPrint target, cost, iterations, timing, and heap measurements.

Start with:

examples/BasicHash

Documentation

Detailed documentation is available in the docs/ folder.

DocumentDescription
docs/getting-started.mdSetup and first password flow.
docs/configuration.mdConfig options and defaults.
docs/api.mdPublic classes, methods, and result types.
docs/examples.mdExplanation of all included examples.
docs/security.mdPassword hashing and storage notes.
docs/memory.mdBuffers, mutexes, and synchronous behavior.
docs/troubleshooting.mdCommon issues and fixes.
docs/bcrypt-positioning.mdBcrypt-like format and compatibility limits.

API overview

Knot knot;
knot.init();
KnotSaltResult salt = knot.genSalt(14);
KnotHashResult hash = knot.hash("password", salt.value);
KnotCompareResult check = knot.compare("password", hash.value);
KnotRoundsResult cost = knot.getRounds(hash.value);
bool upgrade = knot.needsRehash(hash.value, 14);

For the full API, see docs/api.md.

Compatibility

ItemSupport
FrameworkArduino ESP32
Platformespressif32
LanguageC++20
Filesystemnone
PSRAMnot used in v0.1
DependenciesmbedTLS from ESP32 platform
ExceptionsNot used
StatusEarly-stage 0.1.0

Configuration

KnotConfig config;
config.defaultCost = 14;
config.minCost = 4;
config.maxCost = 16;
config.maxPasswordLength = 72;
KnotResult result = knot.init(config);

For all options, see docs/configuration.md.

Existing $knot$v1$c10$... hashes remain verifiable after the default cost change. They are treated as rehash candidates when checked against the new default cost 14.

Error handling

Knot reports operation status through result objects.

KnotHashResult hash = knot.hash("password");
if (!hash) {
Serial.println(hash.message);
return;
}

For result fields and status codes, see docs/api.md.

License

MIT - see LICENSE.md.

ZekStack

Part of the ZekStack ESP32 library stack.

About

Knot is a bcrypt-style password hashing library

Topics

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Knot

Knot is a compact ESP32 password hashing library with a bcrypt-like self-contained hash string format, backed by PBKDF2-HMAC-SHA256.

Knot helps Arduino ESP32 projects create and verify self-contained password hashes with secure salt generation, cost-based PBKDF2-HMAC-SHA256 hashing, constant-time comparison, cooperative verification, and bounded result buffers.

CIReleaseLicense: MIT

Why use Knot?

  • Password-focused - generate salts, hash passwords, compare stored hashes, and detect old costs.
  • Self-contained hashes - the encoded hash stores the algorithm marker, version, cost, salt, and derived key.
  • Cooperative verification - split PBKDF2 comparison into bounded steps for FreeRTOS and Worker integration.
  • ESP32-friendly - fixed public buffers, no exceptions, result-based errors, and optional mutex protection.
  • Familiar API shape - genSalt(), hash(), compare(), and getRounds() helpers without bcrypt compatibility claims.
  • Clear compatibility - Knot hashes are not bcrypt hashes and never use bcrypt $2a$, $2b$, or $2y$ prefixes.

Install

PlatformIO

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/knot.git
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Knot is not published to Arduino Library Manager yet.

Install it by downloading the repository ZIP or cloning it into your Arduino libraries folder.

Arduino/libraries/Knot

Quick start

#include<Arduino.h>
#include<Knot.h>
Knot knot;
voidsetup() {
Serial.begin(115200);
KnotResult init = knot.init();
if (!init) {
Serial.println(init.message);
return;
}
KnotHashResult hash = knot.hash("my-password");
if (!hash) {
Serial.println(hash.message);
return;
}
KnotCompareResult check = knot.compare("my-password", hash.value);
if (!check) {
Serial.println(check.message);
return;
}
Serial.println(check.match ? "valid" : "invalid");
}
voidloop() {
delay(1000);
}

Cooperative compare

Use a stateful operation when verification must return control to FreeRTOS between bounded PBKDF2 slices.

KnotCompareOperation operation;
KnotResult begin = knot.beginCompare(operation, password, encodedHash);
KnotStepResult step = operation.step(iterationBudget);
while (step.inProgress()) {
vTaskDelay(1);
step = operation.step(iterationBudget);
}
if (step.completed() && step.match) {
login();
}

Benchmark iterationBudget on the target and aim for the application's responsiveness window, normally around 5-20 ms per step(). Call operation.cancel() between steps when a request or Worker job is cancelled.

Important notes

Important

Knot uses PBKDF2-HMAC-SHA256, not bcrypt. Store and verify Knot hashes only with Knot.

  • v0.1 hashes use $knot$v1$c<cost>$<salt>$<hash>.
  • v1 means PBKDF2-HMAC-SHA256 with a 16-byte salt and 32-byte derived key.
  • Password input is limited to KNOT_MAX_PASSWORD_LENGTH bytes by default.
  • Cost 14 is the secure default, not the demo setting. Tune cost on real target hardware.
  • hash() and compare() are synchronous convenience APIs. Use beginCompare() and step() for cooperative verification.

Examples

ExampleDescription
BasicHashMinimal init, hash, and compare.
CooperativeCompareVerify a hash through bounded PBKDF2 steps.
ExplicitCostChoose a cost and read rounds/cost.
ManualSaltGenerate a salt and pass it into hash().
CallerBufferUse caller-owned buffers.
NeedsRehashUpgrade stored hashes after login.
HashInfoRead algorithm, version, and cost metadata.
ConfigurationConfigure password length and cost limits.
ClassUsageUse Knot from an application class.
BenchmarkPrint target, cost, iterations, timing, and heap measurements.

Start with:

examples/BasicHash

Documentation

Detailed documentation is available in the docs/ folder.

DocumentDescription
docs/getting-started.mdSetup and first password flow.
docs/configuration.mdConfig options and defaults.
docs/api.mdPublic classes, methods, and result types.
docs/examples.mdExplanation of all included examples.
docs/security.mdPassword hashing and storage notes.
docs/memory.mdBuffers, mutexes, and synchronous behavior.
docs/troubleshooting.mdCommon issues and fixes.
docs/bcrypt-positioning.mdBcrypt-like format and compatibility limits.

API overview

Knot knot;
knot.init();
KnotSaltResult salt = knot.genSalt(14);
KnotHashResult hash = knot.hash("password", salt.value);
KnotCompareResult check = knot.compare("password", hash.value);
KnotRoundsResult cost = knot.getRounds(hash.value);
bool upgrade = knot.needsRehash(hash.value, 14);

For the full API, see docs/api.md.

Compatibility

ItemSupport
FrameworkArduino ESP32
Platformespressif32
LanguageC++20
Filesystemnone
PSRAMnot used in v0.1
DependenciesmbedTLS from ESP32 platform
ExceptionsNot used
StatusEarly-stage 0.1.0

Configuration

KnotConfig config;
config.defaultCost = 14;
config.minCost = 4;
config.maxCost = 16;
config.maxPasswordLength = 72;
KnotResult result = knot.init(config);

For all options, see docs/configuration.md.

Existing $knot$v1$c10$... hashes remain verifiable after the default cost change. They are treated as rehash candidates when checked against the new default cost 14.

Error handling

Knot reports operation status through result objects.

KnotHashResult hash = knot.hash("password");
if (!hash) {
Serial.println(hash.message);
return;
}

For result fields and status codes, see docs/api.md.

License

MIT - see LICENSE.md.

ZekStack

Part of the ZekStack ESP32 library stack.

About

Knot is a bcrypt-style password hashing library

Topics

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Knot

Knot is a compact ESP32 password hashing library with a bcrypt-like self-contained hash string format, backed by PBKDF2-HMAC-SHA256.

Knot helps Arduino ESP32 projects create and verify self-contained password hashes with secure salt generation, cost-based PBKDF2-HMAC-SHA256 hashing, constant-time comparison, cooperative verification, and bounded result buffers.

CIReleaseLicense: MIT

Why use Knot?

  • Password-focused - generate salts, hash passwords, compare stored hashes, and detect old costs.
  • Self-contained hashes - the encoded hash stores the algorithm marker, version, cost, salt, and derived key.
  • Cooperative verification - split PBKDF2 comparison into bounded steps for FreeRTOS and Worker integration.
  • ESP32-friendly - fixed public buffers, no exceptions, result-based errors, and optional mutex protection.
  • Familiar API shape - genSalt(), hash(), compare(), and getRounds() helpers without bcrypt compatibility claims.
  • Clear compatibility - Knot hashes are not bcrypt hashes and never use bcrypt $2a$, $2b$, or $2y$ prefixes.

Install

PlatformIO

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/knot.git
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Knot is not published to Arduino Library Manager yet.

Install it by downloading the repository ZIP or cloning it into your Arduino libraries folder.

Arduino/libraries/Knot

Quick start

#include<Arduino.h>
#include<Knot.h>
Knot knot;
voidsetup() {
Serial.begin(115200);
KnotResult init = knot.init();
if (!init) {
Serial.println(init.message);
return;
}
KnotHashResult hash = knot.hash("my-password");
if (!hash) {
Serial.println(hash.message);
return;
}
KnotCompareResult check = knot.compare("my-password", hash.value);
if (!check) {
Serial.println(check.message);
return;
}
Serial.println(check.match ? "valid" : "invalid");
}
voidloop() {
delay(1000);
}

Cooperative compare

Use a stateful operation when verification must return control to FreeRTOS between bounded PBKDF2 slices.

KnotCompareOperation operation;
KnotResult begin = knot.beginCompare(operation, password, encodedHash);
KnotStepResult step = operation.step(iterationBudget);
while (step.inProgress()) {
vTaskDelay(1);
step = operation.step(iterationBudget);
}
if (step.completed() && step.match) {
login();
}

Benchmark iterationBudget on the target and aim for the application's responsiveness window, normally around 5-20 ms per step(). Call operation.cancel() between steps when a request or Worker job is cancelled.

Important notes

Important

Knot uses PBKDF2-HMAC-SHA256, not bcrypt. Store and verify Knot hashes only with Knot.

  • v0.1 hashes use $knot$v1$c<cost>$<salt>$<hash>.
  • v1 means PBKDF2-HMAC-SHA256 with a 16-byte salt and 32-byte derived key.
  • Password input is limited to KNOT_MAX_PASSWORD_LENGTH bytes by default.
  • Cost 14 is the secure default, not the demo setting. Tune cost on real target hardware.
  • hash() and compare() are synchronous convenience APIs. Use beginCompare() and step() for cooperative verification.

Examples

ExampleDescription
BasicHashMinimal init, hash, and compare.
CooperativeCompareVerify a hash through bounded PBKDF2 steps.
ExplicitCostChoose a cost and read rounds/cost.
ManualSaltGenerate a salt and pass it into hash().
CallerBufferUse caller-owned buffers.
NeedsRehashUpgrade stored hashes after login.
HashInfoRead algorithm, version, and cost metadata.
ConfigurationConfigure password length and cost limits.
ClassUsageUse Knot from an application class.
BenchmarkPrint target, cost, iterations, timing, and heap measurements.

Start with:

examples/BasicHash

Documentation

Detailed documentation is available in the docs/ folder.

DocumentDescription
docs/getting-started.mdSetup and first password flow.
docs/configuration.mdConfig options and defaults.
docs/api.mdPublic classes, methods, and result types.
docs/examples.mdExplanation of all included examples.
docs/security.mdPassword hashing and storage notes.
docs/memory.mdBuffers, mutexes, and synchronous behavior.
docs/troubleshooting.mdCommon issues and fixes.
docs/bcrypt-positioning.mdBcrypt-like format and compatibility limits.

API overview

Knot knot;
knot.init();
KnotSaltResult salt = knot.genSalt(14);
KnotHashResult hash = knot.hash("password", salt.value);
KnotCompareResult check = knot.compare("password", hash.value);
KnotRoundsResult cost = knot.getRounds(hash.value);
bool upgrade = knot.needsRehash(hash.value, 14);

For the full API, see docs/api.md.

Compatibility

ItemSupport
FrameworkArduino ESP32
Platformespressif32
LanguageC++20
Filesystemnone
PSRAMnot used in v0.1
DependenciesmbedTLS from ESP32 platform
ExceptionsNot used
StatusEarly-stage 0.1.0

Configuration

KnotConfig config;
config.defaultCost = 14;
config.minCost = 4;
config.maxCost = 16;
config.maxPasswordLength = 72;
KnotResult result = knot.init(config);

For all options, see docs/configuration.md.

Existing $knot$v1$c10$... hashes remain verifiable after the default cost change. They are treated as rehash candidates when checked against the new default cost 14.

Error handling

Knot reports operation status through result objects.

KnotHashResult hash = knot.hash("password");
if (!hash) {
Serial.println(hash.message);
return;
}

For result fields and status codes, see docs/api.md.

License

MIT - see LICENSE.md.

ZekStack

Part of the ZekStack ESP32 library stack.

About

Knot is a bcrypt-style password hashing library

Topics

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages