Change 11852 by jhi@alpha on 2001/09/03 19:50:57

        Make crypt() do something more sane for Unicode
        (take crypt() of the low eight bits of the characters,
        instead of taking crypt() of the UTF-8 of the scalar);
        add a test for crypt().

Affected files ...

... //depot/perl/MANIFEST#533 edit
... //depot/perl/pod/perlfunc.pod#258 edit
... //depot/perl/pp.c#301 edit
... //depot/perl/t/op/crypt.t#1 add

Differences ...

==== //depot/perl/MANIFEST#533 (text) ====
Index: perl/MANIFEST
--- perl/MANIFEST.~1~   Mon Sep  3 14:00:05 2001
+++ perl/MANIFEST       Mon Sep  3 14:00:05 2001
@@ -2019,6 +2019,7 @@
 t/op/concat.t                  See if string concatenation works
 t/op/cond.t                    See if conditional expressions work
 t/op/context.t                 See if context propagation works
+t/op/crypt.t                   See if crypt works
 t/op/defins.t                  See if auto-insert of defined() works
 t/op/delete.t                  See if delete works
 t/op/die.t                     See if die works

==== //depot/perl/pod/perlfunc.pod#258 (text) ====
Index: perl/pod/perlfunc.pod
--- perl/pod/perlfunc.pod.~1~   Mon Sep  3 14:00:05 2001
+++ perl/pod/perlfunc.pod       Mon Sep  3 14:00:05 2001
@@ -806,17 +806,29 @@
 the password file for lousy passwords, amongst other things.  Only the
 guys wearing white hats should do this.
 
-Note that C<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
+Note that C<crypt> is intended to be a one-way function, much like
+breaking eggs to make an omelette.  There is no (known) corresponding
+decrypt function (in other words, the crypt() is a one-way hash
+function).  As a result, this function isn't all that useful for
 cryptography.  (For that, see your nearby CPAN mirror.)
 
-When verifying an existing encrypted string you should use the encrypted
-text as the salt (like C<crypt($plain, $crypted) eq $crypted>).  This
-allows your code to work with the standard C<crypt> and with more
-exotic implementations.  When choosing a new salt create a random two
-character string whose characters come from the set C<[./0-9A-Za-z]>
-(like C<join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64]>).
+When verifying an existing encrypted string you should use the
+encrypted text as the salt (like C<crypt($plain, $crypted) eq
+$crypted>).  This allows your code to work with the standard C<crypt>
+and with more exotic implementations.  In other words, do not assume
+anything about the returned string itself, or how many bytes in
+the encrypted string matter.
+
+Traditionally the result is a string of 13 bytes: two first bytes of
+the salt, followed by 11 bytes from the set C<[./0-9A-Za-z]>, and only
+the first eight bytes of the encrypted string mattered, but
+alternative hashing schemes (like MD5), higher level security schemes
+(like C2), and implementations on non-UNIX platforms may produce
+different strings.
+
+When choosing a new salt create a random two character string whose
+characters come from the set C<[./0-9A-Za-z]> (like C<join '', ('.',
+'/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64]>).
 
 Here's an example that makes sure that whoever runs this program knows
 their own password:
@@ -844,6 +856,11 @@
 on your favorite CPAN mirror for a slew of potentially useful
 modules.
 
+If using crypt() on an Unicode string (which potentially has
+characters with codepoints above 255), Perl tries to make sense of
+the situation by using only the low eight bits of the characters when
+calling crypt().
+
 =item dbmclose HASH
 
 [This function has been largely superseded by the C<untie> function.]

==== //depot/perl/pp.c#301 (text) ====
Index: perl/pp.c
--- perl/pp.c.~1~       Mon Sep  3 14:00:05 2001
+++ perl/pp.c   Mon Sep  3 14:00:05 2001
@@ -3095,12 +3095,28 @@
     dSP; dTARGET; dPOPTOPssrl;
     STRLEN n_a;
 #ifdef HAS_CRYPT
-    char *tmps = SvPV(left, n_a);
+    STRLEN len;
+    char *tmps = SvPV(left, len);
+    char *t    = 0;
+    if (DO_UTF8(left)) {
+         /* If Unicode take the crypt() of the low 8 bits
+         * of the characters of the string. */
+        char *s    = tmps;
+        char *send = tmps + len;
+        STRLEN i   = 0;
+        Newz(688, t, len, char);
+        while (s < send) {
+             t[i++] = utf8_to_uvchr((U8*)s, 0) & 0xFF;
+             s += UTF8SKIP(s);
+        }
+        tmps = t;
+    }
 #ifdef FCRYPT
     sv_setpv(TARG, fcrypt(tmps, SvPV(right, n_a)));
 #else
     sv_setpv(TARG, PerlProc_crypt(tmps, SvPV(right, n_a)));
 #endif
+    Safefree(t);
 #else
     DIE(aTHX_
       "The crypt() function is unimplemented due to excessive paranoia.");

==== //depot/perl/t/op/crypt.t#1 (text) ====
Index: perl/t/op/crypt.t
--- perl/t/op/crypt.t.~1~       Mon Sep  3 14:00:05 2001
+++ perl/t/op/crypt.t   Mon Sep  3 14:00:05 2001
@@ -0,0 +1,15 @@
+use Test::More tests => 2;
+
+# Can't assume too much about the string returned by crypt(),
+# and about how many bytes of the encrypted (really, hashed)
+# string matter.
+#
+# HISTORICALLY the results started with the first two bytes of the salt,
+# followed by 11 bytes from the set [./0-9A-Za-z], and only the first
+# eight characters mattered, but those are probably no more safe
+# bets, given alternative encryption/hashing schemes like MD5,
+# C2 (or higher) security schemes, and non-UNIX platforms.
+
+ok(substr(crypt("ab", "cd"), 2) ne substr(crypt("ab", "ce"), 2), "salt");
+
+ok(crypt("HI", "HO") eq crypt(v4040.4041, "HO"), "Unicode");
End of Patch.

Reply via email to