salty-crypto 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -0
- package/dist/salty-crypto.js +1 -0
- package/package.json +39 -0
- package/rollup.config.js +9 -0
- package/src/aead.ts +86 -0
- package/src/blake2.ts +135 -0
- package/src/chacha20.ts +72 -0
- package/src/index.ts +12 -0
- package/src/noise.ts +368 -0
- package/src/patterns.ts +59 -0
- package/src/poly1305.ts +369 -0
- package/src/profiles.ts +65 -0
- package/src/random.ts +36 -0
- package/src/x25519.ts +596 -0
- package/test/harness.ts +73 -0
- package/test/tests/aead.test.ts +49 -0
- package/test/tests/blake2.test.ts +41 -0
- package/test/tests/chacha20.test.ts +81 -0
- package/test/tests/noise.test.ts +144 -0
- package/test/tests/poly1305.test.ts +16 -0
- package/test-vectors/noise-c-basic.txt +19684 -0
- package/test-vectors/snow.txt +10348 -0
- package/tsconfig.json +18 -0
package/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# typescript-salty-crypto
|
|
2
|
+
|
|
3
|
+
A TypeScript implementation of the [Noise Protocol Framework](https://noiseprotocol.org/),
|
|
4
|
+
intended to be runnable both in the browser and server-side. Also includes just enough minimal
|
|
5
|
+
crypto code (partly from [tweetnacl.js](https://github.com/dchest/tweetnacl-js), partly code I
|
|
6
|
+
wrote [myself](https://leastfixedpoint.com/) from the RFCs) to get
|
|
7
|
+
`Noise_*_25519_ChaChaPoly_BLAKE2s` working.
|
|
8
|
+
|
|
9
|
+
Includes (and passes) test vectors from [noise-c](https://github.com/rweather/noise-c/) and
|
|
10
|
+
[snow](https://github.com/mcginty/snow/).
|
|
11
|
+
|
|
12
|
+
## Code overview
|
|
13
|
+
|
|
14
|
+
- `src` directory:
|
|
15
|
+
- [`aead.ts`](src/aead.ts): RFC-8439 ("IETF") ChaCha20-Poly1305 AEAD construction
|
|
16
|
+
- [`blake2.ts`](src/blake2.ts): RFC-7693 BLAKE2s hash function
|
|
17
|
+
- [`chacha20.ts`](src/chacha20.ts): RFC-8439 ("IETF") ChaCha20 cipher
|
|
18
|
+
- [`noise.ts`](src/noise.ts): Core Noise Protocol Framework handshake and CipherState
|
|
19
|
+
implementation
|
|
20
|
+
- [`patterns.ts`](src/patterns.ts): Library of Noise handshake patterns
|
|
21
|
+
- [`poly1305.ts`](src/poly1305.ts): Port of [the Poly1305 MAC implementation from
|
|
22
|
+
tweetnacl.js](https://github.com/dchest/tweetnacl-js/blob/6a9594a35a27f9c723c5f1c107e376d1c65c23b3/nacl-fast.js#L462-L817),
|
|
23
|
+
which in turn ported [Andrew Moon's Poly1305-donna-16
|
|
24
|
+
code](https://github.com/floodyberry/poly1305-donna/blob/e6ad6e091d30d7f4ec2d4f978be1fcfcbce72781/poly1305-donna-16.h).
|
|
25
|
+
- [`profiles.ts`](src/profiles.ts): Profiles of the Noise Protocol Framework. Currently
|
|
26
|
+
just `Noise_25519_ChaChaPoly_BLAKE2s`.
|
|
27
|
+
- [`random.ts`](src/random.ts): Port of [the randomness-generation code from
|
|
28
|
+
tweetnacl.js](https://github.com/dchest/tweetnacl-js/blob/6a9594a35a27f9c723c5f1c107e376d1c65c23b3/nacl-fast.js#L2363-L2389).
|
|
29
|
+
- [`x25519.ts`](src/x25519.ts): Port of [the X25519 key agreement implementation from
|
|
30
|
+
tweetnacl.js](https://github.com/dchest/tweetnacl-js/blob/6a9594a35a27f9c723c5f1c107e376d1c65c23b3/nacl-fast.js#L852-L1379).
|
|
31
|
+
- `test-vectors` directory: Contains Noise test vectors (more-or-less in the [standard JSON
|
|
32
|
+
format](https://github.com/noiseprotocol/noise_wiki/wiki/Test-vectors)) copied from other
|
|
33
|
+
projects.
|
|
34
|
+
- `test` directory: Contains a test driver and test code.
|
|
35
|
+
|
|
36
|
+
## Copyright and License
|
|
37
|
+
|
|
38
|
+
These libraries are Copyright © 2023 Tony Garnock-Jones `<tonyg@leastfixedpoint.com>`.
|
|
39
|
+
|
|
40
|
+
They are made available to you under the [MIT license](https://spdx.org/licenses/MIT.html).
|
|
41
|
+
|
|
42
|
+
MIT License
|
|
43
|
+
|
|
44
|
+
Copyright (c) 2023 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
|
45
|
+
|
|
46
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
47
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
48
|
+
in the Software without restriction, including without limitation the rights
|
|
49
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
50
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
51
|
+
furnished to do so, subject to the following conditions:
|
|
52
|
+
|
|
53
|
+
The above copyright notice and this permission notice (including the next
|
|
54
|
+
paragraph) shall be included in all copies or substantial portions of the
|
|
55
|
+
Software.
|
|
56
|
+
|
|
57
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
58
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
59
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
60
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
61
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
62
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
63
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).SaltyCrypto={})}(this,(function(t){"use strict";function e(t,e){return t<<e|t>>>32-e}function s(t,s,i,h,r){t[s]+=t[i],t[r]^=t[s],t[r]=e(t[r],16),t[h]+=t[r],t[i]^=t[h],t[i]=e(t[i],12),t[s]+=t[i],t[r]^=t[s],t[r]=e(t[r],8),t[h]+=t[r],t[i]^=t[h],t[i]=e(t[i],7)}function i(t,e,s,i){t[0]+=1634760805,t[1]+=857760878,t[2]+=2036477234,t[3]+=1797285236,t[4]+=e.getUint32(0,!0),t[5]+=e.getUint32(4,!0),t[6]+=e.getUint32(8,!0),t[7]+=e.getUint32(12,!0),t[8]+=e.getUint32(16,!0),t[9]+=e.getUint32(20,!0),t[10]+=e.getUint32(24,!0),t[11]+=e.getUint32(28,!0),t[12]+=s,t[13]+=i.getUint32(0,!0),t[14]+=i.getUint32(4,!0),t[15]+=i.getUint32(8,!0)}function h(t,e,h){const r=new Uint32Array(16);i(r,t,e,h);for(let t=0;t<20;t+=2)s(r,0,4,8,12),s(r,1,5,9,13),s(r,2,6,10,14),s(r,3,7,11,15),s(r,0,5,10,15),s(r,1,6,11,12),s(r,2,7,8,13),s(r,3,4,9,14);return i(r,t,e,h),r}function r(t,e,s,i,r=0,n=s.byteLength){const a=n>>6,o=63&n;for(let n=0;n<a;n++){const a=h(t,r+n,e);for(let t=0;t<64;t++)i[(n<<6)+t]=s[(n<<6)+t]^a[t>>2]>>((3&t)<<3)}if(0!==o){const n=h(t,r+a,e);for(let t=0;t<o;t++)i[(a<<6)+t]=s[(a<<6)+t]^n[t>>2]>>((3&t)<<3)}}var n=Object.freeze({__proto__:null,CHACHA20_BLOCKBYTES:64,CHACHA20_KEYBYTES:32,CHACHA20_NONCEBYTES:12,chacha20:r,chacha20_block:h,chacha20_quarter_round:s});class a{static digest(t,e){const s=new a(t);s.update(e,0,e.byteLength);const i=new Uint8Array(a.TAGBYTES);return s.finish(i,0),i}constructor(t){this.key=t,this.buffer=new Uint8Array(16),this.r=new Uint16Array(10),this.h=new Uint16Array(10),this.pad=new Uint16Array(8),this.leftover=0,this.fin=0;const e=255&t[0]|(255&t[1])<<8;this.r[0]=8191&e;const s=255&t[2]|(255&t[3])<<8;this.r[1]=8191&(e>>>13|s<<3);const i=255&t[4]|(255&t[5])<<8;this.r[2]=7939&(s>>>10|i<<6);const h=255&t[6]|(255&t[7])<<8;this.r[3]=8191&(i>>>7|h<<9);const r=255&t[8]|(255&t[9])<<8;this.r[4]=255&(h>>>4|r<<12),this.r[5]=r>>>1&8190;const n=255&t[10]|(255&t[11])<<8;this.r[6]=8191&(r>>>14|n<<2);const a=255&t[12]|(255&t[13])<<8;this.r[7]=8065&(n>>>11|a<<5);const o=255&t[14]|(255&t[15])<<8;this.r[8]=8191&(a>>>8|o<<8),this.r[9]=o>>>5&127,this.pad[0]=255&t[16]|(255&t[17])<<8,this.pad[1]=255&t[18]|(255&t[19])<<8,this.pad[2]=255&t[20]|(255&t[21])<<8,this.pad[3]=255&t[22]|(255&t[23])<<8,this.pad[4]=255&t[24]|(255&t[25])<<8,this.pad[5]=255&t[26]|(255&t[27])<<8,this.pad[6]=255&t[28]|(255&t[29])<<8,this.pad[7]=255&t[30]|(255&t[31])<<8}blocks(t,e,s){const i=this.fin?0:2048;let h=this.h[0],r=this.h[1],n=this.h[2],a=this.h[3],o=this.h[4],l=this.h[5],c=this.h[6],f=this.h[7],u=this.h[8],y=this.h[9],p=this.r[0],d=this.r[1],m=this.r[2],g=this.r[3],b=this.r[4],K=this.r[5],w=this.r[6],_=this.r[7],A=this.r[8],E=this.r[9];for(;s>=16;){const U=255&t[e+0]|(255&t[e+1])<<8;h+=8191&U;const v=255&t[e+2]|(255&t[e+3])<<8;r+=8191&(U>>>13|v<<3);const M=255&t[e+4]|(255&t[e+5])<<8;n+=8191&(v>>>10|M<<6);const S=255&t[e+6]|(255&t[e+7])<<8;a+=8191&(M>>>7|S<<9);const k=255&t[e+8]|(255&t[e+9])<<8;o+=8191&(S>>>4|k<<12),l+=k>>>1&8191;const L=255&t[e+10]|(255&t[e+11])<<8;c+=8191&(k>>>14|L<<2);const H=255&t[e+12]|(255&t[e+13])<<8;f+=8191&(L>>>11|H<<5);const N=255&t[e+14]|(255&t[e+15])<<8;u+=8191&(H>>>8|N<<8),y+=N>>>5|i;let x=0,P=x;P+=h*p,P+=r*(5*E),P+=n*(5*A),P+=a*(5*_),P+=o*(5*w),x=P>>>13,P&=8191,P+=l*(5*K),P+=c*(5*b),P+=f*(5*g),P+=u*(5*m),P+=y*(5*d),x+=P>>>13,P&=8191;let B=x;B+=h*d,B+=r*p,B+=n*(5*E),B+=a*(5*A),B+=o*(5*_),x=B>>>13,B&=8191,B+=l*(5*w),B+=c*(5*K),B+=f*(5*b),B+=u*(5*g),B+=y*(5*m),x+=B>>>13,B&=8191;let T=x;T+=h*m,T+=r*d,T+=n*p,T+=a*(5*E),T+=o*(5*A),x=T>>>13,T&=8191,T+=l*(5*_),T+=c*(5*w),T+=f*(5*K),T+=u*(5*b),T+=y*(5*g),x+=T>>>13,T&=8191;let C=x;C+=h*g,C+=r*m,C+=n*d,C+=a*p,C+=o*(5*E),x=C>>>13,C&=8191,C+=l*(5*A),C+=c*(5*_),C+=f*(5*w),C+=u*(5*K),C+=y*(5*b),x+=C>>>13,C&=8191;let O=x;O+=h*b,O+=r*g,O+=n*m,O+=a*d,O+=o*p,x=O>>>13,O&=8191,O+=l*(5*E),O+=c*(5*A),O+=f*(5*_),O+=u*(5*w),O+=y*(5*K),x+=O>>>13,O&=8191;let Y=x;Y+=h*K,Y+=r*b,Y+=n*g,Y+=a*m,Y+=o*d,x=Y>>>13,Y&=8191,Y+=l*p,Y+=c*(5*E),Y+=f*(5*A),Y+=u*(5*_),Y+=y*(5*w),x+=Y>>>13,Y&=8191;let I=x;I+=h*w,I+=r*K,I+=n*b,I+=a*g,I+=o*m,x=I>>>13,I&=8191,I+=l*d,I+=c*p,I+=f*(5*E),I+=u*(5*A),I+=y*(5*_),x+=I>>>13,I&=8191;let z=x;z+=h*_,z+=r*w,z+=n*K,z+=a*b,z+=o*g,x=z>>>13,z&=8191,z+=l*m,z+=c*d,z+=f*p,z+=u*(5*E),z+=y*(5*A),x+=z>>>13,z&=8191;let X=x;X+=h*A,X+=r*_,X+=n*w,X+=a*K,X+=o*b,x=X>>>13,X&=8191,X+=l*g,X+=c*m,X+=f*d,X+=u*p,X+=y*(5*E),x+=X>>>13,X&=8191;let j=x;j+=h*E,j+=r*A,j+=n*_,j+=a*w,j+=o*K,x=j>>>13,j&=8191,j+=l*b,j+=c*g,j+=f*m,j+=u*d,j+=y*p,x+=j>>>13,j&=8191,x=(x<<2)+x|0,x=x+P|0,P=8191&x,x>>>=13,B+=x,h=P,r=B,n=T,a=C,o=O,l=Y,c=I,f=z,u=X,y=j,e+=16,s-=16}this.h[0]=h,this.h[1]=r,this.h[2]=n,this.h[3]=a,this.h[4]=o,this.h[5]=l,this.h[6]=c,this.h[7]=f,this.h[8]=u,this.h[9]=y}finish(t,e){if(this.leftover){let t=this.leftover;for(this.buffer[t++]=1;t<16;t++)this.buffer[t]=0;this.fin=1,this.blocks(this.buffer,0,16)}let s=this.h[1]>>>13;this.h[1]&=8191;for(let t=2;t<10;t++)this.h[t]+=s,s=this.h[t]>>>13,this.h[t]&=8191;this.h[0]+=5*s,s=this.h[0]>>>13,this.h[0]&=8191,this.h[1]+=s,s=this.h[1]>>>13,this.h[1]&=8191,this.h[2]+=s;const i=new Uint16Array(10);i[0]=this.h[0]+5,s=i[0]>>>13,i[0]&=8191;for(let t=1;t<10;t++)i[t]=this.h[t]+s,s=i[t]>>>13,i[t]&=8191;i[9]-=8192;let h=(1^s)-1;for(let t=0;t<10;t++)i[t]&=h;h=~h;for(let t=0;t<10;t++)this.h[t]=this.h[t]&h|i[t];this.h[0]=65535&(this.h[0]|this.h[1]<<13),this.h[1]=65535&(this.h[1]>>>3|this.h[2]<<10),this.h[2]=65535&(this.h[2]>>>6|this.h[3]<<7),this.h[3]=65535&(this.h[3]>>>9|this.h[4]<<4),this.h[4]=65535&(this.h[4]>>>12|this.h[5]<<1|this.h[6]<<14),this.h[5]=65535&(this.h[6]>>>2|this.h[7]<<11),this.h[6]=65535&(this.h[7]>>>5|this.h[8]<<8),this.h[7]=65535&(this.h[8]>>>8|this.h[9]<<5);let r=this.h[0]+this.pad[0];this.h[0]=65535&r;for(let t=1;t<8;t++)r=(this.h[t]+this.pad[t]|0)+(r>>>16)|0,this.h[t]=65535&r;t[e+0]=this.h[0]>>>0&255,t[e+1]=this.h[0]>>>8&255,t[e+2]=this.h[1]>>>0&255,t[e+3]=this.h[1]>>>8&255,t[e+4]=this.h[2]>>>0&255,t[e+5]=this.h[2]>>>8&255,t[e+6]=this.h[3]>>>0&255,t[e+7]=this.h[3]>>>8&255,t[e+8]=this.h[4]>>>0&255,t[e+9]=this.h[4]>>>8&255,t[e+10]=this.h[5]>>>0&255,t[e+11]=this.h[5]>>>8&255,t[e+12]=this.h[6]>>>0&255,t[e+13]=this.h[6]>>>8&255,t[e+14]=this.h[7]>>>0&255,t[e+15]=this.h[7]>>>8&255}update(t,e,s){if(this.leftover){let i=16-this.leftover;i>s&&(i=s);for(let s=0;s<i;s++)this.buffer[this.leftover+s]=t[e+s];if(s-=i,e+=i,this.leftover+=i,this.leftover<16)return;this.blocks(this.buffer,0,16),this.leftover=0}if(s>=16){const i=s-s%16;this.blocks(t,e,i),e+=i,s-=i}if(s){for(let i=0;i<s;i++)this.buffer[this.leftover+i]=t[e+i];this.leftover+=s}}}a.KEYBYTES=32,a.TAGBYTES=16,a.BLOCKBYTES=16;var o=Object.freeze({__proto__:null,Poly1305:a});const l=new Uint8Array(16);function c(t,e){const s=15&e;0!==s&&t.update(l,0,16-s)}function f(t,e,s,i,h,n){const o=new Uint8Array(a.KEYBYTES);r(e,s,o,o,0);const l=new a(o);void 0!==n&&(l.update(n,0,n.byteLength),c(l,n.byteLength)),l.update(i,0,h),c(l,h);const f=new Uint8Array(16),u=new DataView(f.buffer);void 0!==n&&u.setUint32(0,n.byteLength,!0),u.setUint32(8,h,!0),l.update(f,0,f.byteLength),l.finish(t,0)}function u(t,e,s,i,h,n,a){r(h,n,t,e,1,s),f(i,h,n,e,s,a)}function y(t,e,s,i,h,n,a){const o=new Uint8Array(16);f(o,h,n,e,s,a);const l=0===function(t,e,s){let i=0;for(let h=0;h<s;h++)i|=t[h]^e[h];return(1&i-1>>>8)-1}(o,i,o.byteLength);return l?r(h,n,e,t,1,s):t.fill(0,0,s),l}var p=Object.freeze({__proto__:null,AEAD_CHACHA20_POLY1305_KEYBYTES:32,AEAD_CHACHA20_POLY1305_NONCEBYTES:12,AEAD_CHACHA20_POLY1305_TAGBYTES:16,aead_decrypt_detached:y,aead_encrypt_detached:u});function d(t,e){return t>>>e|t<<32-e}function m(t,e,s,i,h,r,n){t[e]=t[e]+t[s]+r,t[h]=d(t[h]^t[e],16),t[i]=t[i]+t[h],t[s]=d(t[s]^t[i],12),t[e]=t[e]+t[s]+n,t[h]=d(t[h]^t[e],8),t[i]=t[i]+t[h],t[s]=d(t[s]^t[i],7)}const g=Uint32Array.from([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]),b=Uint8Array.from([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,14,10,4,8,9,15,13,6,1,12,0,2,11,7,5,3,11,8,12,0,5,2,15,13,10,14,3,6,7,1,9,4,7,9,3,1,13,12,11,14,2,6,5,10,4,0,15,8,9,0,5,7,2,4,10,15,14,1,11,12,6,8,3,13,2,12,6,10,0,11,8,3,4,13,7,5,15,14,1,9,12,5,1,15,14,13,4,10,0,7,6,3,9,2,8,11,13,11,7,14,12,1,3,9,5,0,15,4,8,6,2,10,6,15,14,9,11,3,0,8,12,2,13,7,1,4,10,5,10,2,8,4,7,6,1,5,15,11,9,14,3,12,13,0]);function K(t,e){return b[(t<<4)+e]}class w{static digest(t,e,s){const i=new w(e,s);return i.update(t),i.final()}constructor(t=w.OUTBYTES,e){var s;this.outlen=t,this.b=new Uint8Array(64),this.bv=new DataView(this.b.buffer),this.h=Uint32Array.from(g),this.t=new Uint32Array(2),this.c=0;const i=null!==(s=null==e?void 0:e.byteLength)&&void 0!==s?s:0;if(0==t||t>32||i>32)throw new Error("illegal BLAKE2s parameter length(s)");this.h[0]^=16842752^i<<8^t,void 0!==e&&i>0&&(this.update(e),this.c=64)}update(t){for(let e=0;e<t.byteLength;e++)64==this.c&&(this.t[0]+=this.c,this.t[0]<this.c&&this.t[1]++,this.compress(!1),this.c=0),this.b[this.c++]=t[e]}final(t){for(this.t[0]+=this.c,this.t[0]<this.c&&this.t[1]++;this.c<64;)this.b[this.c++]=0;this.compress(!0),void 0===t&&(t=new Uint8Array(this.outlen));for(let e=0;e<this.outlen;e++)t[e]=this.h[e>>2]>>8*(3&e)&255;return t}compress(t){const e=new Uint32Array(16),s=new Uint32Array(16);for(let t=0;t<8;t++)e[t]=this.h[t],e[t+8]=g[t];e[12]^=this.t[0],e[13]^=this.t[1],t&&(e[14]=~e[14]);for(let t=0;t<16;t++)s[t]=this.bv.getUint32(t<<2,!0);for(let t=0;t<10;t++)m(e,0,4,8,12,s[K(t,0)],s[K(t,1)]),m(e,1,5,9,13,s[K(t,2)],s[K(t,3)]),m(e,2,6,10,14,s[K(t,4)],s[K(t,5)]),m(e,3,7,11,15,s[K(t,6)],s[K(t,7)]),m(e,0,5,10,15,s[K(t,8)],s[K(t,9)]),m(e,1,6,11,12,s[K(t,10)],s[K(t,11)]),m(e,2,7,8,13,s[K(t,12)],s[K(t,13)]),m(e,3,4,9,14,s[K(t,14)],s[K(t,15)]);for(let t=0;t<8;t++)this.h[t]^=e[t]^e[t+8]}}w.KEYBYTES=32,w.OUTBYTES=32,w.BLOCKLEN=64;var _=Object.freeze({__proto__:null,BLAKE2s:w});class A{constructor(t=0,e=0){this.lo=t,this.hi=e}increment(){const t=this.lo,e=t+1|0;this.lo=e,e<t&&(this.hi=this.hi+1|0)}reset(t=0,e=0){this.lo=t,this.hi=e}static get MAX(){return new A(4294967295,4294967295)}}function E(t,e){const s=Math.min(t.byteLength,e.byteLength),i=new Uint8Array(s);for(let h=0;h<s;h++)i[h]=t[h]^e[h];return i}function U(t,e){const s=new Uint8Array(t.byteLength+e.byteLength);return s.set(t,0),s.set(e,t.byteLength),s}const v=new Uint8Array(0);class M{constructor(t){const e=this.generateKeypair();this.dhlen=this.dh(e,e.public).byteLength,this.hmac=null!=t?t:function(t){const e=new Uint8Array(t.hashBlocklen());e.fill(54);const s=new Uint8Array(t.hashBlocklen());return s.fill(92),(i,h)=>{const r=t._padOrHash(i,t.hashBlocklen());return t.hash(U(E(r,s),t.hash(U(E(r,e),h))))}}(this)}rekey(t){return new DataView(this.encrypt(t,A.MAX,new Uint8Array(32)).buffer)}_padOrHash(t,e){const s=t.byteLength>e?this.hash(t):t;return U(s,new Uint8Array(e-s.byteLength))}hkdf(t,e,s){const i=this.hmac(t,e),h=this.hmac(i,Uint8Array.from([1])),r=this.hmac(i,U(h,Uint8Array.from([2])));switch(s){case 2:return[h,r];case 3:return[h,r,this.hmac(i,U(r,Uint8Array.from([3])))]}}matchingPattern(t){const e=new RegExp(`^Noise_([A-Za-z0-9+]+)_${this.dhName()}_${this.cipherName()}_${this.hashName()}$`).exec(t);return null===e?null:e[1]}}class S{constructor(t,e){this.algorithms=t,this.view=null,this.nonce=new A,void 0!==e&&(this.view=new DataView(e.buffer))}encrypt(t,e){if(null===this.view)return t;const s=this.algorithms.encrypt(this.view,this.nonce,t,e);return this.nonce.increment(),s}decrypt(t,e){if(null===this.view)return t;const s=this.algorithms.decrypt(this.view,this.nonce,t,e);return this.nonce.increment(),s}rekey(){null!==this.view&&(this.view=this.algorithms.rekey(this.view))}}var k=Object.freeze({__proto__:null,CipherState:S,NoiseHandshake:class{constructor(t,e,s,i={}){var h,r,n,a,o;this.algorithms=t,this.pattern=e,this.role=s,this.stepIndex=0,this.staticKeypair=null!==(h=i.staticKeypair)&&void 0!==h?h:this.algorithms.generateKeypair(),this.remoteStaticPublicKey=null!==(r=i.remoteStaticPublicKey)&&void 0!==r?r:null,this.ephemeralKeypair=null!==(n=i.pregeneratedEphemeralKeypair)&&void 0!==n?n:this.algorithms.generateKeypair(),this.remoteEphemeralPublicKey=null!==(a=i.remotePregeneratedEphemeralPublicKey)&&void 0!==a?a:null,this.preSharedKeys=i.preSharedKeys,this.preSharedKeys&&(this.preSharedKeys=this.preSharedKeys.slice(),0===this.preSharedKeys.length&&(this.preSharedKeys=void 0));const l=(new TextEncoder).encode("Noise_"+this.pattern.name+"_"+this.algorithms.dhName()+"_"+this.algorithms.cipherName()+"_"+this.algorithms.hashName());this.cipherState=new S(this.algorithms),this.chainingKey=this.algorithms._padOrHash(l,this.algorithms.hash(v).byteLength),this.handshakeHash=this.chainingKey,this.mixHash(null!==(o=i.prologue)&&void 0!==o?o:v),this.pattern.initiatorPreMessage.forEach((t=>this.mixHash("e"===t?this.isInitiator?this.ephemeralKeypair.public:this.remoteEphemeralPublicKey:this.isInitiator?this.staticKeypair.public:this.remoteStaticPublicKey))),this.pattern.responderPreMessage.forEach((t=>this.mixHash("e"===t?this.isInitiator?this.remoteEphemeralPublicKey:this.ephemeralKeypair.public:this.isInitiator?this.remoteStaticPublicKey:this.staticKeypair.public)))}get isInitiator(){return"initiator"===this.role}mixHash(t){this.handshakeHash=this.algorithms.hash(U(this.handshakeHash,t))}mixKey(t){const[e,s]=this.algorithms.hkdf(this.chainingKey,t,2);this.chainingKey=e,this.cipherState=new S(this.algorithms,s)}mixKeyAndHashNextPSK(){const t=this.preSharedKeys.shift(),[e,s,i]=this.algorithms.hkdf(this.chainingKey,t,3);this.chainingKey=e,this.mixHash(s),this.cipherState=new S(this.algorithms,i)}encryptAndHash(t){const e=this.cipherState.encrypt(t,this.handshakeHash);return this.mixHash(e),e}decryptAndHash(t){const e=this.cipherState.decrypt(t,this.handshakeHash);return this.mixHash(t),e}_split(){if(this.stepIndex<this.pattern.messages.length)return null;{let[t,e]=this.algorithms.hkdf(this.chainingKey,v,2).map((t=>new S(this.algorithms,t)));return this.isInitiator?{send:t,recv:e}:{send:e,recv:t}}}_nextStep(){if(this.stepIndex>=this.pattern.messages.length)throw new Error("Handshake already complete, cannot continue");return this.pattern.messages[this.stepIndex++]}_processKeyMixToken(t){switch(t){case"ee":this.mixKey(this.algorithms.dh(this.ephemeralKeypair,this.remoteEphemeralPublicKey));break;case"es":this.mixKey(this.isInitiator?this.algorithms.dh(this.ephemeralKeypair,this.remoteStaticPublicKey):this.algorithms.dh(this.staticKeypair,this.remoteEphemeralPublicKey));break;case"se":this.mixKey(this.isInitiator?this.algorithms.dh(this.staticKeypair,this.remoteEphemeralPublicKey):this.algorithms.dh(this.ephemeralKeypair,this.remoteStaticPublicKey));break;case"ss":this.mixKey(this.algorithms.dh(this.staticKeypair,this.remoteStaticPublicKey));break;case"psk":this.mixKeyAndHashNextPSK()}}writeMessage(t){const e=[];let s;if(this._nextStep().forEach((t=>{switch(t){case"e":e.push(this.ephemeralKeypair.public),this.mixHash(this.ephemeralKeypair.public),this.preSharedKeys&&this.mixKey(this.ephemeralKeypair.public);break;case"s":e.push(this.encryptAndHash(this.staticKeypair.public));break;default:this._processKeyMixToken(t)}})),e.push(this.encryptAndHash(t)),1===e.length)s=e[0];else{s=new Uint8Array(e.reduce(((t,e)=>t+e.byteLength),0));let t=0;e.forEach((e=>{s.set(e,t),t+=e.byteLength}))}return{packet:s,finished:this._split()}}readMessage(t){const e=e=>{const s=t.slice(0,e);return t=t.subarray(e),s};this._nextStep().forEach((t=>{switch(t){case"e":this.remoteEphemeralPublicKey=e(this.algorithms.dhlen),this.mixHash(this.remoteEphemeralPublicKey),this.preSharedKeys&&this.mixKey(this.remoteEphemeralPublicKey);break;case"s":this.remoteStaticPublicKey=this.decryptAndHash(e(this.algorithms.dhlen+(this.cipherState.view?16:0)));break;default:this._processKeyMixToken(t)}}));return{message:this.decryptAndHash(t),finished:this._split()}}async completeHandshake(t,e,s=(async t=>{}),i=(async()=>new Uint8Array(0))){const h=async()=>{const{packet:e,finished:s}=this.writeMessage(await i());return await t(e),s||r()},r=async()=>{const{message:t,finished:i}=this.readMessage(await e());return await s(t),i||h()};return this.isInitiator?h():r()}},NoiseProtocolAlgorithms:M,Nonce:A,bytesAppend:U,bytesXor:E});const L={};function H(t,e,s,i){const h={name:t,baseName:t,messages:e,initiatorPreMessage:s,responderPreMessage:i};L[h.name]=h}H("N",[["e","es"]],[],["s"]),H("K",[["e","es","ss"]],["s"],["s"]),H("X",[["e","es","s","ss"]],[],["s"]),H("NN",[["e"],["e","ee"]],[],[]),H("NK",[["e","es"],["e","ee"]],[],["s"]),H("NX",[["e"],["e","ee","s","es"]],[],[]),H("KN",[["e"],["e","ee","se"]],["s"],[]),H("KK",[["e","es","ss"],["e","ee","se"]],["s"],["s"]),H("KX",[["e"],["e","ee","se","s","es"]],["s"],[]),H("XN",[["e"],["e","ee"],["s","se"]],[],[]),H("XK",[["e","es"],["e","ee"],["s","se"]],[],["s"]),H("XX",[["e"],["e","ee","s","es"],["s","se"]],[],[]),H("IN",[["e","s"],["e","ee","se"]],[],[]),H("IK",[["e","es","s","ss"],["e","ee","se"]],[],["s"]),H("IX",[["e","s"],["e","ee","se","s","es"]],[],[]);const N=/^([NKX]|[NKXI]1?[NKX]1?)([a-z][a-z0-9]*(\+[a-z][a-z0-9]*)*)?$/,x=/^psk([0-9]+)$/;var P=Object.freeze({__proto__:null,PATTERNS:L,isOneWay:function(t){return 1===t.baseName.length},lookupPattern:function(t){var e,s,i;const h=N.exec(t);if(null===h)return null;const r=null!==(s=null===(e=h[2])||void 0===e?void 0:e.split("+"))&&void 0!==s?s:[];let n=null!==(i=L[h[1]])&&void 0!==i?i:null;return n?(r.forEach((t=>n=n&&function(t,e){const s=x.exec(e);if(null===s)return null;const i=parseInt(s[1],10),h=t.messages;return Object.assign(Object.assign({},t),{messages:0===i?[["psk",...h[0]],...h.slice(1)]:[...h.slice(0,i-1),[...h[i-1],"psk"],...h.slice(i)]})}(n,t))),n&&Object.assign(Object.assign({},n),{name:t})):null}});const B=(()=>{var t="undefined"!=typeof self?self.crypto||self.msCrypto:null;if(t&&t.getRandomValues){const e=65536;return(s,i)=>{for(let h=0;h<i;h+=e)t.getRandomValues(s.subarray(h,h+Math.min(i-h,e)))}}if("undefined"!=typeof require&&(t=require("crypto"))&&t.randomBytes)return(e,s)=>e.set(t.randomBytes(s));throw new Error("No usable randomness source found")})();function T(t){const e=new Uint8Array(t);return B(e,t),e}var C=Object.freeze({__proto__:null,_randomBytes:B,randomBytes:T});function O(){return new Float64Array(16)}const Y=new Uint8Array(32);Y[0]=9;const I=O();function z(t){let e=1;for(let s=0;s<16;s++){const i=t[s]+e+65535;e=Math.floor(i/65536),t[s]=i-65536*e}t[0]+=e-1+37*(e-1)}function X(t,e,s){const i=~(s-1);for(let s=0;s<16;s++){const h=i&(t[s]^e[s]);t[s]^=h,e[s]^=h}}function j(t,e,s){for(let i=0;i<16;i++)t[i]=e[i]+s[i]}function D(t,e,s){for(let i=0;i<16;i++)t[i]=e[i]-s[i]}function V(t,e,s){let i=0,h=0,r=0,n=0,a=0,o=0,l=0,c=0,f=0,u=0,y=0,p=0,d=0,m=0,g=0,b=0,K=0,w=0,_=0,A=0,E=0,U=0,v=0,M=0,S=0,k=0,L=0,H=0,N=0,x=0,P=0;const B=s[0],T=s[1],C=s[2],O=s[3],Y=s[4],I=s[5],z=s[6],X=s[7],j=s[8],D=s[9],V=s[10],R=s[11],$=s[12],q=s[13],G=s[14],F=s[15];let W=e[0];i+=W*B,h+=W*T,r+=W*C,n+=W*O,a+=W*Y,o+=W*I,l+=W*z,c+=W*X,f+=W*j,u+=W*D,y+=W*V,p+=W*R,d+=W*$,m+=W*q,g+=W*G,b+=W*F,W=e[1],h+=W*B,r+=W*T,n+=W*C,a+=W*O,o+=W*Y,l+=W*I,c+=W*z,f+=W*X,u+=W*j,y+=W*D,p+=W*V,d+=W*R,m+=W*$,g+=W*q,b+=W*G,K+=W*F,W=e[2],r+=W*B,n+=W*T,a+=W*C,o+=W*O,l+=W*Y,c+=W*I,f+=W*z,u+=W*X,y+=W*j,p+=W*D,d+=W*V,m+=W*R,g+=W*$,b+=W*q,K+=W*G,w+=W*F,W=e[3],n+=W*B,a+=W*T,o+=W*C,l+=W*O,c+=W*Y,f+=W*I,u+=W*z,y+=W*X,p+=W*j,d+=W*D,m+=W*V,g+=W*R,b+=W*$,K+=W*q,w+=W*G,_+=W*F,W=e[4],a+=W*B,o+=W*T,l+=W*C,c+=W*O,f+=W*Y,u+=W*I,y+=W*z,p+=W*X,d+=W*j,m+=W*D,g+=W*V,b+=W*R,K+=W*$,w+=W*q,_+=W*G,A+=W*F,W=e[5],o+=W*B,l+=W*T,c+=W*C,f+=W*O,u+=W*Y,y+=W*I,p+=W*z,d+=W*X,m+=W*j,g+=W*D,b+=W*V,K+=W*R,w+=W*$,_+=W*q,A+=W*G,E+=W*F,W=e[6],l+=W*B,c+=W*T,f+=W*C,u+=W*O,y+=W*Y,p+=W*I,d+=W*z,m+=W*X,g+=W*j,b+=W*D,K+=W*V,w+=W*R,_+=W*$,A+=W*q,E+=W*G,U+=W*F,W=e[7],c+=W*B,f+=W*T,u+=W*C,y+=W*O,p+=W*Y,d+=W*I,m+=W*z,g+=W*X,b+=W*j,K+=W*D,w+=W*V,_+=W*R,A+=W*$,E+=W*q,U+=W*G,v+=W*F,W=e[8],f+=W*B,u+=W*T,y+=W*C,p+=W*O,d+=W*Y,m+=W*I,g+=W*z,b+=W*X,K+=W*j,w+=W*D,_+=W*V,A+=W*R,E+=W*$,U+=W*q,v+=W*G,M+=W*F,W=e[9],u+=W*B,y+=W*T,p+=W*C,d+=W*O,m+=W*Y,g+=W*I,b+=W*z,K+=W*X,w+=W*j,_+=W*D,A+=W*V,E+=W*R,U+=W*$,v+=W*q,M+=W*G,S+=W*F,W=e[10],y+=W*B,p+=W*T,d+=W*C,m+=W*O,g+=W*Y,b+=W*I,K+=W*z,w+=W*X,_+=W*j,A+=W*D,E+=W*V,U+=W*R,v+=W*$,M+=W*q,S+=W*G,k+=W*F,W=e[11],p+=W*B,d+=W*T,m+=W*C,g+=W*O,b+=W*Y,K+=W*I,w+=W*z,_+=W*X,A+=W*j,E+=W*D,U+=W*V,v+=W*R,M+=W*$,S+=W*q,k+=W*G,L+=W*F,W=e[12],d+=W*B,m+=W*T,g+=W*C,b+=W*O,K+=W*Y,w+=W*I,_+=W*z,A+=W*X,E+=W*j,U+=W*D,v+=W*V,M+=W*R,S+=W*$,k+=W*q,L+=W*G,H+=W*F,W=e[13],m+=W*B,g+=W*T,b+=W*C,K+=W*O,w+=W*Y,_+=W*I,A+=W*z,E+=W*X,U+=W*j,v+=W*D,M+=W*V,S+=W*R,k+=W*$,L+=W*q,H+=W*G,N+=W*F,W=e[14],g+=W*B,b+=W*T,K+=W*C,w+=W*O,_+=W*Y,A+=W*I,E+=W*z,U+=W*X,v+=W*j,M+=W*D,S+=W*V,k+=W*R,L+=W*$,H+=W*q,N+=W*G,x+=W*F,W=e[15],b+=W*B,K+=W*T,w+=W*C,_+=W*O,A+=W*Y,E+=W*I,U+=W*z,v+=W*X,M+=W*j,S+=W*D,k+=W*V,L+=W*R,H+=W*$,N+=W*q,x+=W*G,P+=W*F,i+=38*K,h+=38*w,r+=38*_,n+=38*A,a+=38*E,o+=38*U,l+=38*v,c+=38*M,f+=38*S,u+=38*k,y+=38*L,p+=38*H,d+=38*N,m+=38*x,g+=38*P;let Z=1;W=i+Z+65535,Z=Math.floor(W/65536),i=W-65536*Z,W=h+Z+65535,Z=Math.floor(W/65536),h=W-65536*Z,W=r+Z+65535,Z=Math.floor(W/65536),r=W-65536*Z,W=n+Z+65535,Z=Math.floor(W/65536),n=W-65536*Z,W=a+Z+65535,Z=Math.floor(W/65536),a=W-65536*Z,W=o+Z+65535,Z=Math.floor(W/65536),o=W-65536*Z,W=l+Z+65535,Z=Math.floor(W/65536),l=W-65536*Z,W=c+Z+65535,Z=Math.floor(W/65536),c=W-65536*Z,W=f+Z+65535,Z=Math.floor(W/65536),f=W-65536*Z,W=u+Z+65535,Z=Math.floor(W/65536),u=W-65536*Z,W=y+Z+65535,Z=Math.floor(W/65536),y=W-65536*Z,W=p+Z+65535,Z=Math.floor(W/65536),p=W-65536*Z,W=d+Z+65535,Z=Math.floor(W/65536),d=W-65536*Z,W=m+Z+65535,Z=Math.floor(W/65536),m=W-65536*Z,W=g+Z+65535,Z=Math.floor(W/65536),g=W-65536*Z,W=b+Z+65535,Z=Math.floor(W/65536),b=W-65536*Z,i+=Z-1+37*(Z-1),Z=1,W=i+Z+65535,Z=Math.floor(W/65536),i=W-65536*Z,W=h+Z+65535,Z=Math.floor(W/65536),h=W-65536*Z,W=r+Z+65535,Z=Math.floor(W/65536),r=W-65536*Z,W=n+Z+65535,Z=Math.floor(W/65536),n=W-65536*Z,W=a+Z+65535,Z=Math.floor(W/65536),a=W-65536*Z,W=o+Z+65535,Z=Math.floor(W/65536),o=W-65536*Z,W=l+Z+65535,Z=Math.floor(W/65536),l=W-65536*Z,W=c+Z+65535,Z=Math.floor(W/65536),c=W-65536*Z,W=f+Z+65535,Z=Math.floor(W/65536),f=W-65536*Z,W=u+Z+65535,Z=Math.floor(W/65536),u=W-65536*Z,W=y+Z+65535,Z=Math.floor(W/65536),y=W-65536*Z,W=p+Z+65535,Z=Math.floor(W/65536),p=W-65536*Z,W=d+Z+65535,Z=Math.floor(W/65536),d=W-65536*Z,W=m+Z+65535,Z=Math.floor(W/65536),m=W-65536*Z,W=g+Z+65535,Z=Math.floor(W/65536),g=W-65536*Z,W=b+Z+65535,Z=Math.floor(W/65536),b=W-65536*Z,i+=Z-1+37*(Z-1),t[0]=i,t[1]=h,t[2]=r,t[3]=n,t[4]=a,t[5]=o,t[6]=l,t[7]=c,t[8]=f,t[9]=u,t[10]=y,t[11]=p,t[12]=d,t[13]=m,t[14]=g,t[15]=b}function R(t,e){V(t,e,e)}function $(t,e,s){const i=new Uint8Array(32),h=new Float64Array(80),r=O(),n=O(),a=O(),o=O(),l=O(),c=O();for(let t=0;t<31;t++)i[t]=e[t];i[31]=127&e[31]|64,i[0]&=248,function(t,e){for(let s=0;s<16;s++)t[s]=e[2*s]+(e[2*s+1]<<8);t[15]&=32767}(h,s);for(let t=0;t<16;t++)n[t]=h[t],o[t]=r[t]=a[t]=0;r[0]=o[0]=1;for(let t=254;t>=0;--t){const e=i[t>>>3]>>>(7&t)&1;X(r,n,e),X(a,o,e),j(l,r,a),D(r,r,a),j(a,n,o),D(n,n,o),R(o,l),R(c,r),V(r,a,r),V(a,n,l),j(l,r,a),D(r,r,a),R(n,r),D(a,o,c),V(r,a,I),j(r,r,o),V(a,a,r),V(r,o,c),V(o,n,h),R(n,l),X(r,n,e),X(a,o,e)}for(let t=0;t<16;t++)h[t+16]=r[t],h[t+32]=a[t],h[t+48]=n[t],h[t+64]=o[t];const f=h.subarray(32),u=h.subarray(16);!function(t,e){const s=O();for(let t=0;t<16;t++)s[t]=e[t];for(let t=253;t>=0;t--)R(s,s),2!==t&&4!==t&&V(s,s,e);for(let e=0;e<16;e++)t[e]=s[e]}(f,f),V(u,u,f),function(t,e){const s=O(),i=O();for(let t=0;t<16;t++)i[t]=e[t];z(i),z(i),z(i);for(let t=0;t<2;t++){s[0]=i[0]-65517;for(let t=1;t<15;t++)s[t]=i[t]-65535-(s[t-1]>>16&1),s[t-1]&=65535;s[15]=i[15]-32767-(s[14]>>16&1);const t=s[15]>>16&1;s[14]&=65535,X(i,s,1-t)}for(let e=0;e<16;e++)t[2*e]=255&i[e],t[2*e+1]=i[e]>>8}(t,u)}function q(t,e){$(t,e,Y)}function G(t,e){if(32!==t.length)throw new Error("bad n size");if(32!==e.length)throw new Error("bad p size");const s=new Uint8Array(32);return $(s,t,e),s}function F(t){if(32!==t.length)throw new Error("bad n size");const e=new Uint8Array(32);return q(e,t),e}I[0]=56129,I[1]=1,G.scalarLength=32,G.groupElementLength=32;var W=Object.freeze({__proto__:null,crypto_scalarmult:$,crypto_scalarmult_BYTES:32,crypto_scalarmult_SCALARBYTES:32,crypto_scalarmult_base:q,scalarMult:G,scalarMultBase:F});function Z(t){const e=new DataView(new ArrayBuffer(12));return e.setUint32(4,t.lo,!0),e.setUint32(8,t.hi,!0),e}var J=Object.freeze({__proto__:null,Noise_25519_ChaChaPoly_BLAKE2s:class extends M{constructor(){super()}dhName(){return"25519"}generateKeypair(){const t=T(G.scalarLength);return{public:F(t),secret:t}}dh(t,e){return G(t.secret,e)}cipherName(){return"ChaChaPoly"}encrypt(t,e,s,i){const h=new Uint8Array(s.byteLength+16);return u(s,h,s.byteLength,h.subarray(s.byteLength),t,Z(e),i),h}decrypt(t,e,s,i){const h=new Uint8Array(s.byteLength-16);if(!y(h,s,h.byteLength,s.subarray(h.byteLength),t,Z(e),i))throw new Error("packet decryption failed");return h}hashName(){return"BLAKE2s"}hash(t){return w.digest(t)}hashBlocklen(){return w.BLOCKLEN}}});t.AEAD=p,t.BLAKE2=_,t.ChaCha20=n,t.Noise=k,t.NoiseProfiles=J,t.Patterns=P,t.Poly1305=o,t.Random=C,t.X25519=W}));
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "salty-crypto",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Noise Protocol Framework, plus X25519/ChaCha20Poly1305/BLAKE2s code, for browser and node.js",
|
|
5
|
+
"author": "Tony Garnock-Jones <tonyg@leastfixedpoint.com>",
|
|
6
|
+
"homepage": "https://github.com/tonyg/typescript-salty-crypto",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"main": "dist/salty-crypto.js",
|
|
9
|
+
"module": "lib/src/index.js",
|
|
10
|
+
"types": "lib/src/index.d.ts",
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/tonyg/typescript-salty-crypto"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"prepare": "yarn compile && yarn rollup",
|
|
20
|
+
"compile": "yarn tsc",
|
|
21
|
+
"compile:watch": "yarn compile -w",
|
|
22
|
+
"rollup": "rollup -c",
|
|
23
|
+
"rollup:watch": "yarn rollup -w",
|
|
24
|
+
"test": "node -r esm lib/test/harness.js",
|
|
25
|
+
"test:watch": "inotifytest yarn test",
|
|
26
|
+
"clean": "rm -rf lib/",
|
|
27
|
+
"fixcopyright": "fixcopyright.rkt --preset-typescript --file-pattern 'src/**.ts' MIT && fixcopyright.rkt --preset-typescript --file-pattern 'test/**.ts' MIT"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@rollup/plugin-terser": "^0.4.0",
|
|
31
|
+
"@types/glob": "^8.0.1",
|
|
32
|
+
"esm": "^3.2.25",
|
|
33
|
+
"expect": "^29.4.0",
|
|
34
|
+
"glob": "^8.1.0",
|
|
35
|
+
"rollup": "^3.10.1",
|
|
36
|
+
"typescript": "4.9"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {}
|
|
39
|
+
}
|
package/rollup.config.js
ADDED
package/src/aead.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/// SPDX-License-Identifier: MIT
|
|
2
|
+
/// SPDX-FileCopyrightText: Copyright © 2023 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
|
3
|
+
|
|
4
|
+
// RFC-8439 AEAD construction.
|
|
5
|
+
|
|
6
|
+
export const AEAD_CHACHA20_POLY1305_KEYBYTES = 32;
|
|
7
|
+
export const AEAD_CHACHA20_POLY1305_NONCEBYTES = 12;
|
|
8
|
+
export const AEAD_CHACHA20_POLY1305_TAGBYTES = 16;
|
|
9
|
+
|
|
10
|
+
import { chacha20 } from './chacha20';
|
|
11
|
+
import { Poly1305 } from './poly1305';
|
|
12
|
+
|
|
13
|
+
const PADDING = new Uint8Array(16);
|
|
14
|
+
|
|
15
|
+
function pad16(p: Poly1305, unpadded_length: number) {
|
|
16
|
+
const leftover = unpadded_length & 15;
|
|
17
|
+
if (leftover !== 0) p.update(PADDING, 0, 16 - leftover);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function aead_tag(tag: Uint8Array,
|
|
21
|
+
key: DataView,
|
|
22
|
+
nonce: DataView,
|
|
23
|
+
ciphertext: Uint8Array,
|
|
24
|
+
cipherlength: number,
|
|
25
|
+
associated_data?: Uint8Array)
|
|
26
|
+
{
|
|
27
|
+
const mac_key = new Uint8Array(Poly1305.KEYBYTES);
|
|
28
|
+
chacha20(key, nonce, mac_key, mac_key, 0);
|
|
29
|
+
const p = new Poly1305(mac_key);
|
|
30
|
+
|
|
31
|
+
if (associated_data !== void 0) {
|
|
32
|
+
p.update(associated_data, 0, associated_data.byteLength);
|
|
33
|
+
pad16(p, associated_data.byteLength);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
p.update(ciphertext, 0, cipherlength);
|
|
37
|
+
pad16(p, cipherlength);
|
|
38
|
+
|
|
39
|
+
const L = new Uint8Array(16);
|
|
40
|
+
const Lv = new DataView(L.buffer);
|
|
41
|
+
if (associated_data !== void 0) {
|
|
42
|
+
Lv.setUint32(0, associated_data.byteLength, true);
|
|
43
|
+
}
|
|
44
|
+
Lv.setUint32(8, cipherlength, true);
|
|
45
|
+
p.update(L, 0, L.byteLength);
|
|
46
|
+
|
|
47
|
+
p.finish(tag, 0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function aead_encrypt_detached(plaintext: Uint8Array,
|
|
51
|
+
ciphertext: Uint8Array,
|
|
52
|
+
messagelength: number,
|
|
53
|
+
tag: Uint8Array,
|
|
54
|
+
key: DataView,
|
|
55
|
+
nonce: DataView,
|
|
56
|
+
associated_data?: Uint8Array)
|
|
57
|
+
{
|
|
58
|
+
chacha20(key, nonce, plaintext, ciphertext, 1, messagelength);
|
|
59
|
+
aead_tag(tag, key, nonce, ciphertext, messagelength, associated_data);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// `verify` from nacl-fast.js
|
|
63
|
+
function verify(x: Uint8Array, y: Uint8Array, n: number): number {
|
|
64
|
+
let d = 0;
|
|
65
|
+
for (let i = 0; i < n; i++) d |= x[i]^y[i];
|
|
66
|
+
return (1 & ((d - 1) >>> 8)) - 1;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function aead_decrypt_detached(plaintext: Uint8Array,
|
|
70
|
+
ciphertext: Uint8Array,
|
|
71
|
+
messagelength: number,
|
|
72
|
+
expected_tag: Uint8Array,
|
|
73
|
+
key: DataView,
|
|
74
|
+
nonce: DataView,
|
|
75
|
+
associated_data?: Uint8Array): boolean
|
|
76
|
+
{
|
|
77
|
+
const actual_tag = new Uint8Array(AEAD_CHACHA20_POLY1305_TAGBYTES);
|
|
78
|
+
aead_tag(actual_tag, key, nonce, ciphertext, messagelength, associated_data);
|
|
79
|
+
const ok = verify(actual_tag, expected_tag, actual_tag.byteLength) === 0;
|
|
80
|
+
if (ok) {
|
|
81
|
+
chacha20(key, nonce, ciphertext, plaintext, 1, messagelength);
|
|
82
|
+
} else {
|
|
83
|
+
plaintext.fill(0, 0, messagelength);
|
|
84
|
+
}
|
|
85
|
+
return ok;
|
|
86
|
+
}
|
package/src/blake2.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/// SPDX-License-Identifier: MIT
|
|
2
|
+
/// SPDX-FileCopyrightText: Copyright © 2023 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
|
3
|
+
|
|
4
|
+
// RFC 7693 BLAKE2s, ported from the C code therein.
|
|
5
|
+
|
|
6
|
+
function ROTR32(n: number, bits: number): number {
|
|
7
|
+
return (n >>> bits) | (n << (32 - bits));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function B2S_G(v: Uint32Array, a: number, b: number, c: number, d: number, x: number, y: number) {
|
|
11
|
+
v[a] = v[a] + v[b] + x;
|
|
12
|
+
v[d] = ROTR32(v[d] ^ v[a], 16);
|
|
13
|
+
v[c] = v[c] + v[d];
|
|
14
|
+
v[b] = ROTR32(v[b] ^ v[c], 12);
|
|
15
|
+
v[a] = v[a] + v[b] + y;
|
|
16
|
+
v[d] = ROTR32(v[d] ^ v[a], 8);
|
|
17
|
+
v[c] = v[c] + v[d];
|
|
18
|
+
v[b] = ROTR32(v[b] ^ v[c], 7);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const blake2s_iv = Uint32Array.from([
|
|
22
|
+
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
|
|
23
|
+
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19,
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
const _sigma = Uint8Array.from([
|
|
27
|
+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
|
28
|
+
14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
|
|
29
|
+
11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4,
|
|
30
|
+
7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8,
|
|
31
|
+
9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13,
|
|
32
|
+
2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9,
|
|
33
|
+
12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11,
|
|
34
|
+
13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10,
|
|
35
|
+
6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5,
|
|
36
|
+
10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0,
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
function sigma(i: number, j: number): number {
|
|
40
|
+
return _sigma[(i << 4) + j];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class BLAKE2s {
|
|
44
|
+
static readonly KEYBYTES = 32;
|
|
45
|
+
static readonly OUTBYTES = 32;
|
|
46
|
+
static readonly BLOCKLEN = 64;
|
|
47
|
+
|
|
48
|
+
b = new Uint8Array(64);
|
|
49
|
+
bv = new DataView(this.b.buffer);
|
|
50
|
+
|
|
51
|
+
h = Uint32Array.from(blake2s_iv);
|
|
52
|
+
t = new Uint32Array(2);
|
|
53
|
+
c = 0;
|
|
54
|
+
|
|
55
|
+
static digest(input: Uint8Array, outlen?: number, key?: Uint8Array): Uint8Array {
|
|
56
|
+
const p = new BLAKE2s(outlen, key);
|
|
57
|
+
p.update(input);
|
|
58
|
+
return p.final();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
constructor(public outlen: number = BLAKE2s.OUTBYTES, key?: Uint8Array)
|
|
62
|
+
{
|
|
63
|
+
const keylen = key?.byteLength ?? 0;
|
|
64
|
+
|
|
65
|
+
if (outlen == 0 || outlen > 32 || keylen > 32) {
|
|
66
|
+
throw new Error("illegal BLAKE2s parameter length(s)");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
this.h[0] ^= 0x01010000 ^ (keylen << 8) ^ outlen;
|
|
70
|
+
|
|
71
|
+
if (key !== void 0 && keylen > 0) {
|
|
72
|
+
this.update(key);
|
|
73
|
+
this.c = 64;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
update(input: Uint8Array) {
|
|
78
|
+
for (let i = 0; i < input.byteLength; i++) {
|
|
79
|
+
if (this.c == 64) {
|
|
80
|
+
this.t[0] += this.c;
|
|
81
|
+
if (this.t[0] < this.c) this.t[1]++;
|
|
82
|
+
this.compress(false);
|
|
83
|
+
this.c = 0;
|
|
84
|
+
}
|
|
85
|
+
this.b[this.c++] = input[i];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
final(output?: Uint8Array): Uint8Array {
|
|
90
|
+
this.t[0] += this.c;
|
|
91
|
+
if (this.t[0] < this.c) this.t[1]++;
|
|
92
|
+
|
|
93
|
+
while (this.c < 64) this.b[this.c++] = 0;
|
|
94
|
+
this.compress(true);
|
|
95
|
+
|
|
96
|
+
if (output === void 0) output = new Uint8Array(this.outlen);
|
|
97
|
+
for (let i = 0; i < this.outlen; i++) {
|
|
98
|
+
output[i] = (this.h[i >> 2] >> (8 * (i & 3))) & 0xFF;
|
|
99
|
+
}
|
|
100
|
+
return output;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
compress(last: boolean) {
|
|
104
|
+
const v = new Uint32Array(16);
|
|
105
|
+
const m = new Uint32Array(16);
|
|
106
|
+
|
|
107
|
+
for (let i = 0; i < 8; i++) {
|
|
108
|
+
v[i] = this.h[i];
|
|
109
|
+
v[i + 8] = blake2s_iv[i];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
v[12] ^= this.t[0];
|
|
113
|
+
v[13] ^= this.t[1];
|
|
114
|
+
if (last) v[14] = ~v[14];
|
|
115
|
+
|
|
116
|
+
for (let i = 0; i < 16; i++) {
|
|
117
|
+
m[i] = this.bv.getUint32(i << 2, true);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
for (let i = 0; i < 10; i++) {
|
|
121
|
+
B2S_G(v, 0, 4, 8, 12, m[sigma(i, 0)], m[sigma(i, 1)]);
|
|
122
|
+
B2S_G(v, 1, 5, 9, 13, m[sigma(i, 2)], m[sigma(i, 3)]);
|
|
123
|
+
B2S_G(v, 2, 6, 10, 14, m[sigma(i, 4)], m[sigma(i, 5)]);
|
|
124
|
+
B2S_G(v, 3, 7, 11, 15, m[sigma(i, 6)], m[sigma(i, 7)]);
|
|
125
|
+
B2S_G(v, 0, 5, 10, 15, m[sigma(i, 8)], m[sigma(i, 9)]);
|
|
126
|
+
B2S_G(v, 1, 6, 11, 12, m[sigma(i, 10)], m[sigma(i, 11)]);
|
|
127
|
+
B2S_G(v, 2, 7, 8, 13, m[sigma(i, 12)], m[sigma(i, 13)]);
|
|
128
|
+
B2S_G(v, 3, 4, 9, 14, m[sigma(i, 14)], m[sigma(i, 15)]);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
for (let i = 0; i < 8; i++) {
|
|
132
|
+
this.h[i] ^= v[i] ^ v[i + 8];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
package/src/chacha20.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/// SPDX-License-Identifier: MIT
|
|
2
|
+
/// SPDX-FileCopyrightText: Copyright © 2023 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
|
3
|
+
|
|
4
|
+
// RFC-8439 ChaCha20.
|
|
5
|
+
|
|
6
|
+
export const CHACHA20_KEYBYTES = 32;
|
|
7
|
+
export const CHACHA20_NONCEBYTES = 12;
|
|
8
|
+
export const CHACHA20_BLOCKBYTES = 64;
|
|
9
|
+
|
|
10
|
+
function ROTATE(n: number, bits: number): number {
|
|
11
|
+
return (n << bits) | (n >>> (32 - bits));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function chacha20_quarter_round(s: Uint32Array, a: number, b: number, c: number, d: number) {
|
|
15
|
+
s[a] += s[b]; s[d] ^= s[a]; s[d] = ROTATE(s[d], 16);
|
|
16
|
+
s[c] += s[d]; s[b] ^= s[c]; s[b] = ROTATE(s[b], 12);
|
|
17
|
+
s[a] += s[b]; s[d] ^= s[a]; s[d] = ROTATE(s[d], 8);
|
|
18
|
+
s[c] += s[d]; s[b] ^= s[c]; s[b] = ROTATE(s[b], 7);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function fill_state(state: Uint32Array, key: DataView, block: number, nonce: DataView) {
|
|
22
|
+
state[0] += 0x61707865; state[1] += 0x3320646e; state[2] += 0x79622d32; state[3] += 0x6b206574;
|
|
23
|
+
state[4] += key.getUint32(0, true); state[5] += key.getUint32(4, true);
|
|
24
|
+
state[6] += key.getUint32(8, true); state[7] += key.getUint32(12, true);
|
|
25
|
+
state[8] += key.getUint32(16, true); state[9] += key.getUint32(20, true);
|
|
26
|
+
state[10] += key.getUint32(24, true); state[11] += key.getUint32(28, true);
|
|
27
|
+
state[12] += block;
|
|
28
|
+
state[13] += nonce.getUint32(0, true);
|
|
29
|
+
state[14] += nonce.getUint32(4, true);
|
|
30
|
+
state[15] += nonce.getUint32(8, true);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function chacha20_block(key: DataView, block: number, nonce: DataView): Uint32Array {
|
|
34
|
+
const state = new Uint32Array(16);
|
|
35
|
+
fill_state(state, key, block, nonce);
|
|
36
|
+
for (let round = 0; round < 20; round += 2) {
|
|
37
|
+
chacha20_quarter_round(state, 0, 4, 8, 12);
|
|
38
|
+
chacha20_quarter_round(state, 1, 5, 9, 13);
|
|
39
|
+
chacha20_quarter_round(state, 2, 6, 10, 14);
|
|
40
|
+
chacha20_quarter_round(state, 3, 7, 11, 15);
|
|
41
|
+
chacha20_quarter_round(state, 0, 5, 10, 15);
|
|
42
|
+
chacha20_quarter_round(state, 1, 6, 11, 12);
|
|
43
|
+
chacha20_quarter_round(state, 2, 7, 8, 13);
|
|
44
|
+
chacha20_quarter_round(state, 3, 4, 9, 14);
|
|
45
|
+
}
|
|
46
|
+
fill_state(state, key, block, nonce);
|
|
47
|
+
return state;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function chacha20(
|
|
51
|
+
key: DataView,
|
|
52
|
+
nonce: DataView,
|
|
53
|
+
input: Uint8Array,
|
|
54
|
+
output: Uint8Array,
|
|
55
|
+
initial_counter = 0,
|
|
56
|
+
messagelength = input.byteLength,
|
|
57
|
+
) {
|
|
58
|
+
const whole_blocks = messagelength >> 6;
|
|
59
|
+
const remaining_bytes = messagelength & 63;
|
|
60
|
+
for (let j = 0; j < whole_blocks; j++) {
|
|
61
|
+
const chunk = chacha20_block(key, initial_counter + j, nonce);
|
|
62
|
+
for (let i = 0; i < 64; i++) {
|
|
63
|
+
output[(j << 6) + i] = input[(j << 6) + i] ^ (chunk[i >> 2] >> ((i & 3) << 3));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (remaining_bytes !== 0) {
|
|
67
|
+
const chunk = chacha20_block(key, initial_counter + whole_blocks, nonce);
|
|
68
|
+
for (let i = 0; i < remaining_bytes; i++) {
|
|
69
|
+
output[(whole_blocks << 6) + i] = input[(whole_blocks << 6) + i] ^ (chunk[i >> 2] >> ((i & 3) << 3));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/// SPDX-License-Identifier: MIT
|
|
2
|
+
/// SPDX-FileCopyrightText: Copyright © 2023 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
|
3
|
+
|
|
4
|
+
export * as AEAD from './aead';
|
|
5
|
+
export * as BLAKE2 from './blake2';
|
|
6
|
+
export * as ChaCha20 from './chacha20';
|
|
7
|
+
export * as Noise from './noise';
|
|
8
|
+
export * as Patterns from './patterns';
|
|
9
|
+
export * as Poly1305 from './poly1305';
|
|
10
|
+
export * as NoiseProfiles from './profiles';
|
|
11
|
+
export * as Random from './random';
|
|
12
|
+
export * as X25519 from './x25519';
|