BLAKE3 on vuonna 2020 julkaistu hajautusalgoritmi, joka tuottaa jopa 3,5 kertaa suuremman suorituskyvyn kuin SHA-256 x86_64-arkkitehtuurilla. Algoritmilla on puumainen rinnakkaisrakenne, 128 bitin törmäyskestävyys ja laajennettava tulospiituus, joten se sopii erinomaisesti tiedostojen tarkistussummiin, viestiautentikointikoodeihin ja avainten johdannaisiin. Tässä oppaassa asennat blake3-paketin version 3.0.0, kirjoitat 10 toimivaa koodiesimerkkiä ja opit tyypillisimmät sudenkuopat tuotantokäytössä.
Mikä on BLAKE3 ja miksi se korvaa SHA-256:n monissa käyttötapauksissa
BLAKE3 syntyi BLAKE2:n jatkoksi. Jean-Philippe Aumasson, Samuel Neves, Zooko Wilcox-O’Hearn ja Jack O’Connor julkaisivat algoritmin tammikuussa 2020. Suunnittelun lähtökohta oli yhdistää BLAKE2:n nopeus Merkle-puurakenteeseen, joka mahdollistaa rinnakkaistamisen useilla prosessoriytimillä ja SIMD-vektoriyksiköillä.
Käytännön vaikutus on merkittävä. Viralliset vertailumittaukset x86_64 EPYC-prosessorilla osoittavat BLAKE3:n saavuttavan 6 121 MB/s 10 megatavun syötteellä, kun SHA-256 yltää 1 772 MB/s ja SHA-512 2 560 MB/s. Pienemmällä 64 tavun syötteellä ero on pienempi: BLAKE3 tuottaa 1 069 MB/s vastaan SHA-256:n 860 MB/s. Arm64-arkkitehtuurilla SHA-256 on paikoin nopeampi, koska prosessoreissa on usein laitteistokiihdytys SHA-laskennalle, mutta x86_64-palvelimilla BLAKE3 voittaa ylivoimaisesti suurilla syötteillä.
Turvallisuusominaisuudet vastaavat SHA-256:ta: törmäyskestävyys on 128 bittiä, esikuvakestävyys 256 bittiä ja toisen esikuvan kestävyys 256 bittiä. BLAKE3 tukee myös kolmea erikoistilaa, joita SHA-256 ei tarjoa suoraan: avaimellinen hajautus (keyed hash), avainten johdannainen (KDF) ja laajennettu tuloste (XOF). Solana-lohkoketju käyttää BLAKE3:a tilihajautuksiin, mikä osoittaa algoritmin kelpoisuuden tuotantojärjestelmissä.
Node.js:n sisäänrakennettu crypto-moduuli ei tue BLAKE3:a edes versiossa 22. Siksi tarvitset kolmannen osapuolen paketin. Kaksi suosituinta vaihtoehtoa ovat blake3 (npm, versio 3.0.0, käyttää WebAssemblyä ja natiivia sidontaa) ja @noble/hashes (versio 2.2.0, puhdas TypeScript-toteutus, nolla riippuvuutta, tietoturva-auditoitu). Tässä oppaassa käytetään molempia, mutta pääpaino on blake3-paketissa.
BLAKE3 vs SHA-256 vs SHA-512: suorituskyky ja turvallisuus
Ennen koodiin sukeltamista on hyödyllistä katsoa algoritmien ominaisuuksia rinnakkain. Alla oleva taulukko kokoaa viralliset spesifikaatiot ja vertailumittaukset.
| Algoritmi | Tulospiituus | Törmäyskestävyys | Rinnakkaistettavissa | Nopeus x86_64, 10 Mt (MB/s) | Nopeus x86_64, 64 B (MB/s) |
|---|---|---|---|---|---|
| BLAKE3 | Muuttuva (oletus 256 bit) | 128 bit | Kyllä (Merkle-puu) | 6 121 | 1 069 |
| SHA-256 | 256 bit | 128 bit | Ei | 1 772 | 860 |
| SHA-512 | 512 bit | 256 bit | Ei | 2 560 | 1 000 |
| SHA3-512 | 512 bit | 256 bit | Ei | Hitaampi kuin SHA-512 | Hitaampi kuin SHA-512 |
| BLAKE2b | Muuttuva (maks. 512 bit) | 256 bit | Rajoitetusti | ~3 000 | ~900 |
SHA-256 ja SHA-512 ovat tarpeellisia protokollissa, joissa standardi on pakollinen, kuten TLS-sertifikaateissa tai digitaalisissa allekirjoituksissa, koska ne noudattavat FIPS-standardeja. BLAKE3 sopii paremmin suorituskykykriittiseen sisäiseen käyttöön: tiedostojen eheyden tarkistamiseen, sisällön osoitteistamiseen (content-addressable storage) ja nopeaan MAC-laskentaan.
Esitietovaatimukset
Ennen aloittamista varmista, että kehitysympäristössäsi on seuraavat komponentit:
- Node.js 20.x LTS tai uudempi (blake3-paketti vaatii vähintään Node.js 16, mutta 20.x on suositeltava pitkäaikaistuki)
- npm 10.x tai yarn 4.x pakettienhallintaan
- Python 3.x node-gyp-kääntäjää varten (tarvitaan, jos blake3:n natiivi sidos ladataan)
- C++-kääntäjä: GCC 12+ Linuxilla, Clang 14+ macOS:lla, Visual C++ 2019+ Windowsilla
- Perustiedot Node.js:stä ja async/await-syntaksista
- Ymmärrys hajautusfunktioiden käytöstä (suositeltava: lue ensin SHA-256-artikkeli)
Tarkista Node.js-versio ennen aloittamista:
node --version
# v20.14.0 tai uudempi
npm --version
# 10.7.0 tai uudempi
Vaihe 1: Projektin rakenne ja alustus
Luo uusi hakemisto ja alusta npm-projekti. ESM-moduulijärjestelmä (ES Modules) on suositeltava, koska blake3 3.0.0 tukee sitä täysimääräisesti ja CommonJS-yhteensopivuus on rajatumpaa.
mkdir blake3-demo && cd blake3-demo
npm init -y
# Lisää "type": "module" package.json-tiedostoon
node -e "
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
pkg.type = 'module';
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2));
console.log('package.json päivitetty: type=module');
"
mkdir src test
Projektin lopullinen hakemistorakenne tulee näyttämään tältä:
blake3-demo/
├── package.json
├── src/
│ ├── basic-hash.mjs
│ ├── file-hash.mjs
│ ├── keyed-hash.mjs
│ ├── kdf.mjs
│ ├── xof.mjs
│ ├── cas.mjs
│ └── benchmark.mjs
└── test/
└── hash.test.mjs
Vaihe 2: blake3-paketin asennus ja vahvistus
Asenna blake3-paketti tarkalla versionumerolla. Paketti lataa ensin natiivisen sidonnan node-gyp:n avulla. Jos kääntäminen epäonnistuu, se palaa automaattisesti WebAssembly-toteutukseen.
npm install [email protected]
# Tarkista asennus ja API
node --input-type=module << 'EOF'
import blake3 from 'blake3';
console.log('blake3 versio 3.0.0 ladattu');
console.log('Saatavilla olevat funktiot:', Object.keys(blake3).join(', '));
EOF
Onnistuneen asennuksen jälkeen terminaalissa näkyy:
blake3 versio 3.0.0 ladattu
Saatavilla olevat funktiot: hash, createHash, keyedHash, createKeyedHash, derive, createDeriveKey
Jos natiivinen sidonta käännetään onnistuneesti, BLAKE3 käyttää C++-toteutusta AVX2- tai AVX512-vektoriohjeilla ja saavuttaa täyden suorituskyvyn. WebAssembly-varajärjestelmä on noin 30 prosenttia hitaampi kuin natiivitoteutus, joten tuotantopalvelimella kannattaa varmistaa, että kääntäjäriippuvuudet ovat kunnossa ennen käyttöönottoa.
Vaihe 3: Perustiivisteen laskeminen
BLAKE3:n yksinkertaisin käyttötapaus on tiivisteen laskeminen merkkijonosta tai Buffer-objektista. Luo tiedosto src/basic-hash.mjs:
import { hash } from 'blake3';
// Merkkijonon tiiviste, eksplisiittinen UTF-8-enkoodaus
const viesti = 'Hei, maailma!';
const tiiviste = hash(Buffer.from(viesti, 'utf8'));
console.log('Tiiviste (hex):', tiiviste.toString('hex'));
// 256-bittinen (32 tavua) heksadesimaalimerkkijono
// Uint8Array toimii myös suoraan
const data = new TextEncoder().encode('Hei, maailma!');
const tiiviste2 = hash(data);
console.log('Sama tulos:', tiiviste.toString('hex') === tiiviste2.toString('hex'));
// true - molemmat tuottavat saman tiivisteen
// Pidempi tuloste: 512 bittiä (64 tavua)
const pitkaTiiviste = hash(Buffer.from(viesti, 'utf8'), { length: 64 });
console.log('512-bittinen:', pitkaTiiviste.toString('hex'));
console.log('Pituus:', pitkaTiiviste.length, 'tavua');
Aja skripti komennolla node src/basic-hash.mjs. Odotettu tuloste:
Tiiviste (hex): a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3
Sama tulos: true
512-bittinen: a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3...
Pituus: 64 tavua
hash()-funktio on synkroninen. Se ottaa syötteekseen Buffer:n, Uint8Array:n tai string:n. Oletustulospiituus on 32 tavua (256 bittiä), mutta length-optiolla sen voi asettaa haluamaansa kokoon. Tärkeää: eksplisiittinen UTF-8-enkoodaus estää yllätykset eri alustoilla.
Vaihe 4: Suurten tiedostojen hajautus stream-rajapinnalla
Suurten tiedostojen tiivisteen laskeminen kerralla muistiin ladattuna on tehotonta ja voi kaataa sovelluksen muistin loppuessa. BLAKE3:n createHash() tuottaa Node.js-yhteensopivan Writable stream -objektin, jolla tiedoston voi käsitellä paloina.
Luo src/file-hash.mjs:
import { createHash } from 'blake3';
import { createReadStream } from 'fs';
import { pipeline } from 'stream/promises';
async function laskeTiedostoTiiviste(polku, puskuriKoko = 64 * 1024) {
const hasher = createHash();
const lukija = createReadStream(polku, { highWaterMark: puskuriKoko });
await pipeline(lukija, hasher);
return hasher.digest('hex');
}
// Laske tämän skriptin oma tiiviste
const tiiviste = await laskeTiedostoTiiviste('./src/file-hash.mjs');
console.log('Tiedoston BLAKE3-tiiviste:', tiiviste);
// Vertaa suoritusaikaa eri puskurikoilla
for (const koko of [4096, 65536, 1024 * 1024]) {
const alku = performance.now();
await laskeTiedostoTiiviste('/dev/zero', koko);
const aika = performance.now() - alku;
console.log(`Puskuri ${koko} B: ${aika.toFixed(1)} ms`);
}
Stream-rajapinta on välttämätön gigatasoisissa tiedostoissa. 64 kilotavun puskurikoko (highWaterMark: 64 * 1024) on yleinen optimum, joka tasapainottaa I/O-pyyntöjen määrän ja muistin käytön. Isompi puskuri ei välttämättä nopeuta laskentaa, koska pullonkaula on usein levyn I/O, ei itse hajautusfunktio.
Vaihe 5: Avaimellinen hajautus viestien autentikoinnissa
BLAKE3:n avaimellinen hajautustila toimii MAC-koodina (Message Authentication Code) ilman HMAC-rakennetta. SHA-256-pohjaisessa HMAC:ssa algoritmi ajetaan kahdesti (katso HMAC Node.js:ssä -opas), mutta BLAKE3:n keyed hash tuottaa saman turvallisuustason yhdellä läpäisyllä. Tulos: noin puolet laskenta-ajasta.
Luo src/keyed-hash.mjs:
import { keyedHash } from 'blake3';
import { randomBytes, timingSafeEqual } from 'crypto';
// Avain täytyy olla tarkalleen 32 tavua (256 bittiä)
const avain = randomBytes(32);
const viesti = Buffer.from('Maksutieto: 100 EUR asiakkaalle #4421', 'utf8');
// Laske MAC
const mac = keyedHash(avain, viesti);
console.log('MAC (hex):', mac.toString('hex'));
// Todenna viesti oikealla avaimella
const tarkistus = keyedHash(avain, viesti);
const onValidi = timingSafeEqual(mac, tarkistus);
console.log('Todentaminen onnistui:', onValidi); // true
// Väärä avain tuottaa eri MAC:n
const vaaraAvain = randomBytes(32);
const vaaraTarkistus = keyedHash(vaaraAvain, viesti);
const onVaara = timingSafeEqual(mac, vaaraTarkistus);
console.log('Väärä avain hylätty:', !onVaara); // true
// Käytännön API-esimerkki: webhook-allekirjoitusten todentaminen
function todennaWebhook(avain, runko, allekirjoitus) {
const laskettu = keyedHash(avain, Buffer.from(runko, 'utf8'));
const odotettu = Buffer.from(allekirjoitus, 'hex');
if (laskettu.length !== odotettu.length) return false;
return timingSafeEqual(laskettu, odotettu);
}
const webhookRunko = JSON.stringify({ tapahtuma: 'maksu', summa: 100 });
const webhookAllekirjoitus = keyedHash(avain, Buffer.from(webhookRunko)).toString('hex');
console.log('Webhook validi:', todennaWebhook(avain, webhookRunko, webhookAllekirjoitus));
Avaimellinen hajautus on parempi vaihtoehto kuin yksinkertainen tiiviste salaisuuden lisäämiseen (ns. "secret prefix" tai "secret suffix" -menetelmät). Rakenteessa hash(salaisuus + viesti) pituuslaajennushyökkäys on mahdollinen SHA-256:n kanssa. BLAKE3:n keyed hash on suunniteltu välttämään tämä ongelma alusta asti.
Vaihe 6: Avainten johdannainen (Key Derivation Function)
BLAKE3:n KDF-tila mahdollistaa useiden avainten johtamisen yhdestä pääavaimesta. Tämä on välttämätöntä, kun samasta salaisuudesta tarvitaan erillisiä avaimia eri käyttötarkoituksiin, kuten salaukseen ja autentikointiin. Luo src/kdf.mjs:
import { derive } from 'blake3';
import { randomBytes } from 'crypto';
// Pääavain (esim. DH-avaintenvaihdon tulos tai session secret)
const paaSalaisuus = randomBytes(32);
// Kontekstimerkintä identifioi käyttötarkoituksen yksilöllisesti.
// Sen täytyy pysyä muuttumattomana koko sovelluksen elinkaaren ajan.
const SALAUS_KTX = 'blake3-demo 2026-06-17 aes256-gcm salausavain v1';
const HMAC_KTX = 'blake3-demo 2026-06-17 hmac-sha256 autentikointi v1';
// Johda kaksi erillistä avainta samasta pääsalaisuudesta
const salausAvain = derive(SALAUS_KTX, paaSalaisuus);
const authAvain = derive(HMAC_KTX, paaSalaisuus);
console.log('Salausavain:', salausAvain.toString('hex'));
console.log('Authavain: ', authAvain.toString('hex'));
console.log('Avaimet eroavat:', salausAvain.toString('hex') !== authAvain.toString('hex'));
// true - sama pääsalaisuus, eri kontekstit, eri avaimet
// Johda 64-tavuinen avain AES-256:lle ja IV:lle kerralla
const laajaAvain = derive(SALAUS_KTX, paaSalaisuus, { length: 64 });
const aesAvain = laajaAvain.slice(0, 32); // 256-bittinen AES-avain
const iv = laajaAvain.slice(32, 48); // 128-bittinen IV
console.log('AES-avain:', aesAvain.toString('hex'));
console.log('IV:', iv.toString('hex'));
console.log('Pituudet:', aesAvain.length, 'ja', iv.length, 'tavua');
Kontekstimerkintä on kriittinen turvallisuuden kannalta. Hyväksi käytännöksi on vakiintunut sisällyttää sovelluksen nimi, päivämäärä, käyttötarkoitus ja versionumero. Muuta kontekstia vain, jos haluat tarkoituksellisesti tehdä vanhoista johdetuista avaimista käyttökelvottomia.
Vaihe 7: Laajennettu tuloste (XOF)
BLAKE3 toimii myös XOF-funktiona (eXtendable Output Function), jolloin se tuottaa mielivaltaisen pitkän deterministisen tulosteen. Tämä sopii pseudosatunnaistiedon generointiin tai lookup-taulukoiden rakentamiseen. Luo src/xof.mjs:
import { hash } from 'blake3';
const siemen = Buffer.from('deterministinen siemen 2026', 'utf8');
// Tuota 128 tavua (1024 bittiä) laajennettua tulostetta
const xof128 = hash(siemen, { length: 128 });
console.log('XOF 128 tavua:', xof128.toString('hex'));
console.log('Pituus:', xof128.length, 'tavua');
// Ensimmäiset 32 tavua vastaavat oletustiivistettä
const perus = hash(siemen);
console.log('Alkaa oletustiivisteellä:',
Buffer.compare(perus, xof128.slice(0, 32)) === 0); // true
// Käyttötapaus: generoi deterministinen S-laatikko kryptografiaan
function generoiSLaatikko(avain, koko = 256) {
const data = hash(Buffer.from(avain, 'utf8'), { length: koko });
return Array.from(data);
}
const slaatikko = generoiSLaatikko('sovellus-v1-slaatikko', 256);
console.log('S-laatikko (ensimmäiset 8 arvoa):', slaatikko.slice(0, 8));
console.log('S-laatikko koko:', slaatikko.length);
Vaihe 8: Suorituskykymittaus ja vertailu
Mitataan BLAKE3:n todellinen suorituskyky kehitysympäristössäsi ja verrataan sitä Node.js:n sisäänrakennettuun SHA-256:een. Luo src/benchmark.mjs:
import { hash } from 'blake3';
import { createHash } from 'crypto';
function mittaa(nimi, fn, iteraatiot, dataKoko) {
// Lämmittelyvaihe estää JIT-kääntäjän vinouttamisen
for (let i = 0; i < 200; i++) fn();
const alku = performance.now();
for (let i = 0; i < iteraatiot; i++) fn();
const ms = performance.now() - alku;
const mbps = (dataKoko * iteraatiot) / (ms / 1000) / (1024 * 1024);
return { nimi, mbps: mbps.toFixed(0) };
}
const KOOT = [
{ koko: 64, iter: 50000, label: '64 B' },
{ koko: 1024, iter: 20000, label: '1 kt' },
{ koko: 65536, iter: 2000, label: '64 kt' },
{ koko: 1024 * 1024, iter: 200, label: '1 Mt' },
];
console.log('Algoritmi | 64 B | 1 kt | 64 kt | 1 Mt');
console.log('--------------|---------|---------|---------|--------');
const blake3Tulokset = [];
const sha256Tulokset = [];
for (const { koko, iter, label } of KOOT) {
const data = Buffer.allocUnsafe(koko).fill(0x42);
blake3Tulokset.push(mittaa('BLAKE3', () => hash(data), iter, koko));
sha256Tulokset.push(mittaa('SHA-256', () => createHash('sha256').update(data).digest(), iter, koko));
}
console.log('BLAKE3 ', blake3Tulokset.map(r => r.mbps.padStart(7) + ' MB/s').join(' | '));
console.log('SHA-256 ', sha256Tulokset.map(r => r.mbps.padStart(7) + ' MB/s').join(' | '));
const suhde = blake3Tulokset.map((b, i) =>
(parseInt(b.mbps) / parseInt(sha256Tulokset[i].mbps)).toFixed(1) + 'x'
);
console.log('Suhde ', suhde.map(s => s.padStart(7)).join(' | '));
Tyypilliset tulokset modernilla x86_64-palvelimella Node.js 20.x:llä natiivisidonnalla:
| Algoritmi | 64 B | 1 kt | 64 kt | 1 Mt |
|---|---|---|---|---|
| BLAKE3 | ~950 MB/s | ~2 800 MB/s | ~5 100 MB/s | ~5 800 MB/s |
| SHA-256 | ~820 MB/s | ~1 400 MB/s | ~1 700 MB/s | ~1 700 MB/s |
| Suhde | 1,2x | 2,0x | 3,0x | 3,4x |
Suorituskykyetu kasvaa syötteen koon mukana, koska BLAKE3:n Merkle-puurakenne hyödyntää prosessorin välimuistihierarkiaa paremmin kuin SHA-256:n peräkkäinen kompressiorakenne. Pienillä syötteillä ero on vähäinen, suurilla (yli 64 kt) BLAKE3 on 3 kertaa nopeampi.
Vaihe 9: Content-addressable storage tiedostovarastona
Content-addressable storage (CAS) tallentaa tiedostot niiden tiivisteen perusteella nimettyinä. Git käyttää vastaavaa rakennetta SHA-256:n kanssa. BLAKE3 on ihanteellinen CAS:iin suuren nopeutensa ansiosta. Luo src/cas.mjs:
import { hash } from 'blake3';
import { writeFile, readFile, mkdir, access } from 'fs/promises';
import { join } from 'path';
import { constants } from 'fs';
const VARASTO = './blake3-cas';
async function tallenna(data) {
const tiiviste = hash(Buffer.isBuffer(data) ? data : Buffer.from(data)).toString('hex');
const hakemisto = join(VARASTO, tiiviste.slice(0, 2));
const polku = join(hakemisto, tiiviste);
// Älä kirjoita uudelleen, jos jo olemassa (sisältö identtinen tiivisteen perusteella)
try {
await access(polku, constants.F_OK);
return tiiviste; // Jo tallennettu
} catch { /* ei olemassa, jatka */ }
await mkdir(hakemisto, { recursive: true });
await writeFile(polku, Buffer.isBuffer(data) ? data : Buffer.from(data));
return tiiviste;
}
async function hae(tiiviste) {
const polku = join(VARASTO, tiiviste.slice(0, 2), tiiviste);
const data = await readFile(polku);
// Todenna eheys latauksen jälkeen
const tarkistus = hash(data).toString('hex');
if (tarkistus !== tiiviste) {
throw new Error(`Eheysvirhe: odotettu ${tiiviste}, laskettu ${tarkistus}`);
}
return data;
}
// Testi
const osoite = await tallenna('Tärkeä dokumentti 2026');
console.log('Tallennettu:', osoite);
const haettu = await hae(osoite);
console.log('Haettu:', haettu.toString('utf8'));
// Sama sisältö tallennetaan vain kerran
const osoite2 = await tallenna('Tärkeä dokumentti 2026');
console.log('Sama osoite:', osoite === osoite2); // true
Vaihe 10: Yksikkötestit ja CI/CD-integrointi
Node.js 20.x:ssä on sisäänrakennettu testikehys. Luo test/hash.test.mjs:
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { hash, keyedHash, derive, createHash } from 'blake3';
describe('BLAKE3 perustiiviste', () => {
it('tuottaa 32-tavuisen oletustulosteen', () => {
const tulos = hash(Buffer.from('testi', 'utf8'));
assert.equal(tulos.length, 32);
});
it('tuottaa muuttuvan pituisen tulosteen', () => {
const tulos = hash(Buffer.from('testi', 'utf8'), { length: 64 });
assert.equal(tulos.length, 64);
});
it('sama syöte tuottaa aina saman tiivisteen', () => {
const syote = Buffer.from('deterministinen', 'utf8');
assert.equal(hash(syote).toString('hex'), hash(syote).toString('hex'));
});
it('eri syötteet tuottavat eri tiivisteet', () => {
assert.notEqual(
hash(Buffer.from('a', 'utf8')).toString('hex'),
hash(Buffer.from('b', 'utf8')).toString('hex')
);
});
it('tyhjä syöte toimii', () => {
const tulos = hash(Buffer.alloc(0));
assert.equal(tulos.length, 32);
});
});
describe('BLAKE3 stream-rajapinta', () => {
it('tuottaa saman tuloksen kuin suora hash()', () => {
const data = Buffer.from('stream-testi', 'utf8');
const hasher = createHash();
hasher.update(data);
const streamTulos = hasher.digest('hex');
const suoraTulos = hash(data).toString('hex');
assert.equal(streamTulos, suoraTulos);
});
it('useat update()-kutsut toimivat', () => {
const hasher = createHash();
hasher.update(Buffer.from('osa1', 'utf8'));
hasher.update(Buffer.from('osa2', 'utf8'));
const streamTulos = hasher.digest('hex');
const suoraTulos = hash(Buffer.from('osa1osa2', 'utf8')).toString('hex');
assert.equal(streamTulos, suoraTulos);
});
});
describe('BLAKE3 avaimellinen hajautus', () => {
it('vaatii tarkalleen 32-tavuisen avaimen', () => {
const avain = Buffer.alloc(32, 0x01);
const mac = keyedHash(avain, Buffer.from('viesti', 'utf8'));
assert.equal(mac.length, 32);
});
it('eri avain tuottaa eri MAC:n', () => {
const viesti = Buffer.from('sama viesti', 'utf8');
const mac1 = keyedHash(Buffer.alloc(32, 0x01), viesti).toString('hex');
const mac2 = keyedHash(Buffer.alloc(32, 0x02), viesti).toString('hex');
assert.notEqual(mac1, mac2);
});
});
describe('BLAKE3 avainten johdannainen', () => {
it('eri konteksti tuottaa eri avaimen', () => {
const materiaali = Buffer.alloc(32, 0xFF);
const k1 = derive('konteksti-A', materiaali).toString('hex');
const k2 = derive('konteksti-B', materiaali).toString('hex');
assert.notEqual(k1, k2);
});
it('sama konteksti ja materiaali tuottavat saman avaimen', () => {
const materiaali = Buffer.alloc(32, 0xAB);
const k1 = derive('sama-konteksti', materiaali).toString('hex');
const k2 = derive('sama-konteksti', materiaali).toString('hex');
assert.equal(k1, k2);
});
});
Aja testit Node.js:n omalla testikehyksellä:
node --test test/hash.test.mjs
# Odotettu tuloste:
# ✔ BLAKE3 perustiiviste > tuottaa 32-tavuisen oletustulosteen (1.23ms)
# ✔ BLAKE3 perustiiviste > tuottaa muuttuvan pituisen tulosteen (0.45ms)
# ✔ BLAKE3 perustiiviste > sama syöte tuottaa aina saman tiivisteen (0.12ms)
# ✔ BLAKE3 perustiiviste > eri syötteet tuottavat eri tiivisteet (0.11ms)
# ✔ BLAKE3 perustiiviste > tyhjä syöte toimii (0.09ms)
# ✔ BLAKE3 stream-rajapinta > tuottaa saman tuloksen kuin suora hash() (0.18ms)
# ✔ BLAKE3 stream-rajapinta > useat update()-kutsut toimivat (0.14ms)
# ✔ BLAKE3 avaimellinen hajautus > vaatii tarkalleen 32-tavuisen avaimen (0.11ms)
# ✔ BLAKE3 avaimellinen hajautus > eri avain tuottaa eri MAC:n (0.13ms)
# ✔ BLAKE3 avainten johdannainen > eri konteksti tuottaa eri avaimen (0.10ms)
# ✔ BLAKE3 avainten johdannainen > sama konteksti ja materiaali tuottavat saman avaimen (0.09ms)
# ℹ tests 11
# ℹ pass 11
# ℹ fail 0
Lisää testit GitHub Actions -putkeen (.github/workflows/ci.yml):
name: BLAKE3 CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.x, 22.x]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: node --test test/hash.test.mjs
5 yleistä virhettä BLAKE3:n käytössä Node.js:ssä
Seuraavat virheet toistuvat usein kehittäjien ottaessa BLAKE3:a käyttöön ensimmäistä kertaa.
Virhe 1: Merkkijonon suora syöttäminen ilman enkoodausta. blake3-paketti hyväksyy merkkijonot, mutta enkoodaus voi vaihdella kontekstin mukaan. Turvallisin tapa on aina muuntaa Buffer.from(str, 'utf8')-muotoon. Ilman eksplisiittistä enkoodausta eri alustojen välillä voi syntyä tiivistepoikkeamia, joita on vaikea debugata.
Virhe 2: Väärä avaimen pituus keyedHash()-funktiossa. Avaimellinen hajautus vaatii tarkalleen 32 tavua. Liian lyhyt tai pitkä avain aiheuttaa poikkeuksen key must be 32 bytes. Yleinen sudenkuoppa on käyttää salasanaa tai muuta muuttuvan pituisen merkkijonon suoraan avaimena:
// VÄÄRIN: salasana ei ole 32 tavua
const avain = Buffer.from('salasana', 'utf8'); // vain 8 tavua
keyedHash(avain, data); // Heittää virheen!
// OIKEIN: johda 32-tavuinen avain salasanasta
import { scryptSync } from 'crypto';
const avain32 = scryptSync('salasana', 'suola', 32);
keyedHash(avain32, data); // Toimii
Virhe 3: Timing-safe vertailun laiminlyönti. Tiivisteen vertailu ===-operaattorilla on altis ajoitushyökkäyksille, koska JavaScript palaa heti ensimmäisestä poikkeavasta merkistä. Käytä aina crypto.timingSafeEqual():
// VÄÄRIN: vuotaa tietoa ajoituksen kautta
if (laskettuMac.toString('hex') === asiakkaanMac) { ... }
// OIKEIN: vakioaikainen vertailu
import { timingSafeEqual } from 'crypto';
if (timingSafeEqual(laskettuMac, Buffer.from(asiakkaanMac, 'hex'))) { ... }
Virhe 4: createHash()-objektin uudelleenkäyttö digest()-kutsun jälkeen. Kun hasher.digest() kutsutaan, objekti merkitään päättyneeksi. Uusi hasher.update() sen jälkeen ei tuota oikeaa tulosta. Luo aina uusi instanssi jokaista erillistä hajautusta varten.
Virhe 5: BLAKE3:n käyttö salasanojen tiivistykseen. BLAKE3 on suunniteltu nopeudelle, mikä on täsmälleen päinvastainen ominaisuus kuin mitä salasanojen suojauksessa tarvitaan. Nopea hajautusfunktio tekee brute-force-hyökkäyksestä tehokkaampaa. Salasanoihin käytä aina tarkoitukseen suunniteltua KDF:ää: Argon2id (suositeltavin 2026), bcrypt tai scrypt.
Vianmääritys: 8 yleistä ongelmaa ratkaisuineen
Ongelma 1: node-gyp kääntäminen epäonnistuu Windowsilla. Virheviesti: gyp ERR! build error. Ratkaisu: asenna Visual Studio Build Tools 2019 tai uudempi C++-työkuormalla. Jos kääntäjää ei voi asentaa, käytä npm install [email protected] --ignore-scripts, jolloin paketti käyttää WebAssembly-toteutusta.
Ongelma 2: ESM-yhteensopivuusvirhe CommonJS-ympäristössä. Virheviesti: SyntaxError: Cannot use import statement in a CommonJS module. Ratkaisu: lisää "type": "module" package.json:iin tai käytä dynaamista importia: const { hash } = await import('blake3').
Ongelma 3: Suorituskyky on odotettua heikompi. Tarkista, onko natiivinen sidonta aktiiivinen vai WASM-varajärjestelmä käytössä. Jos npm rebuild blake3 ei auta, asenna ensin node-gyp globaalisti: npm install -g node-gyp, ja varmista Python 3 ja C++-kääntäjä.
Ongelma 4: Eri alustojen tuottamat tiivisteet eroavat. BLAKE3 on täysin deterministinen. Jos saat eri tiivisteen samasta syötteestä eri alustoilla, syy on lähes aina enkoodauserot. Tarkista, että syöte on Buffer-objekti, ei merkkijono.
Ongelma 5: Muistivuoto pitkäaikaisessa server-käytössä. createHash()-objekteja ei saa jättää roikkumaan. Pipeline-käytössä varmista pipeline()-funktion oikea käyttö ja that stream sulkeutuu virheen sattuessa. Lisää try/finally-rakenne kriittisiin kohtiin.
Ongelma 6: derive()-funktio tuottaa eri tulokset eri paketin versioissa. Blake3 3.0.0 teki murtavan muutoksen. Lukitse versio tarkasti: "blake3": "3.0.0" (ei ^3.0.0). Käytä npm ci CI/CD-putkessa, ei npm install, jotta versio pysyy kiinnitettynä.
Ongelma 7: TypeScript-tyypit eivät löydy. Varmista, että tsconfig.json:ssa on "moduleResolution": "bundler" tai "node16". Vaihtoehto: käytä @noble/hashes-pakettia, joka on kirjoitettu alun perin TypeScriptillä ja jonka tyypit ovat täydelliset.
Ongelma 8: Testit epäonnistuvat rinnakkaisessa ajossa. createHash()-instanssi ei ole säievarmistettu. Jokaisessa testissä täytyy olla oma instanssi. Jos testit jakavat yhteistä tilaa epähuomiossa, lisää beforeEach-koukku, joka luo uuden hasher-objektin.
Edistyneet tekniikat tuotantokäyttöön
Worker Threads rinnakkaistamiseen
Node.js:n yksisäikeinen tapahtumasilmukka rajoittaa BLAKE3:n Merkle-puun rinnakkaistamisen hyötyä. Worker Threads -laajennuksella voit jakaa suuren tiedoston paloihin ja käsitellä ne rinnakkaisesti. Käytännöllisin jako: jaetaan tiedosto 1 Mt:n segmentteihin, lasketaan segmenttikohtaiset tiivisteet erillisessä säikeessä ja yhdistetään koko tiedoston tiiviste pääsäikeessä. Tämä on hyödyllistä, kun tiedostoja on useita kymmeniä prosessoitavana yhtä aikaa.
@noble/hashes vaihtoehtona serverless-ympäristöissä
Jos natiivinen sidonta ei ole mahdollinen, esimerkiksi Cloudflare Workers tai AWS Lambda -ympäristöissä, @noble/hashes versio 2.2.0 on paras vaihtoehto. Se on tietoturva-auditoitu, kirjoitettu puhtaasti TypeScriptillä ja toimii ilman build-riippuvuuksia:
import { blake3 } from '@noble/hashes/blake3';
import { bytesToHex } from '@noble/hashes/utils';
// Perustiiviste
const tiiviste = blake3(new TextEncoder().encode('Hei Suomi'));
console.log(bytesToHex(tiiviste));
// Avaimellinen hajautus
const avain = new Uint8Array(32).fill(1);
const mac = blake3(new TextEncoder().encode('viesti'), { key: avain });
// Avainten johdannainen
const johdettu = blake3(new TextEncoder().encode('materiaali'), {
context: 'sovellus 2026 avainjohdannainen v1'
});
console.log('Johdettu avain:', bytesToHex(johdettu));
Sama koodirakenne toimii selaimessa, Deno:ssa, Bun:issa ja Node.js:ssä. Ed25519-allekirjoituksiin (katso Ed25519 Node.js:ssä -opas) noble-kirjasto tarjoaa identtisen lähestymistavan auditoidun koodin kanssa.
Webhook-allekirjoitusten varmentaminen tuotannossa
BLAKE3:n avaimellinen hajautus on nopein tapa varmentaa webhook-allekirjoituksia, kuten Stripe- tai GitHub-webhookeja, jos molemmat osapuolet käyttävät BLAKE3:a. Käytännössä useimmat palvelut lähettävät HMAC-SHA256-allekirjoituksen, joten vertailu tapahtuu crypto.createHmac('sha256', avain)-funktiolla. BLAKE3-MAC sopii parhaiten sovelluksen sisäisiin rajapintoihin, joissa molemmat päät ovat hallinnassasi.
Milloin BLAKE3 ei ole oikea valinta
BLAKE3 ei sovi kaikkiin tilanteisiin. Seuraavat käyttötapaukset vaativat muun algoritmin:
- TLS-sertifikaatit ja X.509: Sertifikaattiviranomaisten allekirjoitukset käyttävät SHA-256 tai SHA-384:ä. BLAKE3 ei ole FIPS-hyväksytty eikä CA/Browser Forumin sallima.
- Salasanojen tiivistäminen: BLAKE3 on liian nopea suojaamaan salasanoja. Käytä Argon2id:tä tai bcrypt:ä, jotka on suunniteltu hidastamaan brute-force-hyökkäyksiä.
- FIPS 140-2/3 -ympäristöt: BLAKE3 ei kuulu NIST:n hyväksymiin algoritmeihin. Käytä SHA-2 tai SHA-3 -perheen algoritmeja.
- Standardin velvoittamat digitaaliset allekirjoitukset: RSA-PKCS1 ja ECDSA käyttävät SHA-256:ta tai SHA-512:a standardin mukaan. BLAKE3-pohjainen allekirjoitusjärjestelmä ei ole yhteensopiva vakioprotokollien kanssa.
- Vanha järjestelmäintegraatio: Jos toinen järjestelmä odottaa SHA-256-tiivistettä, BLAKE3 ei ole suoraan korvattavissa ilman molempien päiden muuttamista.
Usein kysytyt kysymykset
Onko BLAKE3 turvallinen tuotantokäyttöön vuonna 2026? Kyllä. BLAKE3:a on analysoitu kryptografiyhteisön toimesta vuodesta 2020, eikä merkittäviä haavoittuvuuksia ole löydetty. Solana käyttää algoritmia tuotannossa tilihajautuksiin. Se ei kuitenkaan ole FIPS-hyväksytty, joten FIPS-ympäristöihin se ei sovi.
Voinko korvata SHA-256:n BLAKE3:lla kaikessa koodissani? Ei kaikessa. Protokollapohjaisessa koodissa (JWT, TLS, S3-allekirjoitukset) SHA-256 on pakollinen standardi. Sovelluksen sisäisessä logiikassa BLAKE3 on usein parempi valinta nopeuden ja monipuolisuutensa ansiosta.
Mitä eroa on blake3-paketilla ja @noble/hashes-paketilla? blake3-paketti käyttää natiivista C++-sidontaa tai WebAssemblyä, mikä tekee siitä nopeamman erityisesti suurilla syötteillä. @noble/hashes on puhdas TypeScript-toteutus, joka on tietoturva-auditoitu ja toimii ilman build-riippuvuuksia. Palvelimilla blake3 on yleensä nopeampi, serverless-ympäristöissä @noble/hashes on käytännöllisempi.
Kuinka suuri suorituskykyero on käytännössä Node.js:ssä? Natiivisidonnalla BLAKE3 on noin 3,4 kertaa nopeampi kuin SHA-256 megatavun syötteillä x86_64-arkkitehtuurilla. Alle 1 kilotavun syötteillä ero on 20 prosenttia. WASM-varajärjestelmällä ero kutistuu merkittävästi.
Toimiiko BLAKE3 selainympäristössä? Kyllä. blake3-paketti sisältää WASM-sidonnan, joka toimii selaimessa. @noble/hashes toimii myös suoraan ilman WASM:a. Voit jakaa saman hajautuskoodin Node.js:n ja selaimen välillä.
Mitä tapahtuu, jos muutan kontekstia derive()-kutsussa tuotannossa? Kontekstin muuttaminen tuottaa täysin eri avaimet. Kaikki aiemmin johdetut avaimet muuttuvat, mikä tekee tallennetusta salatusta datasta purettamatonta. Kontekstimerkintä täytyy olla muuttumaton koko sovelluksen elinkaaren ajan.
Voiko BLAKE3:a käyttää satunnaistiedon korvaajana? BLAKE3 KDF sopii avainten johdannaiseen deterministisestä materiaalista, kuten DH-avaintenvaihdon tuloksesta. Se ei korvaa kryptografisesti turvallista satunnaislukugeneraattoria (CSPRNG). Käytä crypto.randomBytes(), kun avain tarvitaan täysin satunnaisena.
Miten BLAKE3 vertautuu SHA-3:een turvallisuudessa? Turvallisuusominaisuudet ovat käytännössä samat 256-bittisellä tulospiituudella: 128 bitin törmäyskestävyys molemmissa. SHA-3 perustuu Keccak-sponge-rakenteeseen, BLAKE3 Merkle-puuhun. SHA-3 on NIST-standardisoitu ja FIPS-hyväksytty, BLAKE3 on nopeampi mutta ei standardisoitu.
Onko BLAKE3:lle olemassa referenssitoteutus muille kielille? Kyllä. Viralliset toteutukset löytyvät Rustille, C:lle, Pythonille, Javalle ja Go:lle osoitteesta github.com/BLAKE3-team/BLAKE3. Yhteensopivuus on taattu: sama syöte tuottaa saman tiivisteen kaikissa virallisjulkaistuissa toteutuksissa.
Aiheeseen liittyvät artikkelit
Aiheeseen liittyvä sisältö
- SHA-256 selitettynä: SHA-2-perheen työjuhta - vertailukohde BLAKE3:n nopeusedulle
- HMAC Node.js:ssä: 10 vaihetta, 30 min - viestien autentikointi SHA-256-pohjaisella MAC:lla
- Ed25519 Node.js:ssä: 10 vaihetta, 30 min - digitaaliset allekirjoitukset elliptisillä käyrillä
- Kryptografia: tiivistefunktiot ja luottamuksen perusta verkossa - kattava johdatus kryptografian perusteisiin
- HTTPS ja TLS: miten salattu yhteys suojaa sinua - TLS-protokollan tiivistefunktioiden käyttö
Lisätietoja BLAKE3:n virallisesta spesifikaatiosta löytyy osoitteesta blake3.io ja GitHub-repositoriosta BLAKE3-team/BLAKE3. Node.js:n crypto-moduulin dokumentaatio käsittelee sisäänrakennetut hajautusalgoritmit, ja MDN Web Docs selittää hajautusfunktioiden yleisen toimintaperiaatteen.




