diff --git a/apprelays.js b/apprelays.js index 4ec5861d..fb42194c 100644 --- a/apprelays.js +++ b/apprelays.js @@ -36,6 +36,7 @@ const PROTOCOL_WEBVNC = 204; // Construct a MSTSC Relay object, called upon connection +// This implementation does not have TLS support // This is a bit of a hack as we are going to run the RDP connection thru a loopback connection. // If the "node-rdpjs-2" module supported passing a socket, we would do something different. module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) { @@ -282,7 +283,6 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) { }; - // Construct a SSH Relay object, called upon connection module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) { const Net = require('net'); diff --git a/meshcentral-config-schema.json b/meshcentral-config-schema.json index 49cd48ef..575f6335 100644 --- a/meshcentral-config-schema.json +++ b/meshcentral-config-schema.json @@ -156,16 +156,17 @@ }, "tlsOffload": { "type": [ "boolean", "string" ], "default": false, "description": "When true, indicates that a TLS offloader is in front of the MeshCentral server. More typically, set this to the IP address of the reverse proxy or TLS offloader so that IP forwarding headers will be trusted. For example: \"127.0.0.1,192.168.1.100\"." }, "trustedProxy": { "type": "string", "default": null, "description": "Trust forwarded headers from these IPs or domains. Providing the magic string \"CloudFlare\" will cause the server to download the IP address list of trusted CloudFlare proxies directly from CloudFlare on each server start. For example: \"127.0.0.1,proxy.example.com,CloudFlare\"." }, - "mpsPort": { "type": "integer", "minimum": 1, "maximum": 65535 }, - "mpsPortBind": { "type": "string" }, - "mpsAliasPort": { "type": "integer", "minimum": 1, "maximum": 65535 }, - "mpsAliasHost": { "type": "string" }, - "mpsTlsOffload": { "type": "boolean", "default": false }, + "mpsPort": { "type": "integer", "minimum": 0, "maximum": 65535, "default": 4433, "description": "The Management Presence Server (MPS), this is the server that received Intel AMT Client Initiated Remote Access (CIRA) connections." }, + "mpsPortBind": { "type": "string", "default": null }, + "mpsAliasPort": { "type": "integer", "minimum": 1, "maximum": 65535, "default": null }, + "mpsAliasHost": { "type": "string", "default": null }, + "mpsTlsOffload": { "type": "boolean", "default": false, "description": "When set to true, indicate that TLS is being performed by a device in front of MeshCentral." }, + "mpsHighSecurity": { "type": "boolean", "default": false, "description": "When set to true, the MPS server will only accept TLS 1.2 and 1.3 connections. Older Intel AMT devices will not be able to connect." }, "no2FactorAuth": { "type": "boolean", "default": false }, - "log": { "type": "string" }, - "syslog": { "type": "string" }, - "syslogauth": { "type": "string" }, - "syslogjson": { "type": "string" }, + "log": { "type": "string", "default": null }, + "syslog": { "type": "string", "default": null }, + "syslogauth": { "type": "string", "default": null }, + "syslogjson": { "type": "string", "default": null }, "syslogtcp": { "type": "string", "default": null, "description": "Send syslog events over the network (RFC3164) to a target hostname:port. For example: localhost:514" }, "webrtcConfig": { "type": "object", diff --git a/mpsserver.js b/mpsserver.js index 824c1289..11ec460c 100644 --- a/mpsserver.js +++ b/mpsserver.js @@ -43,8 +43,13 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { if (obj.args.mpstlsoffload) { obj.server = net.createServer(onConnection); } else { - // Note that in order to support older Intel AMT CIRA connections, we have to turn on TLSv1. - obj.server = tls.createServer({ key: certificates.mps.key, cert: certificates.mps.cert, minVersion: 'TLSv1', requestCert: true, rejectUnauthorized: false, ciphers: "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA", secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION }, onConnection); + if (obj.args.mpshighsecurity) { + // Higher security TLS 1.2 and 1.3 only, some older Intel AMT CIRA connections will fail. + obj.server = tls.createServer({ key: certificates.mps.key, cert: certificates.mps.cert, requestCert: true, rejectUnauthorized: false, ciphers: "HIGH:TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256:TLS_CHACHA20_POLY1305_SHA256", secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE | constants.SSL_OP_NO_TLSv1 | constants.SSL_OP_NO_TLSv1_1 }, onConnection) + } else { + // Lower security MPS in order to support older Intel AMT CIRA connections, we have to turn on TLSv1. + obj.server = tls.createServer({ key: certificates.mps.key, cert: certificates.mps.cert, minVersion: 'TLSv1', requestCert: true, rejectUnauthorized: false, ciphers: "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA", secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION }, onConnection) + } //obj.server.on('error', function () { console.log('MPS tls server error'); }); obj.server.on('newSession', function (id, data, cb) { if (tlsSessionStoreCount > 1000) { tlsSessionStoreCount = 0; tlsSessionStore = {}; } tlsSessionStore[id.toString('hex')] = data; tlsSessionStoreCount++; cb(); }); obj.server.on('resumeSession', function (id, cb) { cb(null, tlsSessionStore[id.toString('hex')] || null); }); diff --git a/package.json b/package.json index fc371103..1489695c 100644 --- a/package.json +++ b/package.json @@ -36,8 +36,6 @@ "sample-config-advanced.json" ], "dependencies": { - "@yetzt/nedb": "^1.8.0", - "archiver": "^4.0.2", "body-parser": "^1.19.0", "cbor": "~5.2.0", "compression": "^1.7.4", @@ -45,25 +43,13 @@ "express": "^4.17.0", "express-handlebars": "^5.3.5", "express-ws": "^4.0.0", - "html-minifier": "^4.0.0", - "image-size": "^1.0.1", "ipcheck": "^0.1.0", - "jsdom": "^19.0.0", - "loadavg-windows": "^1.1.1", - "minify-js": "^0.0.4", "minimist": "^1.2.5", "multiparty": "^4.2.1", + "@yetzt/nedb": "^1.8.0", "node-forge": "^1.0.0", - "node-rdpjs-2": "^0.3.5", - "node-windows": "^0.1.4", - "otplib": "^10.2.3", - "pg": "^8.7.1", - "pgtools": "^0.3.2", - "ssh2": "^1.9.0", - "web-push": "^3.4.5", "ws": "^5.2.3", - "yauzl": "^2.10.0", - "yubikeyotp": "^0.2.0" + "yauzl": "^2.10.0" }, "engines": { "node": ">=10.0.0" diff --git a/test.js b/test.js new file mode 100644 index 00000000..5024be07 --- /dev/null +++ b/test.js @@ -0,0 +1,248 @@ +/* +YST: create_negotiate_message: 4e544c4d53535000010000003582086000000000000000000000000000000000 +WRITE: negotiate_message 302fa003020102a12830263024a02204204e544c4d53535000010000003582086000000000000000000000000000000000 +READ: read_ts_server_challenge 3081b2a003020106a181aa3081a73081a4a081a104819e4e544c4d53535000020000000e000e003800000035828a62f2290572b3cac375000000000000000058005800460000000a00614a0000000f430045004e005400520041004c0002000e00430045004e005400520041004c0001000e00430045004e005400520041004c0004000e00430065006e007400720061006c0003000e00430065006e007400720061006c0007000800afbc2c2a9256d80100000000 +YST: read_challenge_message1: 4e544c4d53535000020000000e000e003800000035828a62f2290572b3cac375000000000000000058005800460000000a00614a0000000f430045004e005400520041004c0002000e00430045004e005400520041004c0001000e00430045004e005400520041004c0004000e00430065006e007400720061006c0003000e00430065006e007400720061006c0007000800afbc2c2a9256d80100000000 +YST: target_name: 02000e00430045004e005400520041004c0001000e00430045004e005400520041004c0004000e00430065006e007400720061006c0003000e00430065006e007400720061006c0007000800afbc2c2a9256d80100000000 +YST: timestamp: afbc2c2a9256d801 +YST: client_challenge: f8d1e2057b9c7169 +YST: nt_challenge_response: 32e87ea049485920fca6695ae9daee820101000000000000afbc2c2a9256d801f8d1e2057b9c71690000000002000e00430045004e005400520041004c0001000e00430045004e005400520041004c0004000e00430065006e007400720061006c0003000e00430065006e007400720061006c0007000800afbc2c2a9256d80100000000 +YST: lm_challenge_response: 2b0f863797a00d4446a1c9f49de124a5f8d1e2057b9c7169 +YST: session_base_key: 52f86f741e92e2e9d8013c1afcecfa13 +YST: key_exchange_key: 52f86f741e92e2e9d8013c1afcecfa13 +YST: encrypted_random_session_key: d9b2353a7f7ba9569ece01e96686cdef +YST: self.is_unicode: true +YST: domain: +YST: user: 640065006600610075006c007400 +YST: tmp_final_auth_message: 4e544c4d53535000030000001800180058000000840084007000000000000000f40000000e000e00f40000000000000002010000100010000201000035828a62060072170000000f000000000000000000000000000000002b0f863797a00d4446a1c9f49de124a5f8d1e2057b9c716932e87ea049485920fca6695ae9daee820101000000000000afbc2c2a9256d801f8d1e2057b9c71690000000002000e00430045004e005400520041004c0001000e00430045004e005400520041004c0004000e00430065006e007400720061006c0003000e00430065006e007400720061006c0007000800afbc2c2a9256d80100000000640065006600610075006c007400d9b2353a7f7ba9569ece01e96686cdef +YST: signature: 4cbff4ee21ce4114c7657d7aa427ca3f +YST: read_challenge_message2: 4e544c4d53535000030000001800180058000000840084007000000000000000f40000000e000e00f40000000000000002010000100010000201000035828a62060072170000000f4cbff4ee21ce4114c7657d7aa427ca3f2b0f863797a00d4446a1c9f49de124a5f8d1e2057b9c716932e87ea049485920fca6695ae9daee820101000000000000afbc2c2a9256d801f8d1e2057b9c71690000000002000e00430045004e005400520041004c0001000e00430045004e005400520041004c0004000e00430065006e007400720061006c0003000e00430065006e007400720061006c0007000800afbc2c2a9256d80100000000640065006600610075006c007400d9b2353a7f7ba9569ece01e96686cdef +WRITE challenge: 30820251a003020102a18201223082011e3082011aa0820116048201124e544c4d53535000030000001800180058000000840084007000000000000000f40000000e000e00f40000000000000002010000100010000201000035828a62060072170000000f4cbff4ee21ce4114c7657d7aa427ca3f2b0f863797a00d4446a1c9f49de124a5f8d1e2057b9c716932e87ea049485920fca6695ae9daee820101000000000000afbc2c2a9256d801f8d1e2057b9c71690000000002000e00430045004e005400520041004c0001000e00430045004e005400520041004c0004000e00430065006e007400720061006c0003000e00430065006e007400720061006c0007000800afbc2c2a9256d80100000000640065006600610075006c007400d9b2353a7f7ba9569ece01e96686cdefa38201220482011e0100000095fb7df84ffcd81d00000000a0588a3354258442e2997c79a4005e91ac49759b3c362228f42aefa5daad071a8eb905d7bc16611df35a50c0c577acf4543609e3ade8ff8289d371d8baed9eb2bd738de6c208f6a9885f1b0ee8aee2b26be87aa189bb41989c79716d938eb68e2f1e4bdc74c25e995c438aa5e5e30c2784214f4763c6737417edac43371f65f22aab68619f022b2f46b4cb9d00d0befe6d7e83d0d2c4ae69b544b1e90b1d5198459722eff903e8550864a46c552d75c83fb8ca8491b573fcd59d17cdc34e2505995fa6922554fdf03016161b430e476875a3752907087e993388f56675957a75b42b062f1f66ef8597a69deb466337396747ec8e46c573344b24fc8fba9305207eb10462ce355299063bab4cf105 +READ: read_ts_validate 3081b2a003020106a181aa3081a73081a4a081a104819e4e544c4d53535000020000000e000e003800000035828a62f2290572b3cac375000000000000000058005800460000000a00614a0000000f430045004e005400520041004c0002000e00430045004e005400520041004c0001000e00430045004e005400520041004c0004000e00430065006e007400720061006c0003000e00430065006e007400720061006c0007000800afbc2c2a9256d80100000000 +WRITE credentials: 302fa003020102a12830263024a02204204e544c4d53535000010000003582086000000000000000000000000000000000 +*/ + +/* + "Signature" => Check::new(b"NTLMSSP\x00".to_vec()), + "MessageType" => Check::new(U32::LE(2)), + "TargetNameLen" => U16::LE(0), + "TargetNameLenMax" => U16::LE(0), + "TargetNameBufferOffset" => U32::LE(0), + "NegotiateFlags" => DynOption::new(U32::LE(0), |node| { + if node.inner() & (Negotiate::NtlmsspNegociateVersion as u32) == 0 { + return MessageOption::SkipField("Version".to_string()) + } + return MessageOption::None + }), + "ServerChallenge" => vec![0; 8], + "Reserved" => vec![0; 8], + "TargetInfoLen" => U16::LE(0), + "TargetInfoMaxLen" => U16::LE(0), + "TargetInfoBufferOffset" => U32::LE(0), + "Version" => version(), + "Payload" => Vec::::new() +*/ + +const NegotiateFlags = { + NtlmsspNegociate56: 0x80000000, + NtlmsspNegociateKeyExch: 0x40000000, + NtlmsspNegociate128: 0x20000000, + NtlmsspNegociateVersion: 0x02000000, + NtlmsspNegociateTargetInfo: 0x00800000, + NtlmsspRequestNonNTSessionKey: 0x00400000, + NtlmsspNegociateIdentify: 0x00100000, + NtlmsspNegociateExtendedSessionSecurity: 0x00080000, + NtlmsspTargetTypeServer: 0x00020000, + NtlmsspTargetTypeDomain: 0x00010000, + NtlmsspNegociateAlwaysSign: 0x00008000, + NtlmsspNegociateOEMWorkstationSupplied: 0x00002000, + NtlmsspNegociateOEMDomainSupplied: 0x00001000, + NtlmsspNegociateNTLM: 0x00000200, + NtlmsspNegociateLMKey: 0x00000080, + NtlmsspNegociateDatagram: 0x00000040, + NtlmsspNegociateSeal: 0x00000020, + NtlmsspNegociateSign: 0x00000010, + NtlmsspRequestTarget: 0x00000004, + NtlmNegotiateOEM: 0x00000002, + NtlmsspNegociateUnicode: 0x00000001 +} + +const MajorVersion = { + WindowsMajorVersion5: 0x05, + WindowsMajorVersion6: 0x06 +} + +const MinorVersion = { + WindowsMinorVersion0: 0x00, + WindowsMinorVersion1: 0x01, + WindowsMinorVersion2: 0x02, + WindowsMinorVersion3: 0x03 +} + +const NTLMRevision = { + NtlmSspRevisionW2K3: 0x0F +} + +function decodeTargetInfo(targetInfoBuf) { + var r = {}, type, len, data, ptr = 0; + while (true) { + type = targetInfoBuf.readInt16LE(ptr); + if (type == 0) break; + len = targetInfoBuf.readInt16LE(ptr + 2); + r[type] = targetInfoBuf.slice(ptr + 4, ptr + 4 + len); + ptr += (4 + len); + } + return r; +} + +function bufToArr(b) { var r = []; for (var i = 0; i < b.length; i++) { r.push(b.readUInt8(i)); } return r; } // For unit testing +function toUnicode(str) { return Buffer.from(str, 'ucs2'); } +function md4(str) { return crypto.createHash('md4').update(str).digest(); } +function md5(str) { return crypto.createHash('md5').update(str).digest(); } +function hmacmd5(key, data) { return crypto.createHmac('md5', key).update(data).digest(); } +function ntowfv2(password, user, domain) { return hmacmd5(md4(toUnicode(password)), toUnicode(user.toUpperCase() + domain)); } +function lmowfv2(password, user, domain) { return ntowfv2(password, user, domain); } +function zeroBuffer(len) { return Buffer.alloc(len); } +function compute_response_v2(response_key_nt, response_key_lm, server_challenge, client_challenge, time, server_name) { + const response_version = Buffer.from('01', 'hex'); + const hi_response_version = Buffer.from('01', 'hex'); + const temp = Buffer.concat([response_version, hi_response_version, zeroBuffer(6), time, client_challenge, zeroBuffer(4), server_name]); + const nt_proof_str = hmacmd5(response_key_nt, Buffer.concat([server_challenge, temp])); + const nt_challenge_response = Buffer.concat([nt_proof_str, temp]); + const lm_challenge_response = Buffer.concat([hmacmd5(response_key_lm, Buffer.concat([server_challenge, client_challenge])), client_challenge]); + const session_base_key = hmacmd5(response_key_nt, nt_proof_str); + return [nt_challenge_response, lm_challenge_response, session_base_key]; +} +function kx_key_v2(session_base_key, _lm_challenge_response, _server_challenge) { return session_base_key; } +function rc4k(key, data) { return crypto.createCipheriv('rc4', key, null).update(data); } + +/// Compute a signature of all data exchange during NTLMv2 handshake +function mic(exported_session_key, negotiate_message, challenge_message, authenticate_message) { return hmacmd5(exported_session_key, Buffer.concat([negotiate_message, challenge_message, authenticate_message])); } + +/// NTLMv2 security interface generate a sign key +/// By using MD5 of the session key + a static member (sentense) +function sign_key(exported_session_key, is_client) { + if (is_client) { + return md5(Buffer.concat([exported_session_key, Buffer.from("session key to client-to-server signing key magic constant\0")])); + } else { + return md5(Buffer.concat([exported_session_key, Buffer.from("session key to server-to-client signing key magic constant\0")])); + } +} + +/// NTLMv2 security interface generate a seal key +/// By using MD5 of the session key + a static member (sentense) +function seal_key(exported_session_key, is_client) { + if (is_client) { + return md5(Buffer.concat([exported_session_key, Buffer.from("session key to client-to-server sealing key magic constant\0")])); + } else { + return md5(Buffer.concat([exported_session_key, Buffer.from("session key to server-to-client sealing key magic constant\0")])); + } +} + +function authenticate_message(lm_challenge_response, nt_challenge_response, domain, user, workstation, encrypted_random_session_key, flags) { + const payload = Buffer.concat([lm_challenge_response, nt_challenge_response, domain, user, workstation, encrypted_random_session_key]); + const offset = ((flags & NegotiateFlags.NtlmsspNegociateVersion) == 0) ? 80 : 88; + const buf = Buffer.alloc(offset - 16); + buf.write('4e544c4d53535000', 0, 8, 'hex'); // Signature + buf.writeInt32LE(3, 8); // MessageType + buf.writeInt16LE(lm_challenge_response.length, 12); // LmChallengeResponseLen + buf.writeInt16LE(lm_challenge_response.length, 14); // LmChallengeResponseMaxLen + buf.writeInt32LE(offset, 16); // LmChallengeResponseBufferOffset + buf.writeInt16LE(nt_challenge_response.length, 20); // NtChallengeResponseLen + buf.writeInt16LE(nt_challenge_response.length, 22); // NtChallengeResponseMaxLen + buf.writeInt32LE(offset + lm_challenge_response.length, 24); // NtChallengeResponseBufferOffset + buf.writeInt16LE(domain.length, 28); // DomainNameLen + buf.writeInt16LE(domain.length, 30); // DomainNameMaxLen + buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length, 32); // DomainNameBufferOffset + buf.writeInt16LE(user.length, 36); // UserNameLen + buf.writeInt16LE(user.length, 38); // UserNameMaxLen + buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length + domain.length, 40); // UserNameBufferOffset + buf.writeInt16LE(workstation.length, 44); // WorkstationLen + buf.writeInt16LE(workstation.length, 46); // WorkstationMaxLen + buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length + domain.length + user.length, 48); // WorkstationBufferOffset + buf.writeInt16LE(encrypted_random_session_key.length, 52); // EncryptedRandomSessionLen + buf.writeInt16LE(encrypted_random_session_key.length, 54); // EncryptedRandomSessionMaxLen + buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length + domain.length + user.length + workstation.length, 56); // EncryptedRandomSessionBufferOffset + buf.writeInt32LE(flags, 60); // NegotiateFlags + if ((flags & NegotiateFlags.NtlmsspNegociateVersion) != 0) { + buf.writeUInt8(MajorVersion.WindowsMajorVersion6, 64); // ProductMajorVersion + buf.writeUInt8(MinorVersion.WindowsMinorVersion0, 65); // ProductMinorVersion + buf.writeInt16LE(6002, 66); // ProductBuild + //buf.writeInt16LE(0, 68); // Reserved + //buf.writeUInt8(0, 70); // Reserved + buf.writeUInt8(NTLMRevision.NtlmSspRevisionW2K3, 71); // NTLMRevisionCurrent + } + return [buf, payload]; +} + +function read_challenge_message(derBuffer, user, pass, domain, negotiate_message) { + const headerSignature = derBuffer.slice(0, 8); + if (headerSignature.toString('hex') != '4e544c4d53535000') { console.log('BAD SIGNATURE'); } + const messageType = derBuffer.readInt32LE(8); + if (messageType != 2) { console.log('BAD MESSAGE TYPE'); } + const targetNameLen = derBuffer.readInt16LE(12); + const targetNameLenMax = derBuffer.readInt16LE(14); + const targetNameBufferOffset = derBuffer.readInt32LE(16); + const negotiateFlags = derBuffer.readInt32LE(20); + const serverChallenge = derBuffer.slice(24, 32); + const reserved = derBuffer.slice(32, 40); + if (reserved.toString('hex') != '0000000000000000') { console.log('BAD RESERVED'); } + const targetInfoLen = derBuffer.readInt16LE(40); + const targetInfoLenMax = derBuffer.readInt16LE(42); + const targetInfoBufferOffset = derBuffer.readInt32LE(44); + const targetName = derBuffer.slice(targetNameBufferOffset, targetNameBufferOffset + targetNameLen); + const targetInfo = decodeTargetInfo(derBuffer.slice(targetInfoBufferOffset, targetInfoBufferOffset + targetInfoLen)); + const timestamp = targetInfo[7]; + if (timestamp == null) { console.log('NO TIMESTAMP'); } + const clientChallenge = crypto.randomBytes(8); + const response_key_nt = ntowfv2(pass, user, domain); // Password, Username, Domain + const response_key_lm = lmowfv2(pass, user, domain); // Password, Username, Domain + + var resp = compute_response_v2(response_key_nt, response_key_lm, serverChallenge, clientChallenge, timestamp, targetName); + const nt_challenge_response = resp[0]; + const lm_challenge_response = resp[1]; + const session_base_key = resp[2]; + + const key_exchange_key = kx_key_v2(session_base_key, lm_challenge_response, serverChallenge); + const exported_session_key = crypto.randomBytes(16); + const encrypted_random_session_key = rc4k(key_exchange_key, exported_session_key); + + const is_unicode = ((negotiateFlags & 1) != 0) + var xdomain = null; + var xuser = null; + if (is_unicode) { + xdomain = toUnicode(domain); + xuser = toUnicode(user); + } else { + xdomain = Buffer.from(domain, 'utf8'); + xuser = Buffer.from(domain, 'utf8'); + } + + const auth_message_compute = authenticate_message(lm_challenge_response, nt_challenge_response, xdomain, xuser, zeroBuffer(0), encrypted_random_session_key, negotiateFlags); + + // Write a tmp message to compute MIC and then include it into final message + const tmp_final_auth_message = Buffer.concat([auth_message_compute[0], zeroBuffer(16), auth_message_compute[1]]); + + const signature = mic(exported_session_key, negotiate_message, derBuffer, tmp_final_auth_message); + return Buffer.concat([auth_message_compute[0], signature, auth_message_compute[1]]); +} + + + +// Create create_ts_request +const crypto = require('crypto'); +const forge = require('node-forge'); +const asn1 = forge.asn1; +const pki = forge.pki; +const entireBuffer = Buffer.from('3081b2a003020106a181aa3081a73081a4a081a104819e4e544c4d53535000020000000e000e003800000035828a62f2290572b3cac375000000000000000058005800460000000a00614a0000000f430045004e005400520041004c0002000e00430045004e005400520041004c0001000e00430045004e005400520041004c0004000e00430065006e007400720061006c0003000e00430065006e007400720061006c0007000800afbc2c2a9256d80100000000', 'hex').toString('binary'); + +// We have a full ASN1 data block, decode it now +const der = asn1.fromDer(entireBuffer.toString('binary')); +const derNum = der.value[0].value[0].value.charCodeAt(0); +const derBuffer = Buffer.from(der.value[1].value[0].value[0].value[0].value[0].value, 'binary'); +const negotiate_message = Buffer.from('4e544c4d53535000010000003582086000000000000000000000000000000000', 'hex'); +const client_challenge = read_challenge_message(derBuffer, "default", "", "", negotiate_message); + +console.log('client_challenge', client_challenge.toString('hex')); + +