joec_ has asked for the wisdom of the Perl Monks concerning the following question:

Hi,

I am just going to throw this out there as i have no idea as to whether this is even possible in Perl or whether (most likely) i have the complete and utter wrong end of the stick!

I have a value that has been encoded using Base64 encoding from another application (c#). According to my colleague, this value, when decoded should return a byte array - are they even possible in perl?

Anyway, what i have to do is: take the second byte of this array and XOR it with the first, to return the original value. Then XOR the third with the second and so on and thus construct a string.

Would anyone have any ideas?

Is this going to be possible?

Thanks in advance - Joe.

Replies are listed 'Best First'.
Re: Base64 and byte arrays
by ikegami (Patriarch) on Feb 05, 2009 at 00:35 UTC

    Strings are internally stored "C" byte arrays. You can even do some traditionally numerical operations on them.

    use MIME::Base64 qw( decode_base64 ); my $scrambled = decode_base64($_); my $orig = substr($scrambled, 0, -1) ^ substr($scrambled, 1);

    But if you prefer to deal with an array of numbers, no problem! ord converts allows a character to be treated as a number, and chr does the inverse.

    use MIME::Base64 qw( decode_base64 ); my $scrambled = decode_base64($_); my @scrambled = map ord, split //, $scrambled; my @orig = map { $scrambled[$_-1] ^ $scrambled[$_] } 1..$#scrambl +ed; my $orig = join '', map chr, @orig;
      thank you thank you thank you! your first example did precisely what i needed!

      No need to split and then rejoin the string:

      use MIME::Base64 qw( decode_base64 ); ( my $orig = decode_base64( $_ ) ) =~ s/(.)(?=(.))/ $1 ^ $2 /seg;

        If you're going to use string xor, why do it a character at a time? The point of the second snippet was to show how to get an array of bytes for future reference.

        And your solution doesn't work. It appends a junk character.

      Or, so that they don't feel left out, pack and unpack:

      use MIME::Base64 qw( decode_base64 ); my $scrambled = decode_base64($_); my @scrambled = unpack('C*', $scrambled) ; my @orig = map { $scrambled[$_-1] ^ $scrambled[$_] } 1..$#scram +bled; my $orig = pack('C*', @orig) ;
      In this case the magic byte-wise string xor (^) is clearly the best bet. But in general I'd use pack/unpack to do string <-> byte array -- and, of course, so much more.

      However, watch out for utf8 strings... unpack('C*', ..) will unpack a utf8 string as characters, so may return values > 0xFF -- so:

      my $b = "123\xC4" ; my $u = "a\xC4\x{107}\x{1C4}" ; show(unpack('C*', $b)) ; # 0x31, 0x32, 0x33, 0xC4 show(unpack('C*', $u)) ; # 0x61, 0xC4, 0x107, 0x1C4 { use bytes ; show(unpack('C*', $b)) ; # 0x31, 0x32, 0x33, 0xC4 show(unpack('C*', $u)) ; # 0x61, 0xC3, 0x84, 0xC4, 0x87, 0xC7, 0x +84 } ; sub show { print join(", ", map(sprintf("0x%02X", $_), @_)), "\n" ; } ;
      as shown, use bytes causes the unpack to operate on the bytes of encoded form of the utf8 string.

      Note that with utf8 strings, pack('C*', ... and unpack('C*', ... are not symmetrical, observe:

      my $u = "a\xC4\x{107}\x{1C4}" ; my @u = unpack('C*', $u) ; show(@u) ; # 0x61, 0xC4, 0x107, 0x1C4 bytes(pack('C*',@u)) ; # Character in 'C' format wrapped in pac +k ... # Character in 'C' format wrapped in pac +k ... # 61:C4:07:C4 -- byte sub show { print join(", ", map(sprintf("0x%02X", $_), @_)), "\n" ; } ; sub bytes { my ($s) = @_ ; my $w = utf8::is_utf8($s) ? "utf8" : "byte" ; use bytes ; print join(":", map(sprintf("%02X", $_), unpack('C*', $s))), " -- +$w\n" ; } ;
      but this is wandering off topic into a significantly murky area, of possibly marginal utility...