package plan9.auth; //// elements of Plan 9 authentication//// this is a near transliteration of Plan 9 source, subject to the Lucent Public License 1.02,// via the Limbo P9auth module from Vita Nuova 2005// // Java version moved here from com.vitanuova.auth.P9auth// revisions Copyright © 2012 Coraid Inc // throws import java.io.IOException;import java.security.SecureRandom;import java.security.spec.AlgorithmParameterSpec; import javax.crypto.Cipher;import javax.crypto.spec.SecretKeySpec; import java.nio.*;import java.nio.channels.ByteChannel;import java.nio.channels.ClosedByInterruptException; import java.util.Arrays; import plan9.lib.Misc;import plan9.lib.Packer;import plan9.lib.Strings; public class P9auth { // // plan 9 authentication primitives // public static final int ANAMELEN = 28; // maximum size of name in previous proto public static final int AERRLEN = 64; // maximum size of errstr in previous proto public static final int DOMLEN = 48; // length of an authentication domain name public static final int DESKEYLEN = 7; // length of a des key for encrypt/decrypt public static final int CHALLEN = 8; // length of a plan9 sk1 challenge public static final int NETCHLEN = 16; // max network challenge length (used in AS protocol) public static final int SECRETLEN = 32; // max length of a secret // encryption numberings (anti-replay) public static final int AuthTreq = 1; // ticket request public static final int AuthChal = 2; // challenge box request public static final int AuthPass = 3; // change password public static final int AuthOK = 4; // fixed length reply follows public static final int AuthErr = 5; // error follows public static final int AuthMod = 6; // modify user public static final int AuthApop = 7; // apop authentication for pop3 public static final int AuthOKvar = 9; // variable length reply follows public static final int AuthChap = 10; // chap authentication for ppp public static final int AuthMSchap = 11; // MS chap authentication for ppp public static final int AuthCram = 12; // CRAM verification for IMAP (RFC2195 & rfc2104) public static final int AuthHttp = 13; // http domain login public static final int AuthVNC = 14; // VNC server login (deprecated) public static final int AuthTs = 64; // ticket encrypted with server's key public static final int AuthTc = 65; // ticket encrypted with client's key public static final int AuthAs = 66; // server generated authenticator public static final int AuthAc = 67; // client generated authenticator public static final int AuthTp = 68; // ticket encrypted with client's key for password change public static final int AuthHr = 69; // http reply public static final int TICKREQLEN = 3*ANAMELEN+CHALLEN+DOMLEN+1; public static class Ticketreq { public int rtype = 0; public String authid; // [ANAMELEN] server's encryption id public String authdom; // [DOMLEN] server's authentication domain public byte[] chal; // [CHALLEN] challenge from server public String hostid; // [ANAMELEN] host's encryption id public String uid; // [ANAMELEN] uid of requesting user on host public Ticketreq(){} public Ticketreq(int rtype, String authid, String authdom, byte[] chal, String hostid, String uid){ this.rtype = rtype; this.authid = authid; this.authdom = authdom; this.chal = chal; this.hostid = hostid; this.uid = uid; } public int packedsize(){ return TICKREQLEN; } public final void pack(Packer b) { b.put1(rtype); b.puts(authid, ANAMELEN); b.puts(authdom, DOMLEN); b.puta(chal, CHALLEN); b.puts(hostid, ANAMELEN); b.puts(uid, ANAMELEN); } public Ticketreq unpack(Packer b) { rtype = b.get1(); authid = b.gets(ANAMELEN); authdom = b.gets(DOMLEN); chal = b.geta(CHALLEN); hostid = b.gets(ANAMELEN); uid = b.gets(ANAMELEN); return this; } } public static final int TICKETLEN = CHALLEN+2*ANAMELEN+DESKEYLEN+1; public static class Ticket { public int num; // replay protection public byte[] chal; // [CHALLEN] server challenge public String cuid; // [ANAMELEN] uid on client public String suid; // [ANAMELEN] uid on server public byte[] key; // [DESKEYLEN] nonce DES key public Ticket(){} public Ticket(int num, byte[] chal, String cuid, String suid, byte[] key){ this.num = num; this.chal = chal; this.cuid = cuid; this.suid = suid; this.key = key; } public int packedsize() { return TICKETLEN; } public final void pack(Packer b, byte[] ckey) throws CryptoError { int o = b.offset(); b.put1(num); b.puta(this.chal, CHALLEN); b.puts(cuid, ANAMELEN); b.puts(suid, ANAMELEN); b.puta(key, DESKEYLEN); if(ckey != null) encrypt(ckey, b.array(), o, TICKETLEN); } public Ticket unpack(Packer b, byte[] ckey) throws CryptoError { if(ckey != null) decrypt(ckey, b.array(), b.offset(), TICKETLEN); num = b.get1(); chal = b.geta(CHALLEN); cuid = b.gets(ANAMELEN); suid = b.gets(ANAMELEN); key = b.geta(DESKEYLEN); return this; } } public static class TicketPair { public Ticket known; // was encrypted by Kc; unpacked public byte[] hidden; // encrypted by Ks, still packed public TicketPair(Ticket known, byte[] hidden){ this.known = known; this.hidden = hidden; } } public static final int AUTHENTLEN = CHALLEN+4+1; public static class Authenticator { public int num; // replay protection public byte[] chal; // [CHALLEN] public int id; // authenticator id, ++'d with each auth public Authenticator(){} public Authenticator(int num, byte[] chal, int id){ this.num = num; this.chal = chal; this.id = id; } public int packedsize(){ return AUTHENTLEN; } public final void pack(Packer b, byte[] key) throws CryptoError { int o = b.offset(); b.put1(num); b.puta(chal, CHALLEN); b.put4(id); if(key != null) encrypt(key, b.array(), o, AUTHENTLEN); } public Authenticator unpack(Packer b, byte[] key) throws CryptoError { if(key != null) decrypt(key, b.array(), b.offset(), AUTHENTLEN); num = b.get1(); chal = b.geta(CHALLEN); id = b.get4(); return this; } } public static final int PASSREQLEN = 2*ANAMELEN+1+1+SECRETLEN; public static class Passwordreq { public int num; public byte[] oldpw; // [ANAMELEN] public byte[] newpw; // [ANAMELEN] public boolean changesecret; public byte[] secret; // [SECRETLEN] public Passwordreq(){} public Passwordreq(int num, byte[] oldpw, byte[] newpw, boolean changesecret, byte[] secret){ this.num = num; this.oldpw = oldpw; this.newpw = newpw; this.changesecret = changesecret; this.secret = secret; } public int packedsize(){ return PASSREQLEN; } public final void pack(Packer b, byte[] key) throws CryptoError { b.put1(num); b.puta(oldpw, ANAMELEN); b.puta(newpw, ANAMELEN); b.put(changesecret? (byte)1: (byte)0); b.puta(secret, SECRETLEN); if(key != null) encrypt(key, b.array(), b.offset()-PASSREQLEN, PASSREQLEN); } public Passwordreq unpack(Packer b, byte[] key) throws CryptoError { if(key != null) decrypt(key, b.array(), b.offset(), PASSREQLEN); num = b.get1(); oldpw = b.geta(ANAMELEN); oldpw[ANAMELEN-1] = (byte)0; newpw = b.geta(ANAMELEN); newpw[ANAMELEN-1] = (byte)0; changesecret = b.get() != (byte)0; secret = b.geta(SECRETLEN); secret[SECRETLEN-1] = (byte)0; return this; } } final static int UB(byte b){ return (int)b&0xFF; } /* * SecureID and Plan 9 auth key/request/reply encryption (`netkey') */ public String netcrypt(byte[] key, String chal) throws CryptoError { byte[] buf = new byte[8]; Arrays.fill(buf, (byte)0); byte[] a = Strings.bytes(chal); int l = a.length; if(l > DESKEYLEN) l = DESKEYLEN; System.arraycopy(a, 0, buf, 0, l); encrypt(key, buf, 0, buf.length); return String.format("%02x%02x%02x%02x", UB(buf[0]), UB(buf[1]), UB(buf[2]), UB(buf[3])); } public byte[] passtokey(String p) throws CryptoError { byte[] a = Strings.bytes(p); int n = a.length; if(n >= ANAMELEN) n = ANAMELEN-1; byte[] buf = new byte[ANAMELEN]; Arrays.fill(buf, (byte)' '); System.arraycopy(a, 0, buf, 0, n); buf[n] = (byte)0; byte[] key = new byte[DESKEYLEN]; Arrays.fill(key, (byte)0); int t = 0; for(;;){ for(int i = 0; i < DESKEYLEN; i++) key[i] = (byte)((UB(buf[t+i]) >> i) + (UB(buf[t+i+1]) << (8 - (i+1)))); if(n <= 8) return key; n -= 8; t += 8; if(n < 8){ t -= 8 - n; n = 8; } encrypt(key, buf, t, 8); } } private static final short[] parity = { 0x01, 0x02, 0x04, 0x07, 0x08, 0x0b, 0x0d, 0x0e, 0x10, 0x13, 0x15, 0x16, 0x19, 0x1a, 0x1c, 0x1f, 0x20, 0x23, 0x25, 0x26, 0x29, 0x2a, 0x2c, 0x2f, 0x31, 0x32, 0x34, 0x37, 0x38, 0x3b, 0x3d, 0x3e, 0x40, 0x43, 0x45, 0x46, 0x49, 0x4a, 0x4c, 0x4f, 0x51, 0x52, 0x54, 0x57, 0x58, 0x5b, 0x5d, 0x5e, 0x61, 0x62, 0x64, 0x67, 0x68, 0x6b, 0x6d, 0x6e, 0x70, 0x73, 0x75, 0x76, 0x79, 0x7a, 0x7c, 0x7f, 0x80, 0x83, 0x85, 0x86, 0x89, 0x8a, 0x8c, 0x8f, 0x91, 0x92, 0x94, 0x97, 0x98, 0x9b, 0x9d, 0x9e, 0xa1, 0xa2, 0xa4, 0xa7, 0xa8, 0xab, 0xad, 0xae, 0xb0, 0xb3, 0xb5, 0xb6, 0xb9, 0xba, 0xbc, 0xbf, 0xc1, 0xc2, 0xc4, 0xc7, 0xc8, 0xcb, 0xcd, 0xce, 0xd0, 0xd3, 0xd5, 0xd6, 0xd9, 0xda, 0xdc, 0xdf, 0xe0, 0xe3, 0xe5, 0xe6, 0xe9, 0xea, 0xec, 0xef, 0xf1, 0xf2, 0xf4, 0xf7, 0xf8, 0xfb, 0xfd, 0xfe, }; public static final byte[] des56to64(byte[] k56){ byte[] k64 = new byte[8]; int hi = (UB(k56[0])<<24) | (UB(k56[1])<<16) | (UB(k56[2])<<8) | UB(k56[3]); int lo = (UB(k56[4])<<24) | (UB(k56[5])<<16) | (UB(k56[6])<<8); k64[0] = (byte)parity[(hi>>25)&0x7f]; k64[1] = (byte)parity[(hi>>18)&0x7f]; k64[2] = (byte)parity[(hi>>11)&0x7f]; k64[3] = (byte)parity[(hi>>4)&0x7f]; k64[4] = (byte)parity[((hi<<3)|(int)(lo>>>29))&0x7f]; // watch the sign extension k64[5] = (byte)parity[(lo>>22)&0x7f]; k64[6] = (byte)parity[(lo>>15)&0x7f]; k64[7] = (byte)parity[(lo>>8)&0x7f]; return k64; } final static Cipher initdes(byte[] key, int mode) throws CryptoError { return Aids.initcipher("DES/ECB/NoPadding", des56to64(key), mode); } public final static void encrypt(byte[] key, byte[] data, int o, int n) throws CryptoError { int r, j; byte[] res; if(n < 8) throw new CryptoError("length less than block size"); Cipher enc = initdes(key, Cipher.ENCRYPT_MODE); n--; r = n % 7; n /= 7; j = o; for(int i = 0; i < n; i++){ res = enc.update(data, j, 8); System.arraycopy(res, 0, data, j, 8); j += 7; } if(r > 0){ res = enc.update(data, j-7+r, 8); System.arraycopy(res, 0, data, j-7+r, 8); } } public final static void decrypt(byte[] key, byte[] data, int o, int n) throws CryptoError { int r, j; byte res[]; if(n < 8) throw new CryptoError("length less than block size"); Cipher dec = initdes(key, Cipher.DECRYPT_MODE); n--; r = n % 7; n /= 7; j = o + n*7; if(r > 0){ res = dec.update(data, j-7+r, 8); System.arraycopy(res, 0, data, j-7+r, 8); } for(int i = 0; i < n; i++){ j -= 7; res = dec.update(data, j, 8); System.arraycopy(res, 0, data, j, 8); } } private final static byte[] readn(ByteChannel fd, int nb) throws AuthProtoErr { try{ return Misc.readn(fd, nb); }catch(ClosedByInterruptException e){ throw new AuthProtoErr("interrupted by time-out", e); }catch(IOException e){ throw new AuthProtoErr("read error: "+e.getMessage(), e); } } private final void write(ByteChannel fd, byte[] a) throws AuthProtoErr { try{ fd.write(ByteBuffer.wrap(a)); }catch(ClosedByInterruptException e){ throw new AuthProtoErr("interrupted by time-out", e); }catch(IOException e){ throw new AuthProtoErr("write error: "+e.getMessage(), e); } } private static final String pbmsg = "AS protocol botch"; public final TicketPair _asgetticket(ByteChannel fd, Ticketreq tr, byte[] key) throws AuthProtoErr, LocalAuthErr, RemoteAuthErr { byte[] a = new byte[TICKREQLEN]; tr.pack(new Packer(a)); write(fd, a); a = _asrdresp(fd, 2*TICKETLEN); Packer p = new Packer(a); Ticket t; try{ t = (new Ticket()).unpack(p, key); }catch(CryptoError e){ throw new LocalAuthErr(e.getMessage(), e); } byte[] s = p.geta(TICKETLEN); return new TicketPair(t, s); } TicketPair mktickets(Ticketreq tr, byte[] key) throws CryptoError { Ticket tc, ts; byte[] randkey, a; if(!tr.authid.equals(tr.hostid)) return null; randkey = new byte[DESKEYLEN]; Aids.memrandom(randkey, 0, randkey.length); tc = new Ticket(AuthTc, tr.chal, tr.uid, tr.uid, randkey); ts = new Ticket(AuthTs, tr.chal, tr.uid, tr.uid, randkey); a = new byte[TICKETLEN]; ts.pack(new Packer(a), key); return new TicketPair(tc, a); } public final byte[] _asrdresp(ByteChannel fd, int n) throws AuthProtoErr, RemoteAuthErr { byte[] b = readn(fd, 1); // could convert to null return if(b == null) throw new AuthProtoErr("error reading from server"); byte[] buf = null; switch((int)b[0]){ case AuthOK: buf = readn(fd, n); break; case AuthOKvar: b = readn(fd, 5); if(b == null) break; n = Integer.parseInt(Strings.S(b)); if(n<= 0 || n > 4096) break; buf = readn(fd, n); break; case AuthErr: b = readn(fd, 64); if(b == null) break; int i; for(i = 0; i<b.length && b[i] != (byte)0; i++) {} throw new RemoteAuthErr(String.format("remote: %s", Strings.S(b, 0, i))); default: throw new AuthProtoErr(String.format("%s: resp %d", pbmsg, (int)b[0])); } if(buf == null) throw new AuthProtoErr(pbmsg); return buf; }}
package plan9.auth;
//
// elements of Plan 9 authentication
//
// this is a near transliteration of Plan 9 source, subject to the Lucent Public License 1.02,
// via the Limbo P9auth module from Vita Nuova 2005
//
// Java version moved here from com.vitanuova.auth.P9auth
// revisions Copyright © 2012 Coraid Inc
// throws
import java.io.IOException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.*;
import java.nio.channels.ByteChannel;
import java.nio.channels.ClosedByInterruptException;
import java.util.Arrays;
import plan9.lib.Misc;
import plan9.lib.Packer;
import plan9.lib.Strings;
public class P9auth {
//
// plan 9 authentication primitives
//
public static final int ANAMELEN = 28; // maximum size of name in previous proto
public static final int AERRLEN = 64; // maximum size of errstr in previous proto
public static final int DOMLEN = 48; // length of an authentication domain name
public static final int DESKEYLEN = 7; // length of a des key for encrypt/decrypt
public static final int CHALLEN = 8; // length of a plan9 sk1 challenge
public static final int NETCHLEN = 16; // max network challenge length (used in AS protocol)
public static final int SECRETLEN = 32; // max length of a secret
// encryption numberings (anti-replay)
public static final int AuthTreq = 1; // ticket request
public static final int AuthChal = 2; // challenge box request
public static final int AuthPass = 3; // change password
public static final int AuthOK = 4; // fixed length reply follows
public static final int AuthErr = 5; // error follows
public static final int AuthMod = 6; // modify user
public static final int AuthApop = 7; // apop authentication for pop3
public static final int AuthOKvar = 9; // variable length reply follows
public static final int AuthChap = 10; // chap authentication for ppp
public static final int AuthMSchap = 11; // MS chap authentication for ppp
public static final int AuthCram = 12; // CRAM verification for IMAP (RFC2195 & rfc2104)
public static final int AuthHttp = 13; // http domain login
public static final int AuthVNC = 14; // VNC server login (deprecated)
public static final int AuthTs = 64; // ticket encrypted with server's key
public static final int AuthTc = 65; // ticket encrypted with client's key
public static final int AuthAs = 66; // server generated authenticator
public static final int AuthAc = 67; // client generated authenticator
public static final int AuthTp = 68; // ticket encrypted with client's key for password change
public static final int AuthHr = 69; // http reply
public static final int TICKREQLEN = 3*ANAMELEN+CHALLEN+DOMLEN+1;
public static class Ticketreq {
public int rtype = 0;
public String authid; // [ANAMELEN] server's encryption id
public String authdom; // [DOMLEN] server's authentication domain
public byte[] chal; // [CHALLEN] challenge from server
public String hostid; // [ANAMELEN] host's encryption id
public String uid; // [ANAMELEN] uid of requesting user on host
public Ticketreq(){}
public Ticketreq(int rtype, String authid, String authdom, byte[] chal, String hostid, String uid){
this.rtype = rtype;
this.authid = authid;
this.authdom = authdom;
this.chal = chal;
this.hostid = hostid;
this.uid = uid;
}
public int packedsize(){ return TICKREQLEN; }
public final void pack(Packer b) {
b.put1(rtype);
b.puts(authid, ANAMELEN);
b.puts(authdom, DOMLEN);
b.puta(chal, CHALLEN);
b.puts(hostid, ANAMELEN);
b.puts(uid, ANAMELEN);
}
public Ticketreq unpack(Packer b) {
rtype = b.get1();
authid = b.gets(ANAMELEN);
authdom = b.gets(DOMLEN);
chal = b.geta(CHALLEN);
hostid = b.gets(ANAMELEN);
uid = b.gets(ANAMELEN);
return this;
}
}
public static final int TICKETLEN = CHALLEN+2*ANAMELEN+DESKEYLEN+1;
public static class Ticket {
public int num; // replay protection
public byte[] chal; // [CHALLEN] server challenge
public String cuid; // [ANAMELEN] uid on client
public String suid; // [ANAMELEN] uid on server
public byte[] key; // [DESKEYLEN] nonce DES key
public Ticket(){}
public Ticket(int num, byte[] chal, String cuid, String suid, byte[] key){
this.num = num;
this.chal = chal;
this.cuid = cuid;
this.suid = suid;
this.key = key;
}
public int packedsize() { return TICKETLEN; }
public final void pack(Packer b, byte[] ckey) throws CryptoError {
int o = b.offset();
b.put1(num);
b.puta(this.chal, CHALLEN);
b.puts(cuid, ANAMELEN);
b.puts(suid, ANAMELEN);
b.puta(key, DESKEYLEN);
if(ckey != null)
encrypt(ckey, b.array(), o, TICKETLEN);
}
public Ticket unpack(Packer b, byte[] ckey) throws CryptoError {
if(ckey != null)
decrypt(ckey, b.array(), b.offset(), TICKETLEN);
num = b.get1();
chal = b.geta(CHALLEN);
cuid = b.gets(ANAMELEN);
suid = b.gets(ANAMELEN);
key = b.geta(DESKEYLEN);
return this;
}
}
public static class TicketPair {
public Ticket known; // was encrypted by Kc; unpacked
public byte[] hidden; // encrypted by Ks, still packed
public TicketPair(Ticket known, byte[] hidden){
this.known = known;
this.hidden = hidden;
}
}
public static final int AUTHENTLEN = CHALLEN+4+1;
public static class Authenticator {
public int num; // replay protection
public byte[] chal; // [CHALLEN]
public int id; // authenticator id, ++'d with each auth
public Authenticator(){}
public Authenticator(int num, byte[] chal, int id){
this.num = num;
this.chal = chal;
this.id = id;
}
public int packedsize(){ return AUTHENTLEN; }
public final void pack(Packer b, byte[] key) throws CryptoError {
int o = b.offset();
b.put1(num);
b.puta(chal, CHALLEN);
b.put4(id);
if(key != null)
encrypt(key, b.array(), o, AUTHENTLEN);
}
public Authenticator unpack(Packer b, byte[] key) throws CryptoError {
if(key != null)
decrypt(key, b.array(), b.offset(), AUTHENTLEN);
num = b.get1();
chal = b.geta(CHALLEN);
id = b.get4();
return this;
}
}
public static final int PASSREQLEN = 2*ANAMELEN+1+1+SECRETLEN;
public static class Passwordreq {
public int num;
public byte[] oldpw; // [ANAMELEN]
public byte[] newpw; // [ANAMELEN]
public boolean changesecret;
public byte[] secret; // [SECRETLEN]
public Passwordreq(){}
public Passwordreq(int num, byte[] oldpw, byte[] newpw, boolean changesecret, byte[] secret){
this.num = num;
this.oldpw = oldpw;
this.newpw = newpw;
this.changesecret = changesecret;
this.secret = secret;
}
public int packedsize(){ return PASSREQLEN; }
public final void pack(Packer b, byte[] key) throws CryptoError {
b.put1(num);
b.puta(oldpw, ANAMELEN);
b.puta(newpw, ANAMELEN);
b.put(changesecret? (byte)1: (byte)0);
b.puta(secret, SECRETLEN);
if(key != null)
encrypt(key, b.array(), b.offset()-PASSREQLEN, PASSREQLEN);
}
public Passwordreq unpack(Packer b, byte[] key) throws CryptoError {
if(key != null)
decrypt(key, b.array(), b.offset(), PASSREQLEN);
num = b.get1();
oldpw = b.geta(ANAMELEN);
oldpw[ANAMELEN-1] = (byte)0;
newpw = b.geta(ANAMELEN);
newpw[ANAMELEN-1] = (byte)0;
changesecret = b.get() != (byte)0;
secret = b.geta(SECRETLEN);
secret[SECRETLEN-1] = (byte)0;
return this;
}
}
final static int UB(byte b){ return (int)b&0xFF; }
/*
* SecureID and Plan 9 auth key/request/reply encryption (`netkey')
*/
public String netcrypt(byte[] key, String chal) throws CryptoError {
byte[] buf = new byte[8];
Arrays.fill(buf, (byte)0);
byte[] a = Strings.bytes(chal);
int l = a.length;
if(l > DESKEYLEN)
l = DESKEYLEN;
System.arraycopy(a, 0, buf, 0, l);
encrypt(key, buf, 0, buf.length);
return String.format("%02x%02x%02x%02x", UB(buf[0]), UB(buf[1]), UB(buf[2]), UB(buf[3]));
}
public byte[] passtokey(String p) throws CryptoError {
byte[] a = Strings.bytes(p);
int n = a.length;
if(n >= ANAMELEN)
n = ANAMELEN-1;
byte[] buf = new byte[ANAMELEN];
Arrays.fill(buf, (byte)' ');
System.arraycopy(a, 0, buf, 0, n);
buf[n] = (byte)0;
byte[] key = new byte[DESKEYLEN];
Arrays.fill(key, (byte)0);
int t = 0;
for(;;){
for(int i = 0; i < DESKEYLEN; i++)
key[i] = (byte)((UB(buf[t+i]) >> i) + (UB(buf[t+i+1]) << (8 - (i+1))));
if(n <= 8)
return key;
n -= 8;
t += 8;
if(n < 8){
t -= 8 - n;
n = 8;
}
encrypt(key, buf, t, 8);
}
}
private static final short[] parity = {
0x01, 0x02, 0x04, 0x07, 0x08, 0x0b, 0x0d, 0x0e,
0x10, 0x13, 0x15, 0x16, 0x19, 0x1a, 0x1c, 0x1f,
0x20, 0x23, 0x25, 0x26, 0x29, 0x2a, 0x2c, 0x2f,
0x31, 0x32, 0x34, 0x37, 0x38, 0x3b, 0x3d, 0x3e,
0x40, 0x43, 0x45, 0x46, 0x49, 0x4a, 0x4c, 0x4f,
0x51, 0x52, 0x54, 0x57, 0x58, 0x5b, 0x5d, 0x5e,
0x61, 0x62, 0x64, 0x67, 0x68, 0x6b, 0x6d, 0x6e,
0x70, 0x73, 0x75, 0x76, 0x79, 0x7a, 0x7c, 0x7f,
0x80, 0x83, 0x85, 0x86, 0x89, 0x8a, 0x8c, 0x8f,
0x91, 0x92, 0x94, 0x97, 0x98, 0x9b, 0x9d, 0x9e,
0xa1, 0xa2, 0xa4, 0xa7, 0xa8, 0xab, 0xad, 0xae,
0xb0, 0xb3, 0xb5, 0xb6, 0xb9, 0xba, 0xbc, 0xbf,
0xc1, 0xc2, 0xc4, 0xc7, 0xc8, 0xcb, 0xcd, 0xce,
0xd0, 0xd3, 0xd5, 0xd6, 0xd9, 0xda, 0xdc, 0xdf,
0xe0, 0xe3, 0xe5, 0xe6, 0xe9, 0xea, 0xec, 0xef,
0xf1, 0xf2, 0xf4, 0xf7, 0xf8, 0xfb, 0xfd, 0xfe,
};
public static final byte[] des56to64(byte[] k56){
byte[] k64 = new byte[8];
int hi = (UB(k56[0])<<24) | (UB(k56[1])<<16) | (UB(k56[2])<<8) | UB(k56[3]);
int lo = (UB(k56[4])<<24) | (UB(k56[5])<<16) | (UB(k56[6])<<8);
k64[0] = (byte)parity[(hi>>25)&0x7f];
k64[1] = (byte)parity[(hi>>18)&0x7f];
k64[2] = (byte)parity[(hi>>11)&0x7f];
k64[3] = (byte)parity[(hi>>4)&0x7f];
k64[4] = (byte)parity[((hi<<3)|(int)(lo>>>29))&0x7f]; // watch the sign extension
k64[5] = (byte)parity[(lo>>22)&0x7f];
k64[6] = (byte)parity[(lo>>15)&0x7f];
k64[7] = (byte)parity[(lo>>8)&0x7f];
return k64;
}
final static Cipher initdes(byte[] key, int mode) throws CryptoError {
return Aids.initcipher("DES/ECB/NoPadding", des56to64(key), mode);
}
public final static void encrypt(byte[] key, byte[] data, int o, int n) throws CryptoError {
int r, j;
byte[] res;
if(n < 8)
throw new CryptoError("length less than block size");
Cipher enc = initdes(key, Cipher.ENCRYPT_MODE);
n--;
r = n % 7;
n /= 7;
j = o;
for(int i = 0; i < n; i++){
res = enc.update(data, j, 8);
System.arraycopy(res, 0, data, j, 8);
j += 7;
}
if(r > 0){
res = enc.update(data, j-7+r, 8);
System.arraycopy(res, 0, data, j-7+r, 8);
}
}
public final static void decrypt(byte[] key, byte[] data, int o, int n) throws CryptoError {
int r, j;
byte res[];
if(n < 8)
throw new CryptoError("length less than block size");
Cipher dec = initdes(key, Cipher.DECRYPT_MODE);
n--;
r = n % 7;
n /= 7;
j = o + n*7;
if(r > 0){
res = dec.update(data, j-7+r, 8);
System.arraycopy(res, 0, data, j-7+r, 8);
}
for(int i = 0; i < n; i++){
j -= 7;
res = dec.update(data, j, 8);
System.arraycopy(res, 0, data, j, 8);
}
}
private final static byte[] readn(ByteChannel fd, int nb) throws AuthProtoErr {
try{
return Misc.readn(fd, nb);
}catch(ClosedByInterruptException e){
throw new AuthProtoErr("interrupted by time-out", e);
}catch(IOException e){
throw new AuthProtoErr("read error: "+e.getMessage(), e);
}
}
private final void write(ByteChannel fd, byte[] a) throws AuthProtoErr {
try{
fd.write(ByteBuffer.wrap(a));
}catch(ClosedByInterruptException e){
throw new AuthProtoErr("interrupted by time-out", e);
}catch(IOException e){
throw new AuthProtoErr("write error: "+e.getMessage(), e);
}
}
private static final String pbmsg = "AS protocol botch";
public final TicketPair _asgetticket(ByteChannel fd, Ticketreq tr, byte[] key) throws AuthProtoErr, LocalAuthErr, RemoteAuthErr {
byte[] a = new byte[TICKREQLEN];
tr.pack(new Packer(a));
write(fd, a);
a = _asrdresp(fd, 2*TICKETLEN);
Packer p = new Packer(a);
Ticket t;
try{
t = (new Ticket()).unpack(p, key);
}catch(CryptoError e){
throw new LocalAuthErr(e.getMessage(), e);
}
byte[] s = p.geta(TICKETLEN);
return new TicketPair(t, s);
}
TicketPair mktickets(Ticketreq tr, byte[] key) throws CryptoError {
Ticket tc, ts;
byte[] randkey, a;
if(!tr.authid.equals(tr.hostid))
return null;
randkey = new byte[DESKEYLEN];
Aids.memrandom(randkey, 0, randkey.length);
tc = new Ticket(AuthTc, tr.chal, tr.uid, tr.uid, randkey);
ts = new Ticket(AuthTs, tr.chal, tr.uid, tr.uid, randkey);
a = new byte[TICKETLEN];
ts.pack(new Packer(a), key);
return new TicketPair(tc, a);
}
public final byte[] _asrdresp(ByteChannel fd, int n) throws AuthProtoErr, RemoteAuthErr {
byte[] b = readn(fd, 1); // could convert to null return
if(b == null)
throw new AuthProtoErr("error reading from server");
byte[] buf = null;
switch((int)b[0]){
case AuthOK:
buf = readn(fd, n);
break;
case AuthOKvar:
b = readn(fd, 5);
if(b == null)
break;
n = Integer.parseInt(Strings.S(b));
if(n<= 0 || n > 4096)
break;
buf = readn(fd, n);
break;
case AuthErr:
b = readn(fd, 64);
if(b == null)
break;
int i;
for(i = 0; i<b.length && b[i] != (byte)0; i++)
{}
throw new RemoteAuthErr(String.format("remote: %s", Strings.S(b, 0, i)));
default:
throw new AuthProtoErr(String.format("%s: resp %d", pbmsg, (int)b[0]));
}
if(buf == null)
throw new AuthProtoErr(pbmsg);
return buf;
}
}