2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-16 10:46:03 +00:00

10_EnOcean: secure functions: add SEC_CDM and RLC 32 bit support, CDM function modified

git-svn-id: https://svn.fhem.de/fhem/trunk@21291 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
klaus.schauer 2020-02-27 11:43:35 +00:00
parent 50447fd9cc
commit ef703ae5ba

View File

@ -69,6 +69,7 @@ my %EnO_rorgname = (
"30" => "SEC", # secure telegram
"31" => "ENC", # secure telegram with encapsulation
"32" => "SECD", # decrypted secure telegram
"33" => "SECCDM", # secure chained data message
"35" => "STE", # secure Teach-In
"40" => "CDM", # chained data message
);
@ -785,7 +786,7 @@ EnOcean_Initialize($)
"pidFactor_P pidIPortionCallBeforeSetting pidSensorTimeout " .
"pollInterval postmasterID productID rampTime rcvRespAction ".
"releasedChannel:A,B,C,D,I,0,auto repeatingAllowed:yes,no remoteCode remoteEEP remoteID remoteManufID " .
"remoteManagement:client,manager,off rlcAlgo:no,2++,3++ rlcRcv rlcSnd rlcTX:true,false " .
"remoteManagement:client,manager,off rlcAlgo:no,2++,3++,4++ rlcRcv rlcSnd rlcTX:true,false " .
"reposition:directly,opens,closes rltRepeat:16,32,64,128,256 rltType:1BS,4BS " .
"scaleDecimals:0,1,2,3,4,5,6,7,8,9 scaleMax scaleMin secMode:rcv,snd,bidir " .
"secLevel:encapsulation,encryption,off sendDevStatus:no,yes sendTimePeriodic sensorMode:switch,pushbutton " .
@ -6938,8 +6939,61 @@ sub EnOcean_Parse($$)
return "";
}
if ($rorg eq "33") {
# secure chained data message (SEC_CDM)
#Log3 $IODev, 5, "EnOcean $senderID received via $IODev SEC_CDM: $msg";
$data =~ m/^(..)(.*)$/;
# SEQ evaluation?
my ($seq, $idx) = (hex($1) & 0xC0, hex($1) & 0x3F);
$data = $2;
if ($idx == 0) {
# first message part
delete $iohash->{helper}{"sec_cdm_$senderID-$seq"};
$data =~ m/^(....)(.*)$/;
$iohash->{helper}{"sec_cdm_$senderID-$seq"}{len} = hex($1);
$iohash->{helper}{"sec_cdm_$senderID-$seq"}{rorg} = '31';
$iohash->{helper}{"sec_cdm_$senderID-$seq"}{data}{$idx} = $2;
$iohash->{helper}{"sec_cdm_$senderID-$seq"}{lenCounter} = length($2) / 2;
RemoveInternalTimer($iohash->{helper}{timer}{"helperClear_sec_$senderID-$seq"}) if(exists $iohash->{helper}{timer}{"helperClear_sec_$senderID-$seq"});
$iohash->{helper}{timer}{"helperClear_sec_$senderID-$seq"} = {hash => $iohash, function => "sec_cdm_$senderID-$seq"};
InternalTimer(gettimeofday() + 3, 'EnOcean_helperClear', $iohash->{helper}{timer}{"helperClear_sec_$senderID-$seq"}, 0);
#Log3 $IODev, 5, "EnOcean $senderID SEC_CDM IDX: $idx DATA: " . $iohash->{helper}{"sec_cdm_$senderID-$seq"}{data}{$idx};
return $IODev;
} else {
if (!exists($iohash->{helper}{"sec_cdm_$senderID-$seq"})) {
Log3 $IODev, 4, "EnOcean $senderID SEC_CDM sequence error";
return $IODev;
}
$iohash->{helper}{"sec_cdm_$senderID-$seq"}{data}{$idx} = $data;
$iohash->{helper}{"sec_cdm_$senderID-$seq"}{lenCounter} += length($data) / 2;
#Log3 $IODev, 5, "EnOcean $senderID SEC_CDM IDX: $idx DATA: " . $iohash->{helper}{"sec_cdm_$senderID-$seq"}{data}{$idx};
}
if ($iohash->{helper}{"sec_cdm_$senderID-$seq"}{lenCounter} >= $iohash->{helper}{"sec_cdm_$senderID-$seq"}{len}) {
# data message complete
# reconstruct RORG, DATA
my ($idx, $dataPart, @data);
while (($idx, $dataPart) = each(%{$iohash->{helper}{"sec_cdm_$senderID-$seq"}{data}})) {
$data[$idx] = $iohash->{helper}{"sec_cdm_$senderID-$seq"}{data}{$idx};
}
$data = join('', @data);
$msg[3] = $data;
$rorg = $iohash->{helper}{"sec_cdm_$senderID-$seq"}{rorg};
$msg[2] = $rorg;
$msg = join(':', @msg);
$rorgname = $EnO_rorgname{$rorg};
delete $iohash->{helper}{"sec_cdm_$senderID-$seq"};
RemoveInternalTimer($iohash->{helper}{timer}{"helperClear_sec_$senderID-$seq"}) if(exists $iohash->{helper}{timer}{"helperClear_sec_$senderID-$seq"});
delete $iohash->{helper}{timer}{"helperClear_sec_$senderID-$seq"} if (exists $iohash->{helper}{timer}{"helperClear_sec_$senderID-$seq"});
#Log3 $IODev, 5, "EnOcean $senderID SEC_CDM concatenated DATA: $data";
} else {
# wait for next data message part
return $IODev;
}
}
if ($rorg eq "40") {
# chained data message (CDM)
#Log3 $IODev, 5, "EnOcean received via $IODev CDM: $msg";
$data =~ m/^(..)(.*)$/;
# SEQ evaluation?
my ($seq, $idx) = (hex($1) & 0xC0, hex($1) & 0x3F);
@ -6955,10 +7009,16 @@ sub EnOcean_Parse($$)
RemoveInternalTimer($iohash->{helper}{timer}{"helperClear_$senderID-$seq"}) if(exists $iohash->{helper}{timer}{"helperClear_$senderID-$seq"});
$iohash->{helper}{timer}{"helperClear_$senderID-$seq"} = {hash => $iohash, function => "cdm_$senderID-$seq"};
InternalTimer(gettimeofday() + 3, 'EnOcean_helperClear', $iohash->{helper}{timer}{"helperClear_$senderID-$seq"}, 0);
#Log3 $IODev, 3, "EnOcean $IODev CDM timer started";
#Log3 $IODev, 5, "EnOcean $IODev CDM IDX: $idx DATA: " . $iohash->{helper}{"cdm_$senderID-$seq"}{data}{$idx};
return $IODev;
} else {
if (!exists($iohash->{helper}{"cdm_$senderID-$seq"})) {
Log3 $IODev, 4, "EnOcean $senderID CDM sequence error";
return $IODev;
}
$iohash->{helper}{"cdm_$senderID-$seq"}{data}{$idx} = $data;
$iohash->{helper}{"cdm_$senderID-$seq"}{lenCounter} += length($data) / 2;
#Log3 $IODev, 5, "EnOcean $IODev CDM IDX: $idx DATA: " . $iohash->{helper}{"cdm_$senderID-$seq"}{data}{$idx};
}
if ($iohash->{helper}{"cdm_$senderID-$seq"}{lenCounter} >= $iohash->{helper}{"cdm_$senderID-$seq"}{len}) {
# data message complete
@ -6980,7 +7040,7 @@ sub EnOcean_Parse($$)
delete $iohash->{helper}{"cdm_$senderID-$seq"};
RemoveInternalTimer($iohash->{helper}{timer}{"helperClear_$senderID-$seq"}) if(exists $iohash->{helper}{timer}{"helperClear_$senderID-$seq"});
delete $iohash->{helper}{timer}{"helperClear_$senderID-$seq"} if (exists $iohash->{helper}{timer}{"helperClear_$senderID-$seq"});
#Log3 $IODev, 5, "EnOcean $IODev CDM RORG: $rorg concatenated DATA $data";
#Log3 $IODev, 5, "EnOcean $IODev CDM RORG: $rorg concatenated DATA: $data";
} else {
# wait for next data message part
return $IODev;
@ -13885,6 +13945,8 @@ sub EnOcean_Attr(@)
} elsif (AttrVal($name, "rlcAlgo", "") eq "3++" && $attrVal =~ m/^[\dA-Fa-f]{6}$/) {
} elsif (AttrVal($name, "rlcAlgo", "") eq "4++" && $attrVal =~ m/^[\dA-Fa-f]{8}$/) {
} else {
$err = "attribute-value [$attrName] = $attrVal wrong";
}
@ -13892,7 +13954,7 @@ sub EnOcean_Attr(@)
} elsif ($attrName eq "rlcAlgo") {
if (!defined $attrVal){
} elsif ($attrVal !~ m/^no|2++|3++$/) {
} elsif ($attrVal !~ m/^no|2++|3++|4++$/) {
$err = "attribute-value [$attrName] = $attrVal wrong";
}
@ -16924,59 +16986,61 @@ sub EnOcean_sec_parseTeachIn($$$$) {
}
# Decode RLC algorithm and extract RLC and private key (only first part most likely)
if ($rlc_algo == 0) {
# No RLC used in telegram or internally in memory, use case untested
return ("Secure modes without RLC not tested or supported", undef);
} elsif ($rlc_algo == 1) {
# "RLC= 2-byte long. RLC algorithm consists on incrementing in +1 the previous RLC value
if ($rlc_algo == 0) {
# No RLC used in telegram or internally in memory, use case untested
return ("Secure modes without RLC not tested or supported", undef);
} elsif ($rlc_algo == 1) {
# "RLC= 2-byte long. RLC algorithm consists on incrementing in +1 the previous RLC value
# Extract RLC and KEY fields from data trailing SLF field
# RLC, KEY, ID, STATUS
$crypt =~ /^(....)(.*)$/;
$rlc = $1;
$key1 = $2;
# Store in device hash
$attr{$name}{rlcAlgo} = '2++';
readingsSingleUpdate($hash, ".rlcRcv", $rlc, 0);
# storing backup copy
$attr{$name}{rlcRcv} = $rlc;
$attr{$name}{keyRcv} = $key1;
} elsif ($rlc_algo == 2) {
# RLC= 3-byte long. RLC algorithm consists on incrementing in +1 the previous RLC value
# Extract RLC and KEY fields from data trailing SLF field
# RLC, KEY, ID, STATUS
$crypt =~ /^(......)(.*)$/;
$rlc = $1;
$key1 = $2;
# Store in device hash
$attr{$name}{rlcAlgo} = '3++';
readingsSingleUpdate($hash, ".rlcRcv", $rlc, 0);
# storing backup copy
$attr{$name}{rlcRcv} = $rlc;
$attr{$name}{keyRcv} = $key1;
} elsif ($rlc_algo == 3) {
# RLC= 4-byte long. RLC algorithm consists on incrementing in +1 the previous RLC value
# Extract RLC and KEY fields from data trailing SLF field
# RLC, KEY, ID, STATUS
$crypt =~ /^(........)(.*)$/;
$rlc = $1;
$key1 = $2;
# Store in device hash
$attr{$name}{rlcAlgo} = '4++';
readingsSingleUpdate($hash, ".rlcRcv", $rlc, 0);
# storing backup copy
$attr{$name}{rlcRcv} = $rlc;
$attr{$name}{keyRcv} = $key1;
} else {
# Undefined RLC algorithm
return ("Undefined RLC algorithm $rlc_algo", undef);
}
# Extract RLC and KEY fields from data trailing SLF field
# RLC, KEY, ID, STATUS
$crypt =~ /^(....)(.*)$/;
$rlc = $1;
$key1 = $2;
#print "RLC: $rlc\n";
#print "Part 1 of KEY: $key1\n";
# Store in device hash
$attr{$name}{rlcAlgo} = '2++';
readingsSingleUpdate($hash, ".rlcRcv", $rlc, 0);
# storing backup copy
$attr{$name}{rlcRcv} = $rlc;
$attr{$name}{keyRcv} = $key1;
} elsif ($rlc_algo == 2) {
# RLC= 3-byte long. RLC algorithm consists on incrementing in +1 the previous RLC value
# Extract RLC and KEY fields from data trailing SLF field
# RLC, KEY, ID, STATUS
$crypt =~ /^(......)(.*)$/;
$rlc = $1;
$key1 = $2;
#print "RLC: $rlc\n";
#print "Part 1 of KEY: $key1\n";
# Store in device hash
$attr{$name}{rlcAlgo} = '3++';
readingsSingleUpdate($hash, ".rlcRcv", $rlc, 0);
# storing backup copy
$attr{$name}{rlcRcv} = $rlc;
$attr{$name}{keyRcv} = $key1;
} else {
# Undefined RLC algorithm
return ("Undefined RLC algorithm $rlc_algo", undef);
}
# RLC Transmission
if ($rlc_tx == 0 ) {
# Secure operation mode telegrams do not contain RLC, we store and track it ourself
$attr{$name}{rlcTX} = 'false';
} else {
# Secure operation mode messages contain RLC, CAUTION untested
$attr{$name}{rlcTX} = 'true';
}
# RLC Transmission
if ($rlc_tx == 0 ) {
# Secure operation mode telegrams do not contain RLC, we store and track it ourself
$attr{$name}{rlcTX} = 'false';
} else {
# Secure operation mode messages contain RLC, CAUTION untested
$attr{$name}{rlcTX} = 'true';
}
# Decode MAC Algorithm
if ($mac_algo == 0) {
@ -17141,7 +17205,6 @@ sub EnOcean_sec_getRLC($$$$) {
# Boundary check
if ($attr{$name}{rlcAlgo} eq '2++') {
if ($new_rlc > 65535) {
#print "RLC rollover\n";
Log3 $name, 5, "EnOcean $name EnOcean_sec_getRLC RLC rollover";
$new_rlc = 0;
$attr{$name}{$rlcVar} = "0000";
@ -17151,7 +17214,6 @@ sub EnOcean_sec_getRLC($$$$) {
$attr{$name}{$rlcVar} = uc(unpack('H4',pack('n', $new_rlc)));
} elsif ($attr{$name}{rlcAlgo} eq '3++') {
if ($new_rlc > 16777215) {
#print "RLC rollover\n";
Log3 $name, 5, "EnOcean $name EnOcean_sec_getRLC RLC rollover";
$new_rlc = 0;
$attr{$name}{$rlcVar} = "000000";
@ -17159,6 +17221,15 @@ sub EnOcean_sec_getRLC($$$$) {
}
readingsSingleUpdate($hash, "." . $rlcVar, sprintf("%06X", $new_rlc), 0);
$attr{$name}{$rlcVar} = sprintf("%06X", $new_rlc);
} elsif ($attr{$name}{rlcAlgo} eq '4++') {
if ($new_rlc > 4294967295) {
Log3 $name, 5, "EnOcean $name EnOcean_sec_getRLC RLC rollover";
$new_rlc = 0;
$attr{$name}{$rlcVar} = "00000000";
EnOcean_CommandSave(undef, undef);
}
readingsSingleUpdate($hash, "." . $rlcVar, sprintf("%08X", $new_rlc), 0);
$attr{$name}{$rlcVar} = sprintf("%08X", $new_rlc);
}
Log3 $name, 5, "EnOcean $name EnOcean_sec_getRLC RLC new: $attr{$name}{$rlcVar} $new_rlc";
@ -17290,6 +17361,9 @@ sub EnOcean_sec_convertToNonsecure($$$) {
} elsif ($attr{$name}{rlcAlgo} eq '3++') {
$crypt_pattern .= "(......)";
$expect_rlc = 1;
} elsif ($attr{$name}{rlcAlgo} eq '4++') {
$crypt_pattern .= "(........)";
$expect_rlc = 1;
} else {
# RLC_TX but no info on RLC length
return ("RLC_TX and RLC_ALGO inconsistent", undef, undef);