ログ記録された鍵要素から暗号鍵を再生成できるがPFS(Perfect Forward Secrecy)のDiffie-Hellman Groupによってデータが省略され再生成できない場合があった。
DH Group 1(modp768), 2(modp1024) は、省略されない。DH Group 5(modp1536), 14(modp2048)は、データの一部が省略される。ISAKMPのPhase1では、modp2048でもSKEYID_eが256bitで記録されるので鍵長不足にならない。Phase2で、AES256、PFS=onとするのであれば、Phase2のDH Groupを2(modp1024)までとする必要がある。
sha1/hmac-sha1のモジュール。ヤマハLUAでは、「bit関数」を使わずbit操作処理が選択されるが、動作しない。「bit」関数を使う処理が独立ファイルで構成されているため、ヤマハLUAの「bit関数」を使用するように修正すると動作する。修正は、「sha1/bit_ops.lua」をコピーし「bit_rt_ops.lua」として「sha1/init.lua」でヤマハLUAで「sha1/bit_rt_ops.lua」が選択されるようにした。
sha256/hmac-sha256のモジュール。ヤマハLUAでは、動作しない。「math.floor()」が使用されている。ヤマハLUAの「bit関数」を使用するように修正すると動作する。
L2TP/IPSec接続時に選択されたcipher suitが記録されないのでsyslog watchでVPN接続を監視し、「show ipsec sa gateway 2 detail」のデータから抽出しsyslogに記録するスクリプトを設定する。syslogは、「[IKEex]」で識別出来るように記録する
GetNvrCryptKeys.lua
--[[
Jul.16 '2020 Rabbit51
GetNvrCryptKeys.lua
lua sd1:/lua/GetNvrCryptKeys.lua
lua sd1:/lua/GetNvrCryptKeys.lua sd1:/lua/GetNvrCryptKeyslog.txt
]]
local nvr5x0 = true
local filepath = "sd1:/lua/"
local logTmp = "GetNvrCryptKeyslog.txt"
local debugf = false
if not nvr5x0 then filepath = "" end
local tt1=0; tt2=0; tt3=0; tt4=0; tt5=0; tt6=0; tt7=0
local logData, logPPData, iip, rip, ispi, rspi, ieky, reky, dt, r, m
local cky_i, skeyid_e, modpf
local sendconf, receiveconf
logPPData=""
tt1 = os.time() -- begining
-- both modules are modified for NVR5x0 LUA
local sha1 = require "sha1" -- https://github.com/kikito/sha1.lua
local sha2 = require "sha256" -- https://github.com/jqqqi/Lua-HMAC-SHA256
-- hex to binary
local function hex2bin(str)
return (str:gsub('..', function (cc)
return string.char(tonumber(cc, 16))
end))
end
function loadLogFile(logfile)
local handle = io.open(logfile,"r")
local content=handle:read("*all")
handle:close()
return content
end
if arg[1] ~= nil then
logData = loadLogFile(arg[1])
else
r,m = rt.command("delete "..filepath..logTmp)
if r == nil then rt.syslog("info","[LUA] "..arg[0].." "..m ) end
r,m = rt.command("show status pp 1")
if r then
if m ~= nil then logPPData = m else logPPData = "" end
else rt.syslog("info","[LUA] "..arg[0].." "..m ) end
r,m = rt.command("show log | grep 'IKE|L2TP|ANONYMOUS01' >> "..filepath..logTmp)
if r ~= nil then
logData = loadLogFile(filepath..logTmp)
else
rt.syslog("info","[LUA] "..arg[0].." show log command error" )
return r,m
end
end
---- cut only last l2tp/ipsec connection data
logData = string.gsub(logData,".*%c([%d/: ]+: %[IKE%] respond ISAKMP phase to.-%[IKE%] respond IPsec phase to.*)","%1")
tt2 = os.time() -- logging
print("now picking keys data from log...\n")
-- find last IKE connection and get it the ip address and date
local pos = 1
while true do
local pbt,pet,iipt,dtt = string.find(logData, "%[IKE%] respond IPsec phase to ([%d%.]+)%s-([%d/: ]+): ", pos)
if pbt ~= nil then
pb=pbt; pe=pet; iip = iipt; dt = dtt; pos = pet
else
break
end
end
dt = string.gsub(dt,"[/: ]","")
print("Connection date -> "..dt)
-- SA crypt suite
-- syslog debug info. from ckIPSecConnect.lua
local ike1, ike2, ike3, esp1, esp2
for ike1t,ike2t,ike3t,esp1t,esp2t in string.gmatch(logData, "%[IKEex%] IKE: (.-), (.-), (.-bit).-%[IKEex%] ESP: (.-) %(for Auth%.: (.-)%)") do
if ike1t ~= nill then
ike1=ike1t; ike2=ike2t; ike3=ike3t
esp1=esp1t; esp2=esp2t
end
end
if ike1 ~= nil then print("IKE = " .. ike1 .. "/" .. ike2 .. "/" .. ike3 ); print("ESP = " .. esp1 .. "/" .. esp2 )
else print("No [IKEex] log data!") end
-- Local ID(Responder)/Remote ID(Initiator)
local idl, idr
for idlt,idrt in string.gmatch(logData, "%[IKEex%] Local ID: ([%d%.]-)%s.-%[IKEex%] Remote ID: ([%d%.]-)%s") do
if idlt ~= nill then
idl = idlt; idr = idrt
end
end
if debugf then print("Local ID(responder) = " .. idl ); print("Remote ID(initiator) = " .. idr ) end
-- IKE CKY-I / Skeyid_e
local tcky, eky1, eky2
for tcky,eky1,eky2 in string.gmatch(logData, "%[IKE%] CKY%-I%s.-%[IKE%] ([%x ]+)%s.-%[IKE%] SKEYID_e%s.-%[IKE%] ([%x ]+)%s.-%[IKE%] ([%x ]+)") do
if tcky ~= nil then
cky_i = string.gsub(tcky," ","")
skeyid_e = string.gsub(eky1," ","")..string.gsub(eky2," ","")
end
end
if debugf then print("isakmp cky_i="..cky_i) end
if debugf then print("isakmp skeyid_e="..skeyid_e) end
-- get Skeyid_d
local skeyid_d
local t1, t2
for t1, t2 in string.gmatch(logData, "%[IKE%] SKEYID_d%s.-%[IKE%] ([%x ]+)%s.-%[IKE%] ([%x ]+)%s") do
if t1 ~= nil then
skeyid_d = string.gsub(t1,"%s","")..string.gsub(t2,"%s","")
end
end
if debugf then print("sekyid_d = " .. skeyid_d ) end
-- get g(qm)^xy for PSF
local g_qm =""
for t1 in string.gmatch(logData, "%[IKE%] g%(qm%)%^xy%s.-%[IKE%] (.-%[IKE%]) protocol") do
if t1 ~= nil then
local t3 = string.match(t1, "(omitted)")
if t3 ~= nil then modpf = false else modpf = true end
t2 = string.gsub(t1,"%d-%/%d-%/%d- %d+%:%d+%:%d+%: %[IKE%]","")
g_qm = string.gsub(t2,"%s","")
end
end
if g_qm ~= "" then if modpf then print("pfs on") else print("pfs on but partialy omitted!") end end
if debugf then if g_qm ~= "" then if modpf then print("g_qm = " .. g_qm ) else print("above modp1024") end end end
if not modpf then g_qm = "" end
-- get protocol
local protocol
for t1 in string.gmatch(logData, "%[IKE%] protocol = (%d-)%s") do
if t1 ~= nil then
protocol = string.format("%02d", tonumber(t1, 16))
end
end
if debugf then print("protocol = " .. protocol ) end
-- get SPI_r and SPI_i
local spi_r, spi_i
for t1, t2 in string.gmatch(logData, "%[IKE%] SPI%s.-%[IKE%] ([%x ]+)%s.-%[IKE%] SPI%s.-%[IKE%] ([%x ]+)%s") do
if t1 ~= nil then
spi_r = string.gsub(t1, "%s", "")
spi_i = string.gsub(t2, "%s", "")
end
end
if debugf then print("SPI_i = " .. spi_i .. "\nSPI_r = " .. spi_r) end
-- get Ni_b
local ni_b
for t1 in string.gmatch(logData, "%[IKE%] Ni_b%s.-%[IKE%] (.-%[IKE%]) Nr_b") do
if t1 ~= nil then
t2 = string.gsub(t1,"%d-%/%d-%/%d- %d+%:%d+%:%d+%: %[IKE%]","")
ni_b = string.gsub(t2,"%s","")
end
end
if debugf then print("ni_b = " .. ni_b ) end
-- get Nr_b
local nr_b
for t1 in string.gmatch(logData, "%[IKE%] Nr_b%s.-%[IKE%] (.-%[IKE%]) key material") do
if t1 ~= nil then
t2 = string.gsub(t1,"%d-%/%d-%/%d- %d+%:%d+%:%d+%: %[IKE%]","")
nr_b = string.gsub(t2,"%s","")
end
end
if debugf then print("nr_b = " .. nr_b ) end
-- get the SPI and the encryption key of Responder
local spi,ekey
pb,pe,spi,ekey = string.find(logData,"%[IKE%] SPI%s.-%[IKE%] ([%w ]+).-%[IKE%] key material %(first 16byte%)%s.-%[IKE%] ([%w ]+)",pe)
if pb ~= nil then
rspi = spi
rekey = ekey
pos = pe
else
rt.syslog("info","[LUA] "..arg[0].." No Responder SA...")
end
-- get the SPI and the encryption key of Initiator
pb,pe,spi,ekey = string.find(logData,"%[IKE%] SPI%s.-%[IKE%] ([%w ]+).-%[IKE%] key material %(first 16byte%)%s.-%[IKE%] ([%w ]+)",pe)
if pb ~= nil then
ispi = spi
iekey = ekey
else
rt.syslog("info","[LUA] "..arg[0].." No Initiator SA...")
end
if rspi ~= nil then rspi=string.gsub(rspi," ","") end
if ispi ~= nil then ispi=string.gsub(ispi," ","") end
if rekey ~= nil then rekey=string.gsub(rekey," ","") end
if iekey ~= nil then iekey=string.gsub(iekey," ","") end
-- Get the Global IP Address of Responder
rip=string.match(logData,"PP IP Address Local: ([%d%.]+),")
print("")
tt3 = os.time() -- picking up data
---- IKE
local ikecryptkey
if ike1 == "AES-CBC" then
ikecryptkey = string.sub(skeyid_e, 1, 32)
elseif ike1 == "AES256-CBC" and ike2 == "SHA2-256" then
ikecryptkey = string.sub(skeyid_e, 1, 64)
elseif ike1 == "AES256-CBC" and ike2 == "SHA-1" then
local key1 = sha1.hmac( hex2bin( skeyid_e ), hex2bin("00") )
local key2 = sha1.hmac( hex2bin( skeyid_e ), hex2bin( key1 ) )
ikecryptkey = string.sub( key1 .. key2, 1, 64 )
else
print("IKE key is NOT AES")
end
tt4 = os.time() -- sha1.hmac for ike mt2
if ikecryptkey ~= nil then print("IKE crypt key = " .. ikecryptkey) end
if skeyid_e ~= nil then print("loged IKE key = " .. skeyid_e ) end
---- ESP
local espcryptkey_i, espcryptkey_r, km1_i, km1_r, km2_i, km2_r
tt5 = os.time() -- sha1.hmac for esp mt1
if ike2 == "SHA-1" then
km1_i = sha1.hmac( hex2bin(skeyid_d), hex2bin(g_qm .. protocol .. spi_i .. ni_b .. nr_b) )
km1_r = sha1.hmac( hex2bin(skeyid_d), hex2bin(g_qm .. protocol .. spi_r .. ni_b .. nr_b) )
if esp1 == "AES-CBC" then
espcryptkey_i = string.sub( km1_i, 1,32 )
espcryptkey_r = string.sub( km1_r, 1,32 )
elseif esp1 == "AES256-CBC" then
km2_i = sha1.hmac( hex2bin(skeyid_d), hex2bin(km1_i .. g_qm .. protocol .. spi_i .. ni_b .. nr_b) )
km2_r = sha1.hmac( hex2bin(skeyid_d), hex2bin(km1_r .. g_qm .. protocol .. spi_r .. ni_b .. nr_b) )
espcryptkey_i = string.sub( km1_i .. km2_i, 1,64 )
espcryptkey_r = string.sub( km1_r .. km2_r, 1,64 )
else
print("ESP key is NOT AES")
end
elseif ike2 == "SHA2-256" then
km1_i = sha2.hmac_sha256( hex2bin(skeyid_d), hex2bin(g_qm .. protocol .. spi_i .. ni_b .. nr_b) )
km1_r = sha2.hmac_sha256( hex2bin(skeyid_d), hex2bin(g_qm .. protocol .. spi_r .. ni_b .. nr_b) )
if esp1 == "AES-CBC" then
espcryptkey_i = string.sub( km1_i, 1,32 )
espcryptkey_r = string.sub( km1_r, 1,32 )
elseif esp1 == "AES256-CBC" then
espcryptkey_i = string.sub( km1_i, 1,64 )
espcryptkey_r = string.sub( km1_r, 1,64 )
else
print("ESP key is NOT AES")
end
else
print("ESP key is NOT SHA-1 or SHA2-256")
end
tt6 = os.time() -- for esp mt2
print("")
if espcryptkey_i ~= nil then print("ESP crypt key(Initiator) = " .. espcryptkey_i ) end
if iekey ~= nil then print("loged ESP key(initiator) = " .. iekey ) end
print("")
if espcryptkey_r ~= nil then print("ESP crypt key(Responder) = " .. espcryptkey_r ) end
if rekey ~= nil then print("loged ESP key(responder) = " .. rekey ) end
-- ikev1_decryption_table file for Wire Shark
if not ( cky_i == nil or ikecryptkey == nil ) then
sendconf=string.format("%s,%s\n",cky_i, ikecryptkey)
local fh,estr,ecode = io.open(filepath..dt.."ikev1_decryption_table","w")
if fh == nil then
-- fh:close()
return nil,estr,ecode
else
fh:write(sendconf)
fh:close()
end
end
--- esp_sa file for Wire Shark
--- "AES-CBC [RFC3602]" for aes128, aes256
--- "HMAC-SHA-1-96 [RFC2404]"/"HMAC-SHA-256-128 [RFC4868]"
if not ( idl == nil or idr == nil or spi_i == nil or spi_r == nil ) then
if esp2 == "HMAC-SHA" then
sendconf=string.format("%q,%q,%q,%q,%q,%q,%q,%q\n","IPv4",idl,idr,"0x"..spi_r,"AES-CBC [RFC3602]","0x"..espcryptkey_r,"HMAC-SHA-1-96 [RFC2404]","")
receiveconf=string.format("%q,%q,%q,%q,%q,%q,%q,%q\n","IPv4",idr,idl,"0x"..spi_i,"AES-CBC [RFC3602]","0x"..espcryptkey_i,"HMAC-SHA-1-96 [RFC2404]","")
elseif esp2 == "HMAC-SHA256" then
sendconf=string.format("%q,%q,%q,%q,%q,%q,%q,%q\n","IPv4",idl,idr,"0x"..spi_r,"AES-CBC [RFC3602]","0x"..espcryptkey_r,"HMAC-SHA-256-128 [RFC4868]","")
receiveconf=string.format("%q,%q,%q,%q,%q,%q,%q,%q\n","IPv4",idr,idl,"0x"..spi_i,"AES-CBC [RFC3602]","0x"..espcryptkey_i,"HMAC-SHA-256-128 [RFC4868]","")
else
sendconf=string.format("%q,%q,%q,%q,%q,%q,%q,%q\n","IPv4",idl,idr,"0x"..spi_r,"AES-CBC [RFC3602]","0x"..espcryptkey_r,"","")
receiveconf=string.format("%q,%q,%q,%q,%q,%q,%q,%q\n","IPv4",idr,idl,"0x"..spi_i,"AES-CBC [RFC3602]","0x"..espcryptkey_i,"","")
end
fh,estr,ecode = io.open(filepath..dt.."esp_sa","w")
if fh == nil then
-- fh:close()
return nil,estr,ecode
else
fh:write(sendconf)
fh:write(receiveconf)
fh:close()
end
end
--- rename log file
fh,estr,ecode=io.open(filepath..logTmp,"r")
if fh == nil then
-- fh:close()
print("\nNO "..filepath..logTmp)
else
fh:close()
rslt,txt=os.rename(filepath..logTmp,filepath..dt..logTmp)
if rslt == false then print(txt) end
end
--- overwrite log file with last log data
fh,estr,ecode = io.open(filepath .. dt .. logTmp,"w")
if fh == nil then
-- fh:close()
return nil,estr,ecode
else
fh:write(logPPData .. logData)
fh:close()
end
tt7 = os.time()
---
print("")
print("log file = " .. os.difftime(tt2, tt1) .. " sec" )
print("picking up data = " .. os.difftime(tt3, tt2) .. " sec" )
print("Calculation key (IKE) = " .. os.difftime(tt4, tt3) .. " sec" )
print("Calculation key (ESP) = " .. os.difftime(tt6, tt5) .. " sec" )
print("Total time = " .. os.difftime(tt7, tt1) .. " sec")