import { createDecode } from "binary-data" ; import { createHash , createHmac } from "crypto" ; import { ec } from "elliptic" ; import * as nacl from "tweetnacl" ; import { NamedCurveAlgorithm } from "./const" ; const elliptic = new ec ( "secp256k1" ) ; export function prfPreMasterSecret ( publicKey , privateKey , curve ) { switch ( curve ) { case NamedCurveAlgorithm . namedCurveP256 : const pub = elliptic . keyFromPublic ( publicKey ) . getPublic ( ) ; const x = pub . getX ( ) ; const y = pub . getY ( ) ; throw new Error ( ) ; case NamedCurveAlgorithm . namedCurveX25519 : return Buffer . from ( nacl . scalarMult ( privateKey , publicKey ) ) ; default : throw new Error ( ) ; } } export function hmac ( algorithm , secret , data ) { const hash = createHmac ( algorithm , secret ) ; hash . update ( data ) ; return hash . digest ( ) ; } export function prfPHash ( secret , seed , requestedLegth , algorithm = "sha256" ) { const totalLength = requestedLegth ; const bufs = [ ] ; let Ai = seed ; do { Ai = hmac ( algorithm , secret , Ai ) ; const output = hmac ( algorithm , secret , Buffer . concat ( [ Ai , seed ] ) ) ; bufs . push ( output ) ; requestedLegth -= output . length ; } while ( requestedLegth > 0 ) ; return Buffer . concat ( bufs , totalLength ) ; } export function prfMasterSecret ( preMasterSecret , clientRandom , serverRandom ) { const seed = Buffer . concat ( [ Buffer . from ( "master secret" ) , clientRandom , serverRandom , ] ) ; return prfPHash ( preMasterSecret , seed , 48 ) ; } export function exportKeyingMaterial ( label , length , masterSecret , localRandom , remoteRandom , isClient ) { const clientRandom = isClient ? localRandom : remoteRandom ; const serverRandom = isClient ? remoteRandom : localRandom ; const seed = Buffer . concat ( [ Buffer . from ( label ) , clientRandom , serverRandom ] ) ; return prfPHash ( masterSecret , seed , length ) ; } export function hash ( algorithm , data ) { return createHash ( algorithm ) . update ( data ) . digest ( ) ; } export function prfVerifyData ( masterSecret , handshakes , label , size = 12 ) { const bytes = hash ( "sha256" , handshakes ) ; return prfPHash ( masterSecret , Buffer . concat ( [ Buffer . from ( label ) , bytes ] ) , size ) ; } export function prfVerifyDataClient ( masterSecret , handshakes ) { return prfVerifyData ( masterSecret , handshakes , "client finished" ) ; } export function prfVerifyDataServer ( masterSecret , handshakes ) { return prfVerifyData ( masterSecret , handshakes , "server finished" ) ; } export function prfEncryptionKeys ( masterSecret , clientRandom , serverRandom , prfKeyLen , prfIvLen , prfNonceLen , algorithm = "sha256" ) { const size = prfKeyLen * 2 + prfIvLen * 2 ; const secret = masterSecret ; const seed = Buffer . concat ( [ serverRandom , clientRandom ] ) ; const keyBlock = prfPHash ( secret , Buffer . concat ( [ Buffer . from ( "key expansion" ) , seed ] ) , size , algorithm ) ; const stream = createDecode ( keyBlock ) ; const clientWriteKey = stream . readBuffer ( prfKeyLen ) ; const serverWriteKey = stream . readBuffer ( prfKeyLen ) ; const clientNonceImplicit = stream . readBuffer ( prfIvLen ) ; const serverNonceImplicit = stream . readBuffer ( prfIvLen ) ; const clientNonce = Buffer . alloc ( prfNonceLen , 0 ) ; const serverNonce = Buffer . alloc ( prfNonceLen , 0 ) ; clientNonceImplicit . copy ( clientNonce , 0 ) ; serverNonceImplicit . copy ( serverNonce , 0 ) ; return { clientWriteKey , serverWriteKey , clientNonce , serverNonce } ; }