MEGOLDVA - perl változóban az adat megbuggyan

Vagy én buggyantam meg?

Szervusztok!

Egy Crypt::CBC-vel encrypt-ált adatot feltolok Net::FTP vel majd ezt az adatot szedem le File::Fetch-el direktbe egy string típusú változóba amit szeretnék decryptálni. Az eredeti adat nem áll viszont elő. Elkezdtem piszkálni és érdekes eredményre jutottam.
A vett adatokat ha file-ba írom, majd visszaolvasom minden megjavul. Az md5 összege viszont nem változik!


adat - encrypt - ftp - netSzerver - get - $ciphertext - decrypt - szemét

adat - encrypt - ftp - netSzerver - get - $ciphertext \  /  nemA$ciphertextVáltozó - decrypt - adat
                                                      file

Az md5 összegeket Digest::MD5-el állítom elő, és az megegyezik a a file írás - olvasás művelet előtt/után.
Itt jutott eszembe hogy valami kódlap váltás zűr lehet talán? Kínosan ügyelve átírok mindent :raw-ra, de semmi.
Próbaképp a letöltést átírtam LWP::Simple::get-re de sajnos ez sem jött be.
Mitől változik meg az adat és hol?
A hozzászólásokat nagyon köszönöm előre is!
vfero

Hozzászólások


use English;
use strict;
use locale;
use warnings;
use Safe;
use Config;

use Data::Dumper;
use Crypt::CBC;         # libcrypt-cbc-perl
use LWP::Simple;
use Digest::MD5 qw(md5_hex);
use MIME::Base64 qw( encode_base64 decode_base64 );

my $url=sprintf("http://www.%s/%s/%s",$webServerAddress,$webDirectory,$publicFileName);
my $ciphertext = get($url);

if ($debug){
  open(my $FH, '>', "/tmp/ciphertext" );
  print $FH $ciphertext;
  close $FH or warn $!;

  open(my $FH, '<', "/tmp/ciphertext" );
  read ($FH,$c,40960);
  close $FH or warn $!;
  
  $ciphertext = $c;
  }

my $string = Crypt::CBC->new(
    -cipher      => 'Blowfish',
    -header      => 'none',
    -literal_key => 1,
    -key         => "x"x56,
    -iv          => "x"x8
)->decrypt($ciphertext);

$debug = 0 esetén a kimenet szemét
$debug = 1 esetén értelmes adatot kapok
(a hibakezelés és egyéb részeket szándékosan csonkoltam)
vfero

Na most a leírásban File::Fetch van, itt viszont, LWP::Simple. Mellesleg ez a kód amit ide bepasztáztál strict módban hibával elhasal, lévén $c nem létezik a kontextusában. Azt gonodolnám, hogy amikor változóba töltve próbálod dekódolni a file-t van az adat végén egy új sor karakter, amit beolvasáskor nem olvasol el.
Szerintem nézd meg, van-e a végén új sor karakter.

----
올드보이
http://molnaristvan.eu/

Megpróbáltam, de sajnos nem. md5 szerint is stimmelnek.

Így néz most ki:


#############################################
#                                           #
# workaround                                #
#                                           #
#############                               #
if (1){                                     #
  open(my $FH, '>:raw', "/tmp/ciphertext" );#
  print $FH $ciphertext;                    #
  close $FH;                                #
                                            #
  open($FH, '<:raw', "/tmp/ciphertext" );   #
  read ($FH,$c,40960);                      #
  close $FH;                                #
                                            #
  $ciphertext = $c;                         #
  }                                         #
#############################################

Az md5-t hol és hogyan ellenőrzöd?
Nem lehet, hogy már az az ellenőrzés is hibás?
Rég használtam perlt, szóval csak tipp, de mintha azzal a readdel megváltozna a változód hossza.
És ahogy a google találatokat itt félálomban elnézem, a decryptnek mintha nem lenne mindegy, hogy hány bináris nulla van a végén (tévedés joga fenntartva)

Nekem még mindég zavaros a dolog. Amikor még nem tudott a syslog-ng (csak pénzért) titkostott kimenetet csináltam egy logfile titkosito scriptet: https://github.com/kayapo/encryptedlogstore esetleg segit az áttekintése (tudom más annyiban, hogy Crypt::OpenSSL::RSA modullal titkost).

Kódolni és dekódolni a fileokat csak akkora darabonként szabad ami még a cipher chunk méretébe bele fér, ez függ a kódoló eljárásától (de ezt gondolom úgy is olvastad a Crypt doksijában)

Még annyi, hpgy a file tartalmát és a változóban lévő adatot, próbáltad már kiritani a képernyőre?

----
올드보이
http://molnaristvan.eu/

Továbbra is piszkálja a csőröm a téma.
Mondd, meg tudod csinálni, hogy az itt még if($debug)-ként induló blokkban a $ciphertext = $c; előtt binárisan összehasonlítod a $cipertext és a $c értékét?
Kíváncsi lennék, mit kapsz.
(szóval nem az, hogy eltérnek-e, hanem, hogy pontosan miben térnek el - sajnos túl rég használtam perlt, nem emlékszem, ezt mivel lehetne megoldani :( )

Kicsontoztam, de az most működik. Még nem tudom hogy mi a különbség, ami elrontja, de jelentkezem majd még. Most alszom, csak utána rágódok a gumicsonton. :S
Addig is köszönöm mindenkinek!


#!/usr/bin/perl 
# -d 

use English;
#use strict;
use locale;
#use utf8;
use warnings;
use Safe;
use Config;

use Data::Dumper;
use Crypt::CBC;         # libcrypt-cbc-perl, libcrypt-blowfish-perl
use LWP::Simple;        # libwww-perl
use Digest::MD5 qw(md5_hex);
use MIME::Base64 qw( encode_base64 decode_base64 );
###############################################################################
local $| = 1;           # auto flush

my $c="";
my $ciphertext = get("http://users.atw.hu/vfero/hup/common");

if ( ! defined $ciphertext) {
    print " not found\n";
    exit 1;
    }
#############################################
#                                           #
# workaround                                #
#                                           #
#############                               #
if(0){                                      #
  open(my $FH, '>:raw', "/tmp/ciphertext"); #
  print $FH $ciphertext;                    #
  close $FH or warn $!;                     #
                                            #
  open($FH, '<:raw', "/tmp/ciphertext");    #
  read ($FH,$c,40960);                      #
  close $FH or warn $!;                     #
                                            #
  $ciphertext = $c;                         #
  }                                         #
#############################################

if(0){
  printf("ciphertext: %s\n",md5_hex($ciphertext)); # md5
  printf("c         : %s\n",md5_hex($c));          # md5
  }

my $string = Crypt::CBC->new(
    -cipher      => 'Blowfish',
    -header      => 'none',
    -literal_key => 1,
    -key         => "x"x56,
    -iv          => "o"x8
)->decrypt($ciphertext);

#print $string; exit ;

# reload dumped data
undef $/;
eval $string;

printf("%s\n%s\n",
    $h{"hello"},
    $h{"hup"}
);
exit;
__END__
###############################################################################

Szerintem te ezt tűlspiráztad, nekem a lentebbi kód gond nélkül megy:


#!/usr/bin/perl -w

use strict;
use LWP::Simple;
use Crypt::CBC;

local $| = 1;

my $hashed = get("http://users.atw.hu/vfero/hup/common");
exit 1 unless defined $hashed;

my $co = Crypt::CBC->new(
    -Cipher => 'Blowfish',
    -header => 'none',
    -literal_key => 1,
    -key => "x"x56,
    -iv => "o"x8
);

my $dpd = $co->decrypt($hashed);

print "$dpd\n";

A file tartalma egy %h nevű hash abban meg egy vicc van.

----
올드보이
http://molnaristvan.eu/

A probléma, hogy az LWP::Simple get() függvénye a karakterkódolás figyelembe vételével adja vissza a tartalmat, miközben Te bináris fájlt töltesz le, de nem application/octet-stream Content-Type-pal.

Ehelyett ha saját magad használod az LWP-t, akkor megoldható, hogy a Content-Type charsetet ne vegye figyelembe:


use LWP::UserAgent;

sub get {
my ($url) = @_;

my $ua = LWP::UserAgent->new;
$ua->timeout(10);
$ua->env_proxy;

my $response = $ua->get($url);

if ($response->is_success) {
return $response->decoded_content(charset => 'none');
}
else {
die $response->status_line;
}
}

Ha már így kérded, igen, végigolvastam.

Ha részletesebb magyarázatot szeretnél, akkor íme:

Az LWP::Simple szintén az LWP-t használja, a választ tartalmazó HTTP::Response objektum a decoded_content() függvényével adja vissza a letöltött tartalmat. A decoded_content valójában a HTTP::Message modulban van megvalósítva. A kapott választ a Content-Type válaszfejlécben szereplő karakterkódolással, annak hiányában alapértelemezetten a "natív" "ISO-8859-1" kódolással értelmezi és ha van benne az \x00-\x7f tartomány kívül is karakter, akkor az utf8::upgrade függvénnyel alakítja a perl belső unicode string formátumára.

A mi esetünkben a letöltött tartalom bináris, szinte biztosan van benne 7 biten kívül eső karakter, ezért szinte biztosan unicode stringként kapjuk vissza (hint: Encode::is_utf8 függvénnyel tesztelhető). Amikor :raw módoban fájlba írjuk ki, akkor a unicode string visszaalkításra kerül egyszerű ISO-8859-1 kódolt bájtszekvenciává. Utána ezt a bájtszekvenciát :raw módoban olvassuk vissza, akkor már nem unicode stringet kapunk eredményül, hanem megmarad egyszerű bájtszekvenicaként, ISO-8859-1 stringként.

A Crypt::CBC decrypt függvénye kapott adatot pack() függvénnyel és "a" templatetel alakítja paddolt bájt blokkokká. Ha unicode stringet kap paraméterül, akkor a pack "a" templateje csak az alsó 8 bitjét veszi figyelembe az egyes unicode karaktereknek és csak ezeket használja adatként a kódoláshoz. Ha viszont korrektül a bájtszekvenciát kapja meg, akkor a unicode karakterek UTF-8 bájtszekvenciáját kódolja el.

Ezért lényeges, hogy kódoláshoz sose használjunk perles unicode stringet, hanem mindig csak szigorúan bájtszekvenciát (a kívánt kódolás mellett). Bináris adatot pedig végképp szerencsétlen unicode stringként tárolni, hiszen nincsen és sosem volt karakterkódolása, ami alapján értelmezni lehetett volna benne a karaktereket (hát hacsaknem tényleg a latin1/iso-8859-1). Ezért javasolom, hogy kényszerítsük az LWP tartalomnál a karakterkódolás ignorálását és kérjük szépen csak vissza a kapott bájtsorozatot, hiszen valójában erre van szükségünk. Utána már ennek a decryptálása is helyes lesz.

Mondom, a kíírás elött még megvan neki a perles unicode flagje és a perl belső unicode ábrázolásában van tárolva. A :raw kiírás fájlba ezt ISO-8859-1-es kódlap szerinti bájtszekvenciává alakítja és ez kerül bele a fájlba. A :raw visszaolvasásnál már nem unicode string lesz az visszaolvasott string, hanem megmarad egyszerű bájtszekvenciaként (unicode flag nélkül).

A pack függvény másképp kezeli a unicode string (tehát perles unicode flag aktív, több bájtos karakterekből áll a string) karaktereit, meg a nem unicode string (perles unicode flag nem aktív, egyszerű bájszekvencia) karaktereit (ezek egyszűen bájtszekvenciák egyes bájtjai).

Az Encode::is_utf8 függvénye megmondja egy stringről, hogy az a perl belső unicode ábrázolásában van tárolva, vagy egyszerű hagyományos bájtszekvenciaként.

A perl ráadásul a unicode stringet különböző I/O műveleteknél beállításoktól függően konvertálja valamilyen karakterkódolás szerinti bájtszekvenciává (ez lehet ISO-8859-1, de akár UTF-8 is). Ha Te expliciten nem alakítod bájtszekvenciává, vagy nem sikerült a perlt megfelelően beállítanod, hogy automatikusan megfelelően végezze el az átkódolást, akkor érdekes és kellemetlen meglepetésekben lehet részed.

Input műveleteknél érdemes a stringeket az input kódolásáról a perl belső unicode formátumára alakítani, míg kiírásnál pedig érdemes a kívánt kódolás szerinti bájtszakvenciává alakítani és azt kiírni. Egyes bináris adatokon végzett műveleteknél (hashes, titkosítás) szintén javasolt a kívánt kódolás szerinti bájtszekvenciává alakítani a stringet és azon elvégezni a műveletet, különben eléggé undefined lesz az eredmény. Lásd a topic problémáját.

Szervusztok!

Köszönöm mindenkinem a munkáját, azt hiszem lehet benne valami amit Xanco írt!

Külön köszönöm a szabatos magyarázatot is, idővel talán meg is fogom érteni. ;)
Az adat folyamot base64-re módosítottam, és így kerül fel, majd vissza. Ez így már az eredeti környezetben is elsőre megy hiba nélkül.
A javasolt ISO-8859-1 átkódolás is működik, de ez valóban erősnek tűnik.
Kösz +1x!
Páka,
vfero