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

I've been given a puzzler by a work colleague. He has a file with sensitive data, maintained with "vi -x". He wants to get a perl script to be able to read the file, given the password.

I know that the standard crypt function is one way - from perldoc -f crypt:

Note that "crypt" is intended to be a one-way function, much like breaking eggs to make an omelette. There is no (known) corresponding decrypt function. As a result, this function isn't all that useful for cryptography. (For that, see your nearby CPAN mirror.)
For the time being, I have advised him to backtick the shell's crypt command. I know that this is insecure by way of making the password visible to ps, but this will allow him to develop the rest of the script.

So, How can I get the contents of this file? Is there a module that will do the trick amongst the hundred Crypt:: modules?

Any help would be appreciated.

--
I'm Not Just Another Perl Hacker

  • Comment on How to decrypt a file encrypted with vi -x or shell crypt

Replies are listed 'Best First'.
Re: How to decrypt a file encrypted with vi -x or shell crypt
by PodMaster (Abbot) on Aug 27, 2004 at 10:37 UTC
      From that vim doc page:
      The encryption in Vim has not been tested for robustness.
      In fact it has, and it was found wanting. There is an efficient known-plaintext attack against PKZIP encryption.
Re: How to decrypt a file encrypted with vi -x or shell crypt
by tachyon (Chancellor) on Aug 27, 2004 at 15:11 UTC

    This Inline C widget will do it. It modifies the passed SV C style if it is suitable for decryption, or ignores it if the magic header is missing.

    use Inline C; my $password = "hello"; my $file = "c:/enc.txt"; open F, $file or die $!; my $data = do{ local $/; <F> }; close F; print "Before:\n$data\n"; decode( $data, $password ); print "After:\n$data\n"; __END__ __C__ typedef unsigned int ULG; void decode( SV* str, char *passwd ) { ULG s,t,v,crc_32_tab[256],keys[3],temp; STRLEN rawlen; int decrypted = 0; char *file, *data; char *magic = "VimCrypt~01!\0"; #define ROTOR(a) { \ keys[0] = CRC32(keys[0], a); keys[1] += keys[0] & 0xff; \ keys[1] = keys[1] * 134775813L + 1; \ keys[2] = CRC32(keys[2], (int)(keys[1] >> 24)); \ } #define CRC32(c, b) (crc_32_tab[((int)(c) ^ (b)) & 0xff] ^ ((c) >> 8)) file = (char*)SvPV(str, rawlen); if ( rawlen == 0 ) return; /* we got a null string */ while ( *magic != '\0' ) { if ( *(magic++) != *(file++) ) return; /* did not find magic h +eader */ } for (t=0; t<256; t++) { v = t; for (s=0; s<8; s++) v = (v >> 1) ^ ((v & 1) * (ULG)0xedb88320L +); crc_32_tab[t] = v; } keys[0] = 305419896L; keys[1] = 591751049L; keys[2] = 878082192L; while (*passwd != '\0') ROTOR(*(passwd++)); data = file; while( *file != '\0' ) { temp = 0xffff & (keys[2] | 2); *file ^= (int)(((temp * (temp ^ 1)) >> 8) & 0xff); ROTOR(*(file++)); decrypted++; } sv_setpvn( str, data, decrypted ); /* modify the passed SV with de +crypt */ }

    cheers

    tachyon

      Can this be done as a Perl subroutine rather than an Inline C widget ? Are there advantages in doing it via Inline C ? Cheers

        Can this be done as a Perl subroutine rather than an Inline C widget ?

        Yes

        Are there advantages in doing it via Inline C ?

        Yes. The actual Vim code is C so all I did was rip the required code out of Vim and drop it into Inline C. Actually I could not resist cleaning it up a bit but that's another story. Add the fact that C is much faster than doing it in Perl, it is now done in C/XS and I have no desire to port it to Perl just so it can be slower.

        Porting is fairly easy, but not trivial. The thing that makes it non trivial is that most encryption code utilises the natural rollover of integer types. Things get a bit odd in Perl in a DWIM sort of way which is not identical to C - as a result you need to know how to fix those issues.

        use Inline 'C'; use constant UNSIGNED_MAX => 2**32-1; printf "Perl: %u\t%s\n", UNSIGNED_MAX, UNSIGNED_MAX; printf "Perl: %u\t%s\n", (UNSIGNED_MAX+1),(UNSIGNED_MAX+1); printf "Perl: %u\t%s\n", (UNSIGNED_MAX+2),(UNSIGNED_MAX+2); func( UNSIGNED_MAX ); __END__ __C__ int func( unsigned int arg ) { printf( "C: %u\n", arg ); printf( "C: %u\n", arg+1 ); printf( "C: %u\n", arg+2 ); return 0; }

        This shows you the main issue:

        Perl: 4294967295 4294967296 Perl: 4294967295 4294967297 Perl: 4294967295 4294967298 C: 4294967295 C: 0 C: 1

        As you can see C and Perl produce different results at the rollover point.

        cheers

        tachyon

Re: How to decrypt a file encrypted with vi -x or shell crypt
by bronto (Priest) on Aug 27, 2004 at 10:18 UTC

    If vi encrypted the file using crypt, I strongly doubt that your friend could open and read it back a second time... :-)

    So, he'd better off searching for vi's encrypting algorithm specification and work on it...

    Ciao!
    --bronto


    The very nature of Perl to be like natural language--inconsistant and full of dwim and special cases--makes it impossible to know it all without simply memorizing the documentation (which is not complete or totally correct anyway).
    --John M. Dlugosz
      There's crypt(1) and there's crypt(3). I am referring to crypt(1) i.e. the shell command, viz:
      User Commands                                            crypt(1)
      
      NAME
           crypt - encode or decode a file
      
      SYNOPSIS
           crypt [ password ]
      
      DESCRIPTION
           crypt encrypts and decrypts the contents of  a  file.  crypt
           reads  from  the  standard  input and writes on the standard
           output.  The password is a key  that  selects  a  particular
           transformation.   If  no  password is given, crypt demands a
           key from the terminal and turns off printing while  the  key
           is being typed in. crypt encrypts and decrypts with the same
           key:
      
                example% crypt key<clear.file> encrypted.file
                example% crypt key<encrypted.file | pr
      
           will print the contents of  clear.file.
      
           Files encrypted by crypt are compatible with  those  treated
           by the editors ed(1), ex(1), and vi(1) in encryption mode.
      
           The security of encrypted files depends  on  three  factors:
           the  fundamental method must be hard to solve; direct search
           of the key space must be infeasible; "sneak paths" by  which
           keys or cleartext can become visible must be minimized.
      
           crypt implements a  one-rotor  machine  designed  along  the
           lines  of  the  German Enigma, but with a 256-element rotor.
           Methods of attack on such machines are  widely  known,  thus
           crypt provides minimal security.
      
           The transformation of a key into the  internal  settings  of
           the  machine  is deliberately designed to be expensive, that
           is, to take a substantial fraction of a second  to  compute.
           However,  if  keys  are restricted to (say) three lower-case
           letters, then encrypted files can be read by expending  only
           a substantial fraction of five minutes of machine time.
      
           Since the key is an argument to the  crypt  command,  it  is
           potentially visible to users executing ps(1) or a derivative
           command.  To minimize this possibility, crypt takes care  to
           destroy  any  record  of the key immediately upon entry.  No
           doubt the choice of keys  and  key  security  are  the  most
           vulnerable aspect of crypt.
      
      FILES
           /dev/tty
                 for typed key
      

      --
      I'm Not Just Another Perl Hacker

        First, you should always say on which OS you are running: there is no crypt on my Linux box, but there is one (with the same man page as yours) on Solaris

        Second, you say:

        For the time being, I have advised him to backtick the shell's crypt command. I know that this is insecure by way of making the password visible to ps, but this will allow him to develop the rest of the script.

        but, wait! The man page also says:

        Since the key is an argument to the crypt command, it is potentially visible to users executing ps(1) or a derivative command. To minimize this possibility, crypt takes care to destroy any record of the key immediately upon entry. No doubt the choice of keys and key security are the most vulnerable aspect of crypt.

        so the problem with ps is limited by the program itself. What you should care of instead is that the algorithm is very easy to break, even with brute force attacks:

        crypt implements a one-rotor machine designed along the lines of the German Enigma, but with a 256-element rotor. Methods of attack on such machines are widely known, thus crypt provides minimal security.

        Therefore, if the contents of the file are really "sensitive", you really should discard vi - x and look for another option alltogether. There are a bunch of modules on CPAN that let you work with pgp: just choose the one that best fits your needs.

        Ciao!
        --bronto


        The very nature of Perl to be like natural language--inconsistant and full of dwim and special cases--makes it impossible to know it all without simply memorizing the documentation (which is not complete or totally correct anyway).
        --John M. Dlugosz
Re: How to decrypt a file encrypted with vi -x or shell crypt
by sgifford (Prior) on Aug 27, 2004 at 17:14 UTC

    You can probably use Expect to give the password to crypt(1) in a secure way. I'd recommend doing that rather than re-implementing crypt(1) in Perl, since it avoids duplication of code.

    As others have mentioned, on many systems the encryption provided by crypt(1) is very weak, and will really only stop casual observers from seeing the information in it. For better security, use something like Crypt::Rijndael, although that won't be compatible with vi.

Re: How to decrypt a file encrypted with vi -x or shell crypt
by iburrell (Chaplain) on Aug 27, 2004 at 17:08 UTC
    You don't actually need the key to read the files. The Enigma variant used by the old Unix crypt program is so weak that it can be easily broken. The program I have seen to do this is Crypt Breaker's Workbench, available from ftp://ftp.funet.fi/pub/crypt/analysis/cbw.tar.gz.
Re: How to decrypt a file encrypted with vi -x or shell crypt
by Skeeve (Parson) on Aug 27, 2004 at 10:39 UTC
    use Crypt::UnixCrypt;
    Update (At reputation -1): If you downvote, please tell why. UnixCrypt will perfectly do what the OP asked for. He even asked for the modul. So what the heck is wrong with the answer?

    Update #2: Okay... I've learned something: Always look at the documentation before you recommend something you don't really know. Thanks for telling me!
      Crypt::UnixCrypt - perl-only implementation of the crypt function.

      Its same as perldoc -f crypt, and not what op wants.

      how decrypt, its one wa?