diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..66618b3 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +language: node_js + +cache: false + +install: + - yarn install --frozen-lockfile --network-timeout 1000000 + +script: + set -e; + yarn run lint; + yarn run check-types; + yarn run build; diff --git a/src/xx.ts b/src/xx.ts index 1c59e6e..a7046c4 100644 --- a/src/xx.ts +++ b/src/xx.ts @@ -6,51 +6,50 @@ import { bytes32, uint32, uint64, bytes } from './@types/basic' import { KeyPair } from './@types/libp2p' import { generateKeypair } from './utils'; - export interface MessageBuffer { - ne: bytes32, - ns: bytes, - ciphertext: bytes + ne: bytes32; + ns: bytes; + ciphertext: bytes; } type CipherState = { - k: bytes32, - n: uint32, + k: bytes32; + n: uint32; } type SymmetricState = { - cs: CipherState, - ck: bytes32, // chaining key - h: bytes32, // handshake hash + cs: CipherState; + ck: bytes32; // chaining key + h: bytes32; // handshake hash } type HandshakeState = { - ss: SymmetricState, - s: KeyPair, - e?: KeyPair, - rs: bytes32, - re: bytes32, - psk: bytes32, + ss: SymmetricState; + s: KeyPair; + e?: KeyPair; + rs: bytes32; + re: bytes32; + psk: bytes32; } export type NoiseSession = { - hs: HandshakeState, - h?: bytes32, - cs1?: CipherState, - cs2?: CipherState, - mc: uint64, - i: boolean, + hs: HandshakeState; + h?: bytes32; + cs1?: CipherState; + cs2?: CipherState; + mc: uint64; + i: boolean; } export type Hkdf = [bytes, bytes, bytes]; const minNonce = 0; export class XXHandshake { - private createEmptyKey() : bytes32 { + private createEmptyKey(): bytes32 { return Buffer.alloc(32); } - private async initializeInitiator(prologue: bytes32, s: KeyPair, rs: bytes32, psk: bytes32) : Promise { + private async initializeInitiator(prologue: bytes32, s: KeyPair, rs: bytes32, psk: bytes32): Promise { const name = "Noise_XX_25519_ChaChaPoly_SHA256"; const ss = await this.initializeSymmetric(name); this.mixHash(ss, prologue); @@ -59,7 +58,7 @@ export class XXHandshake { return { ss, s, rs, psk, re }; } - private async initializeResponder(prologue: bytes32, s: KeyPair, rs: bytes32, psk: bytes32) : Promise { + private async initializeResponder(prologue: bytes32, s: KeyPair, rs: bytes32, psk: bytes32): Promise { const name = "Noise_XX_25519_ChaChaPoly_SHA256"; const ss = await this.initializeSymmetric(name); this.mixHash(ss, prologue); @@ -68,25 +67,25 @@ export class XXHandshake { return { ss, s, rs, psk, re }; } - private incrementNonce(n: uint32) : uint32 { + private incrementNonce(n: uint32): uint32 { return n + 1; } - private dh(privateKey: bytes32, publicKey: bytes32) : bytes32 { + private dh(privateKey: bytes32, publicKey: bytes32): bytes32 { const derived = x25519.derive(publicKey, privateKey); const result = Buffer.alloc(32); derived.copy(result); return result; } - private nonceToBytes(n: uint32) : bytes { + private nonceToBytes(n: uint32): bytes { const nonce = Buffer.alloc(12); nonce.writeUInt32LE(n, 4); return nonce; } - private encrypt(k: bytes32, n: uint32, ad: bytes, plaintext: bytes) : bytes { + private encrypt(k: bytes32, n: uint32, ad: bytes, plaintext: bytes): bytes { const nonce = this.nonceToBytes(n); const ctx = new AEAD(); @@ -98,7 +97,7 @@ export class XXHandshake { return plaintext; } - private decrypt(k: bytes32, n: uint32, ad: bytes, ciphertext: bytes) : bytes { + private decrypt(k: bytes32, n: uint32, ad: bytes, ciphertext: bytes): bytes { const nonce = this.nonceToBytes(n); const ctx = new AEAD(); @@ -110,33 +109,33 @@ export class XXHandshake { return ciphertext; } - private isEmptyKey(k: bytes32) : boolean { + private isEmptyKey(k: bytes32): boolean { const emptyKey = this.createEmptyKey(); return emptyKey.equals(k); } // Cipher state related - private initializeKey(k: bytes32) : CipherState { + private initializeKey(k: bytes32): CipherState { const n = minNonce; return { k, n }; } - private hasKey(cs: CipherState) : boolean { + private hasKey(cs: CipherState): boolean { return !this.isEmptyKey(cs.k); } - private setNonce(cs: CipherState, nonce: uint32) : void { + private setNonce(cs: CipherState, nonce: uint32): void { cs.n = nonce; } - public encryptWithAd(cs: CipherState, ad: bytes, plaintext: bytes) : bytes { + public encryptWithAd(cs: CipherState, ad: bytes, plaintext: bytes): bytes { const e = this.encrypt(cs.k, cs.n, ad, plaintext); this.setNonce(cs, this.incrementNonce(cs.n)); return e; } - public decryptWithAd(cs: CipherState, ad: bytes, ciphertext: bytes) : bytes { + public decryptWithAd(cs: CipherState, ad: bytes, ciphertext: bytes): bytes { const plaintext = this.decrypt(cs.k, cs.n, ad, ciphertext); this.setNonce(cs, this.incrementNonce(cs.n)); @@ -145,13 +144,13 @@ export class XXHandshake { // Symmetric state related - private async initializeSymmetric(protocolName: string) : Promise { + private async initializeSymmetric(protocolName: string): Promise { const protocolNameBytes: bytes = Buffer.from(protocolName, 'utf-8'); const h = await this.hashProtocolName(protocolNameBytes); const ck = h; const key = this.createEmptyKey(); - const cs:CipherState = this.initializeKey(key); + const cs: CipherState = this.initializeKey(key); return { cs, ck, h }; } @@ -162,9 +161,9 @@ export class XXHandshake { ss.ck = ck; } - private async hashProtocolName(protocolName: bytes) : Promise { + private async hashProtocolName(protocolName: bytes): Promise { if (protocolName.length <= 32) { - let h = Buffer.alloc(32); + const h = Buffer.alloc(32); protocolName.copy(h); return h; } else { @@ -172,7 +171,7 @@ export class XXHandshake { } } - public getHkdf(ck: bytes32, ikm: bytes) : Hkdf { + public getHkdf(ck: bytes32, ikm: bytes): Hkdf { const info = Buffer.alloc(0); const prk = HKDF.extract(SHA256, ikm, ck); const okm = HKDF.expand(SHA256, prk, info, 96); @@ -188,11 +187,11 @@ export class XXHandshake { ss.h = this.getHash(ss.h, data); } - private getHash(a: bytes, b: bytes) : bytes32 { + private getHash(a: bytes, b: bytes): bytes32 { return SHA256.digest(Buffer.from([...a, ...b])); } - private async encryptAndHash(ss: SymmetricState, plaintext: bytes) : Promise { + private async encryptAndHash(ss: SymmetricState, plaintext: bytes): Promise { let ciphertext; if (this.hasKey(ss.cs)) { ciphertext = this.encryptWithAd(ss.cs, ss.h, plaintext); @@ -204,7 +203,7 @@ export class XXHandshake { return ciphertext; } - private async decryptAndHash(ss: SymmetricState, ciphertext: bytes) : Promise { + private async decryptAndHash(ss: SymmetricState, ciphertext: bytes): Promise { let plaintext; if (this.hasKey(ss.cs)) { plaintext = this.decryptWithAd(ss.cs, ss.h, ciphertext); @@ -224,12 +223,9 @@ export class XXHandshake { return { cs1, cs2 }; } - private async writeMessageA(hs: HandshakeState, payload: bytes) : Promise { - let ns = Buffer.alloc(0); + private async writeMessageA(hs: HandshakeState, payload: bytes): Promise { + const ns = Buffer.alloc(0); hs.e = generateKeypair(); - if (!hs.e) { - throw new Error("Handshake state has keypair missing."); - } const ne = hs.e.publicKey; this.mixHash(hs.ss, ne); @@ -238,11 +234,8 @@ export class XXHandshake { return {ne, ns, ciphertext}; } - private async writeMessageB(hs: HandshakeState, payload: bytes) : Promise { + private async writeMessageB(hs: HandshakeState, payload: bytes): Promise { hs.e = generateKeypair(); - if (!hs.e) { - throw new Error("Handshake state has keypair missing."); - } const ne = hs.e.publicKey; this.mixHash(hs.ss, ne); @@ -268,7 +261,7 @@ export class XXHandshake { return { h: hs.ss.h, messageBuffer, cs1, cs2 }; } - private async writeMessageRegular(cs: CipherState, payload: bytes) : Promise { + private async writeMessageRegular(cs: CipherState, payload: bytes): Promise { const ciphertext = this.encryptWithAd(cs, Buffer.alloc(0), payload); const ne = this.createEmptyKey(); const ns = Buffer.alloc(0); @@ -276,8 +269,7 @@ export class XXHandshake { return { ne, ns, ciphertext }; } - private async readMessageA(hs: HandshakeState, message: MessageBuffer) : Promise { - console.log("publci key: ", message.ne) + private async readMessageA(hs: HandshakeState, message: MessageBuffer): Promise { if (x25519.publicKeyVerify(message.ne)) { hs.re = message.ne; } @@ -286,7 +278,7 @@ export class XXHandshake { return await this.decryptAndHash(hs.ss, message.ciphertext); } - private async readMessageB(hs: HandshakeState, message: MessageBuffer) : Promise { + private async readMessageB(hs: HandshakeState, message: MessageBuffer): Promise { if (x25519.publicKeyVerify(message.ne)) { hs.re = message.ne; } @@ -320,7 +312,7 @@ export class XXHandshake { return { h: hs.ss.h, plaintext, cs1, cs2 }; } - private readMessageRegular(cs: CipherState, message: MessageBuffer) : bytes { + private readMessageRegular(cs: CipherState, message: MessageBuffer): bytes { return this.decryptWithAd(cs, Buffer.alloc(0), message.ciphertext); } @@ -341,7 +333,7 @@ export class XXHandshake { }; } - public async sendMessage(session: NoiseSession, message: bytes) : Promise { + public async sendMessage(session: NoiseSession, message: bytes): Promise { let messageBuffer: MessageBuffer; if (session.mc.eqn(0)) { messageBuffer = await this.writeMessageA(session.hs, message); @@ -375,7 +367,7 @@ export class XXHandshake { return messageBuffer; } - public async recvMessage(session: NoiseSession, message: MessageBuffer) : Promise { + public async recvMessage(session: NoiseSession, message: MessageBuffer): Promise { let plaintext: bytes; if (session.mc.eqn(0)) { plaintext = await this.readMessageA(session.hs, message);