39 lines
3.1 KiB
TypeScript
39 lines
3.1 KiB
TypeScript
import {fault} from '../canvas-core/values.ts';
|
|
// SHA-256 compression/padding: NIST FIPS 180-4 (2015), §§4.1.2, 4.2.2, 5.1.1, 6.2.
|
|
// https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
|
|
const K=new Uint32Array([
|
|
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,
|
|
]);
|
|
const rotate=(n:number,bits:number)=>(n>>>bits)|(n<<(32-bits));
|
|
/** Incremental bytes only; owns at most a partial block, state and message schedule. */
|
|
export function createSha256(){
|
|
const state=new Uint32Array([0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19]),block=new Uint8Array(64),words=new Uint32Array(64);let pending=0,length=0,result:string|undefined;
|
|
function compress(bytes:Uint8Array,offset:number){
|
|
const view=new DataView(bytes.buffer,bytes.byteOffset+offset,64);for(let i=0;i<16;i++)words[i]=view.getUint32(i*4,false);
|
|
for(let i=16;i<64;i++){const x=words[i-15],y=words[i-2],s0=rotate(x,7)^rotate(x,18)^(x>>>3),s1=rotate(y,17)^rotate(y,19)^(y>>>10);words[i]=(words[i-16]+s0+words[i-7]+s1)>>>0;}
|
|
let[a,b,c,d,e,f,g,h]=state;
|
|
for(let i=0;i<64;i++){const s1=rotate(e,6)^rotate(e,11)^rotate(e,25),ch=(e&f)^(~e&g),t1=(h+s1+ch+K[i]+words[i])>>>0,s0=rotate(a,2)^rotate(a,13)^rotate(a,22),maj=(a&b)^(a&c)^(b&c),t2=(s0+maj)>>>0;h=g;g=f;f=e;e=(d+t1)>>>0;d=c;c=b;b=a;a=(t1+t2)>>>0;}
|
|
const values=[a,b,c,d,e,f,g,h];for(let i=0;i<8;i++)state[i]=(state[i]+values[i])>>>0;
|
|
}
|
|
return Object.freeze({
|
|
update(bytes:Uint8Array){
|
|
if(result!==undefined)throw fault('digest_finalized');if(!(bytes instanceof Uint8Array)||!Number.isSafeInteger(length+bytes.byteLength))throw fault('invalid_digest_input');length+=bytes.byteLength;let offset=0;
|
|
if(pending){const count=Math.min(64-pending,bytes.byteLength);block.set(bytes.subarray(0,count),pending);pending+=count;offset=count;if(pending===64){compress(block,0);pending=0;}}
|
|
while(offset+64<=bytes.byteLength){compress(bytes,offset);offset+=64;}
|
|
if(offset<bytes.byteLength){block.set(bytes.subarray(offset));pending=bytes.byteLength-offset;}
|
|
},
|
|
hex():string{
|
|
if(result!==undefined)return result;block[pending++]=0x80;
|
|
if(pending>56){block.fill(0,pending);compress(block,0);pending=0;}
|
|
block.fill(0,pending,56);new DataView(block.buffer).setBigUint64(56,BigInt(length)*8n,false);compress(block,0);result=[...state].map(word=>word.toString(16).padStart(8,'0')).join('');block.fill(0);words.fill(0);state.fill(0);return result;
|
|
},
|
|
});
|
|
}
|