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


I have several devices (pcs, phones, tablets, routers) where I store sensitive informations (passwords, PINs etc: short textual information), some of which are even shared via some cloud, so I am looking for a way to secure this in a way that would work everywhere and the one thing I have everywhere is a perl-installation.

So what I am looking for is the best way to encrypt and decrypt short strings via perl using core-modules only that produces encrypted versions containing only printable characters.

As the built-in crypt only uses the first 8 characters (afaik) this is unfortunately not as trivial as I thought it would be...

Note that I do not want solutions that implement an known algorithm in pure perl and that I am quite happy to share my secrets with the NSA, I am simply looking for something convenient to use to protect my secrets from the average Joe that hits the sweet spot between convenience and security.

Any ideas?

Many thanks!

Replies are listed 'Best First'.
Re: crypto with core modules only
by stevieb (Canon) on Aug 28, 2018 at 00:57 UTC

    Quite a few years ago, I was helping someone doing some light data manipulation stuff (obfuscation really). Premise was to obfu a string with a password, print out the result, and then to decipher it, you had to supply the same password and paste in the jumbled hex text. Here's an example of how it works, then the actual code. The code is in two files (for the convenience of the person I was helping; it could easily be merged. Also, if I were to actually use it myself, I'd add another input field for the salt as well (it's hard-coded), but I digress). This is very simple and probably far from secure, but thought I'd share anyhow.

    Obfuscate a message:

    perl Create a password: secretpw Enter your message to be encrypted: This is an encryption test Your encrypted message: c7cda0cc55bde684b5db9698d5d6d7b0c9a9bde2d274e1 +dba6db

    Then, to decrypt:

    perl Enter your password: secretpw Enter your encrypted message: c7cda0cc55bde684b5db9698d5d6d7b0c9a9bde2 +d274e1dba6db Your decrypted message: This is an encryption test

    The code:

    use warnings; use strict; use 5.10.0; print "\nCreate a password: "; chomp ( my $password = <STDIN> ); my $salt = 'j4'; my $crypt_pass = crypt( $salt, $password ); print "Enter your message to be encrypted: "; chomp ( my $message = <STDIN> ); my @hex_pass = map { sprintf( "%x", ( ord( $_ ))) } split //, $crypt_pass; my @hex_msg = map { sprintf( "%x", ( ord( $_ ))) } split //, $message; my @crypted; my @hash; push @hash, @hex_pass until @hash > @hex_msg; my $i=0; for my $letter ( @hex_msg ){ push @crypted, hex( $letter ) + hex( $hash[$i] ); $i++; } @crypted = map { sprintf( "%x", $_ ) } @crypted; print "\nYour encrypted message: "; print @crypted; print "\n\n";

    The code:

    use warnings; use strict; use 5.10.0; print "\nEnter your password: "; chomp ( my $password = <STDIN> ); my $salt = 'j4'; my $crypt_pass = crypt( $salt, $password ); my @hex_pass = map { sprintf( "%x", ( ord( $_ ) ) ) } split //, $crypt +_pass; print "Enter your encrypted message: "; chomp ( my $message = <STDIN> ); my @crypt_message = ( $message =~ m/../g ); my @hash; push @hash, @hex_pass until @hash > @crypt_message; my @decrypted_message; my $n = 0; for my $letter ( @crypt_message ){ push @decrypted_message, ( hex( $letter ) - hex( $hash[$n] ) ) ; $n++; } print "\nYour decrypted message: "; print map { chr( $_ ) } @decrypted_message; print "\n\n";
      That is very insecure. It's basically adding the same set of 13 numbers in a cycle to each 13th character of the plaintext. You can eliminate the key from the cyphertext by calculating something along the lines of
      $diff[$_] = $cyphertext[$_] - $cyphertext[$_+13] for 0..(@cyphertext +- 13);
      You then end up with this equivalence for all the chars in the plaintext, apart from the first and last 13:
      $diff[$_] == $plaintext[$_] - $plaintext[$_+13];
      From there it's fairly easy to deduce what @plaintext is, especially if a few chars of the plaintext are known or can be guessed.


        It's called a Vigenere cipher, and this variant is an especially weak variant because the length of the key is known to be 13 characters. It is somewhat more secure if the key length isn't known to the attacker.

        If the key length is variable, for long ciphertexts, there are algorithms that can quickly yield the plaintext. For very short cipertexts, it's a lot more secure than you might think. If the key is as long as the message, it's basically a one-time pad.

        Thanks Dave,

        I figured it's terribly insecure, but the original premise was helping someone do some simple obfu that could be encoded in hex and then decoded. Security wasn't really part of the deal.

        It was a uni project for the person and we kind of collaborated outside of any forums. A learning exercise essentially.

Re: crypto with core modules only
by zentara (Archbishop) on Aug 29, 2018 at 10:06 UTC
    Here is a good one, which frodo72, now known as polettix showed me in perl encryptions keys vs. c. I was trying to find out how to make c's RC4 compatible with perl's RC4. Anyways, this code is pretty good for your purposes, it uses no modules, and uses a key.
    #!/usr/bin/perl -0777 -- # by frodo72 of perlmonks # $0 key infile > outfile # symmetric works both ways use strict; use warnings; my @k = map { ord($_) } split //, shift; my ( @s, $x, $y ); $y = 0; for ( my @t = @s = 0 .. 255 ) { $y = ( $k[ $_ % @k ] + $s[ $x = $_ ] + $y ) % 256; &S; } $x = $y = 0; for ( unpack( 'C*', <> ) ) { $x++; $y = ( $s[ $x %= 256 ] + $y ) % 256; &S; print pack( 'C', $_ ^= $s[ ( $s[$x] + $s[$y] ) % 256 ] ); } sub S { @s[ $x, $y ] = @s[ $y, $x ] }

    I'm not really a human, but I play one on earth. ..... an animated JAPH
Re: crypto with core modules only
by shmem (Chancellor) on Aug 28, 2018 at 17:37 UTC
    using core-modules only

    That basically amounts to including any useful perl-only encryption module verbatim (along with those it depends on) into the script you want to create.

    There should be no difference between doing that and using a non-core module, except maintainance issues.

    See also App::FatPacker, Module::FatPack etc.

    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
Re: crypto with core modules only
by trippledubs (Deacon) on Aug 28, 2018 at 18:32 UTC
    The hashed text from crypt can be longer than 8 characters unless you are working with ancient implementation
    #!/usr/bin/env perl use strict; use warnings; # a non-random salt == 'salt' #SHA256 print crypt("secret",'$5$salt'); print "\n"; #SHA512 print crypt("secret",'$66salt'); print "\n"; # Oops, security gone, that is not what I meant # I meant this print crypt("secret",'$6$salt');
    You also have Digest, which is core
    #!/usr/bin/env perl use strict; use warnings; use Digest; my $algo = 'SHA-512'; sub hash { my $string = shift; my $salt; # bless whoever wrote this $salt .= join '',('.','/',(0..9),"a".."z","A".."Z")[rand 64] for ( +1..8); my $hasher = Digest->new($algo); $hasher->add($salt); $hasher->add($string); return $salt . $hasher->b64digest(); } sub checkHash { # First 8 characters are salt my $hash = shift; my $string = shift; my $salt = substr($hash,0,8); my $hasher = Digest->new($algo); $hasher->add($salt); $hasher->add($string); return $hash eq $salt . $hasher->b64digest(); } my $hash = hash('blahblah'); print "match" if checkHash($hash,'blahblah');
    for encryption, you can xor, read more here Encryption using perl core functions only.
Re: crypto with core modules only
by zentara (Archbishop) on Aug 28, 2018 at 12:35 UTC
    Hi, if you don't care about passwords, this will still stop the average joe from viewing.
    #!/usr/bin/perl use warnings; use strict; #by fokat of perlmonks my $string = 'justanotherperlhacker'; print "$string\n"; my $obscure = pack("u",$string); print "$obscure\n"; my $unobscure = unpack(chr(ord("a") + 19 + print ""),$obscure); print "$unobscure\n";

    I'm not really a human, but I play one on earth. ..... an animated JAPH
Re: crypto with core modules only
by QM (Parson) on Aug 28, 2018 at 17:06 UTC
    $x =~ y/A-Za-z/N-ZA-Mn-za-m/;

    Quantum Mechanics: The dreams stuff is made of

Re: crypto with core modules only
by TheloniusMonk (Sexton) on Aug 30, 2018 at 14:26 UTC
    The best (unbreakable) and simplest has to be the One-time_pad. This originally was restricted to 26 letters. To include all printables you would need to transpose "\n" to say chr(127) (unprintable just after 126 printable) to keep the printables + a token for "\n" sequential. Then you generate N random numbers between 0 and 95 (hereafter the key), one for each character in the input. The encryption is to subtract 32 from the ord of each character in the input and add it to its corresponding key value modulo 95. Add 32 to that and you have the ord of a printable except for 127 which you replace with "\n". Same for the key - add 32, chr it and transpose chr(127) to "\n" for printing it out. The key (one-time pad) is issued on encryption and is needed for decryption. Decryption is simply the inverse -- subtract 32 from the key characters and input characters, subtract each key value from the input character value modulo 95, add 32, print except that 127 is printed as "\n". Both the key and the encrypted files are random, so without the key, the code is completely unbreakable.

      One-time pads can be implemented much more easily than that. Just create a string of random characters the same length as your message (we'll call it $key), then do this:

      my $encrypted = $message ^ $key; my $decrypted = $encrypted ^ $key;

      This will result in a ciphertext which is full of non-printable characters, but you can base64 encode it (MIME::Base64 has been in core since before Perl 5.8).

      The problem with one-time pads is that the keys are very long (same length as the message) and need to have a decent amount of randomness, so cannot be committed to memory. They need to be stored somewhere.

        I considered XOR before choosing the algorithm and rejected it because XOR only changes those bits that are different = about 50% of them. Although that seems plausibly enough to have the same effect, it seemed easier to block possible security loopholes therein than to have to do days of work analysing them statistically.

        And because you then have to choose something like MIME::Base64 to make it printable, this would increase the average lengths of both key and message by (95-64)/64 * 100% = 60.8%, exacerbating your own final point about lengths.

        Conversely, the OP is trying to encode a small list of info, I imagine to be something like:-

        pwd google p&BBw0RD
        pin iphone 8642

        So I chose the solution that best fits what I imagine to be the use case. Yes the user needs to note the key which is issued at encryption time, equal in length to his secret info list and although it might well be 50+ characters in length, the OP is highly suggestive of wanting to reject keys that are too short - hence I stick by my post. I feel I responded as optimally as I could within a reasonable level of background research.

        update re having to store the key somewhere - this is already consequent from the boundary conditions of the OP. And its precisely what the OTP and the OP for that matter is about - write it on the back of a business card and close the terminal window used to generate the key, so there is only one physical copy, no electronic copies reducing the weakness to one physical item to keep in your physical wallet.

        more update: flipping half the bits with xor is analytically breakable for repeated use on same document where changes are small. modulo solution does not have same weakness.
          A reply falls below the community's threshold of quality. You may see it by logging in.