mirror of
https://github.com/fluencelabs/js-libp2p-noise
synced 2025-04-25 14:12:30 +00:00
Merge remote-tracking branch 'remotes/origin/master' into morrigan/interface
# Conflicts: # src/xx.ts
This commit is contained in:
commit
9c898043b8
12
.travis.yml
Normal file
12
.travis.yml
Normal file
@ -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;
|
110
src/xx.ts
110
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<HandshakeState> {
|
||||
private async initializeInitiator(prologue: bytes32, s: KeyPair, rs: bytes32, psk: bytes32): Promise<HandshakeState> {
|
||||
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<HandshakeState> {
|
||||
private async initializeResponder(prologue: bytes32, s: KeyPair, rs: bytes32, psk: bytes32): Promise<HandshakeState> {
|
||||
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<SymmetricState> {
|
||||
private async initializeSymmetric(protocolName: string): Promise<SymmetricState> {
|
||||
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<bytes32> {
|
||||
private async hashProtocolName(protocolName: bytes): Promise<bytes32> {
|
||||
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<bytes> {
|
||||
private async encryptAndHash(ss: SymmetricState, plaintext: bytes): Promise<bytes> {
|
||||
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<bytes> {
|
||||
private async decryptAndHash(ss: SymmetricState, ciphertext: bytes): Promise<bytes> {
|
||||
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<MessageBuffer> {
|
||||
let ns = Buffer.alloc(0);
|
||||
private async writeMessageA(hs: HandshakeState, payload: bytes): Promise<MessageBuffer> {
|
||||
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<MessageBuffer> {
|
||||
private async writeMessageB(hs: HandshakeState, payload: bytes): Promise<MessageBuffer> {
|
||||
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<MessageBuffer> {
|
||||
private async writeMessageRegular(cs: CipherState, payload: bytes): Promise<MessageBuffer> {
|
||||
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<bytes> {
|
||||
console.log("publci key: ", message.ne)
|
||||
private async readMessageA(hs: HandshakeState, message: MessageBuffer): Promise<bytes> {
|
||||
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<bytes> {
|
||||
private async readMessageB(hs: HandshakeState, message: MessageBuffer): Promise<bytes> {
|
||||
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<MessageBuffer> {
|
||||
public async sendMessage(session: NoiseSession, message: bytes): Promise<MessageBuffer> {
|
||||
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<bytes> {
|
||||
public async recvMessage(session: NoiseSession, message: MessageBuffer): Promise<bytes> {
|
||||
let plaintext: bytes;
|
||||
if (session.mc.eqn(0)) {
|
||||
plaintext = await this.readMessageA(session.hs, message);
|
||||
|
Loading…
x
Reference in New Issue
Block a user