Being a very forgetful person, I relied upon my encoded cvs password too much lately and had never really bothered to
keep a copy of the clear text; until I had to use Tortoise for some cvs
operation. I’ve always meant to be sorting that little negligence on my part out but I never actually got round to it…
As I was waiting for the support people to reset my password, I did some research on cvs’ pserver password scrambling
algorithm and I found that all it does is a simple mapping, described in detail in
section 6 of The CVS Client/Server Protocol. So, for
example, %
will be mapped to m
and m
can be decoded by getting the inverse mapping back to %
, as simple as
that. Officially, only a subset of characters can be used for passwords and encoded passwords, see discussion in
section 8. Based on that, I wrote a java class for encoding and
decoding cvs passwords, using pserver authentication protocol’s scrambling algorithm. I’d been too slow; by that time
my password had already been reset by our support team.
/**
* A simple class for encoding and decoding passwords for cvs'
* pserver protocol. Can be used to recover forgotten passwords.
*/
public class CvsPassword {
private static char[] aChars = {0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
114, 120, 53, 79, 0, 109, 72, 108,
70, 64, 76, 67, 116, 74, 68, 87,
111, 52, 75, 119, 49, 34, 82, 81,
95, 65, 112, 86, 118, 110, 122, 105,
41, 57, 83, 43, 46, 102, 40, 89,
38, 103, 45, 50, 42, 123, 91, 35,
125, 55, 54, 66, 124, 126, 59, 47,
92, 71, 115, 78, 88, 107, 106, 56,
0, 121, 117, 104, 101, 100, 69, 73,
99, 63, 94, 93, 39, 37, 61, 48,
58, 113, 32, 90, 44, 98, 60, 51,
33, 97, 62, 77, 84, 80, 85};
/**
* Encodes a cvs password to be used in .cvspass file. Throws an
* exception if sClear is null, if a character is found outside
* the 0 - 126 range, or if within the range, one of the
* non-allowed characters.
*
* @param sClear the password in clear to be encoded
* @return the encoded cvs password
*/
public static String encode(String sClear) {
char[] acScrambled = new char[sClear.length() + 1];
acScrambled[0] = 'A';
for (int i = 1; i < acScrambled.length; i++) {
char c = sClear.charAt(i - 1);
if (c == '`' || c == '$' || c < 32)
throw new IllegalArgumentException(
"Illegal character was found in clear password.");
acScrambled[i] = aChars[c];
}
return String.valueOf(acScrambled);
}
/**
* Recovers an encoded via pserver protocol cvs password.
* Throws an exception if passed a null input.
*
* @param sScrambled the encoded password to be decoded
* @return the decoded password
*/
public static String decode(String sScrambled) {
if (sScrambled.equals(""))
return "";
return encode(sScrambled.substring(1)).substring(1);
}
public static void main(String[] sArgs) {
// Encode password and decode the result.
System.out.println(CvsPassword.encode("password"));
System.out.println(CvsPassword.decode("A:yZZ30 e"));
// Print the character mapping used for the
// password scrambling.
for (int i = 33; i < aChars.length; i++) {
System.out.print(i + "(" + (char) i + ")->");
System.out.print((int) aChars[i] + "(" + aChars[i] + ") ");
}
}
}