package plan9.auth; /* * Inferno public key authentication * * Copyright © 2005 Vita Nuova Holdings Limited * * to do * attr=val keys * secstore interface * Auth.auth */ import java.math.BigInteger;import java.security.*;import java.security.interfaces.*;import java.security.spec.*;import java.nio.*;import java.nio.channels.*;import java.io.IOException;import javax.crypto.Cipher;import java.io.*;import java.util.Date; import static plan9.lib.Strings.bytes;import plan9.lib.Strings;import plan9.lib.Msgio;import plan9.lib.RemoteError; //java.security.interfaces.RSAKey, RSAPrivateCrtKey, RSAPrivateKey, RSAPublicKey// use instanceof applied to java.security.PublicKey //java.security.spec// use with java.security.KeyFactory and java.security.AlgorithmParameters// RSAKeyGenParameterSpec(modulus, pubexp, privexp, primeP, primeQ, primeExpP, primeExpQ, crtCoeff)// getCrtCoefficient, getPrimeExponentP, getPrimeExponentQ, getPrimeP, getPrimeQ, getPublicExponent// RSAPrivateKeySpec(modulus, privateexp)// getModulus, getPrivateExponent// RSAPublicKeySpec(modulus, publicexp)// getModulus, getPublicExponent import static plan9.auth.Aids.*; public class Infauth { final static int Maxmsg = 4096; static KeyFactory keyfactory; public Infauth() throws NoSuchAlgorithmException { if(keyfactory == null) keyfactory = KeyFactory.getInstance("RSA"); } public static class InfPublicKey { public PublicKey pk; public String owner; public InfPublicKey(PublicKey pk, String owner){ this.pk = pk; this.owner = owner; } public InfPublicKey(String s) throws InvalidKey { if(s == null) throw new InvalidKey("missing public key"); String[] a = Strings.getfields(s, "\n"); if(a.length < 4) throw new InvalidKey("bad public key syntax"); if(!a[0].equals("rsa")) throw new InvalidKey("unknown key algorithm: "+a[0]); BigInteger modulus = s2big64(a[2]); BigInteger publicexp = s2big64(a[3]); try{ this.pk = keyfactory.generatePublic(new RSAPublicKeySpec(modulus, publicexp)); }catch(InvalidKeySpecException e){ throw new InvalidKey("bad key spec: "+e.getMessage(), e); } this.owner = a[1]; } public String text(){ RSAPublicKey pk = (RSAPublicKey)this.pk; return "rsa\n"+this.owner+"\n"+b64(pk.getModulus())+"\n"+b64(pk.getPublicExponent())+"\n"; } public String toString(){ return pk.toString()+"\nowner="+owner; } } public static class InfPrivateKey { PrivateKey sk; public String owner; public InfPrivateKey(PrivateKey sk, String owner){ this.sk = sk; this.owner = owner; } public InfPrivateKey(String s) throws InvalidKey { if(s == null) throw new InvalidKey("missing private key"); String[] a = Strings.getfields(s, "\n"); if(a.length < 10) throw new InvalidKey("bad private key syntax"); if(!a[0].equals("rsa")) throw new InvalidKey("unknown key algorithm: "+a[0]); BigInteger n = s2big64(a[2]); BigInteger e = s2big64(a[3]); BigInteger dk = s2big64(a[4]); BigInteger p = s2big64(a[5]); BigInteger q = s2big64(a[6]); BigInteger kp = s2big64(a[7]); BigInteger kq = s2big64(a[8]); BigInteger c12 = s2big64(a[9]); // mind your p's and q's: libsec's p is java's q! (Java follows PKCS#1 in reversing their roles) // if using Java's RSA implementation directly, reverse p and q, and kp and kq // we can't use it here because it imposes PKCS#1, so we do the calculation ourselves try{ this.sk = keyfactory.generatePrivate(new RSAPrivateCrtKeySpec(n, e, dk, p, q, kp, kq, c12)); }catch(InvalidKeySpecException ex){ throw new InvalidKey("bad key spec: "+ex.getMessage(), e); } this.owner = a[1]; } public InfPublicKey getpk() throws InvalidKey { RSAPrivateCrtKey rsk = (RSAPrivateCrtKey)this.sk; try{ PublicKey pk = keyfactory.generatePublic(new RSAPublicKeySpec(rsk.getModulus(), rsk.getPublicExponent())); return new InfPublicKey(pk, this.owner); }catch(InvalidKeySpecException e){ throw new InvalidKey("bad key spec: "+e.getMessage(), e); } } public String text(){ RSAPrivateCrtKey sk = (RSAPrivateCrtKey)this.sk; return "rsa\n"+ b64(sk.getModulus())+"\n"+ b64(sk.getPublicExponent())+"\n"+ b64(sk.getPrivateExponent())+"\n"+ b64(sk.getPrimeP())+"\n"+ b64(sk.getPrimeQ())+"\n"+ b64(sk.getPrimeExponentP())+"\n"+ b64(sk.getPrimeExponentQ())+"\n"+ b64(sk.getCrtCoefficient())+"\n"; } public String toString(){ return sk.toString()+"\nowner="+owner; } } public static class Authinfo { public InfPrivateKey mysk; public InfPublicKey mypk; public Certificate cert; // signature of my public key public InfPublicKey spk; // signer's public key public BigInteger alpha; // diffie-hellman parameters public BigInteger p; public Authinfo(InfPrivateKey sk, InfPublicKey pk, Certificate cert, InfPublicKey spk, BigInteger alpha, BigInteger p){ this.mysk = sk; this.mypk = pk; this.cert = cert; this.spk = spk; this.alpha = alpha; this.p = p; } } public final Authinfo readauthinfo(ByteChannel fd) throws Exception { // signer's public key, certificate, secret key (use sk.getpk to get public one), alpha, p InfPublicKey spk; Certificate cert; InfPrivateKey mysk; BigInteger alpha, p; Msgio io; io = new Msgio(fd); spk = new InfPublicKey(io.gets()); cert = new Certificate(io.gets()); mysk = new InfPrivateKey(io.gets()); alpha = s2big64(io.gets()); p = s2big64(io.gets()); return new Authinfo(mysk, mysk.getpk(), cert, spk, alpha, p); } public static class Certificate { public String sa; // signature algorithm public String ha; // hash algorithm public String signer; // name of signer public int exp; // expiration date (seconds from Epoch, 0=never) BigInteger rsa; // only RSA signatures supported public Certificate(String sa, String ha, String signer, int exp, BigInteger rsa){ this.sa = sa; this.ha = ha; this.signer = signer; this.exp = exp; this.rsa = rsa; } public Certificate(String s) throws InvalidCertificate { if(s == null) throw new InvalidCertificate("missing certificate"); String[] a = Strings.getfields(s, "\n"); if(a.length < 5) throw new InvalidCertificate("bad certificate syntax"+":"+a.length); this.sa = a[0]; this.ha = a[1]; this.signer = a[2]; this.exp = Integer.parseInt(a[3]); this.rsa = s2big64(a[4]); } public final String text(){ return this.sa+"\n"+this.ha+"\n"+this.signer+"\n"+this.exp+"\n"+b64(this.rsa)+"\n"; } } public static class AuthResult { public Authinfo info; public byte[] secret; AuthResult(Authinfo info, byte[] secret){ this.info = info; this.secret = secret; } } public final AuthResult basicauth(ByteChannel fd, Authinfo info) throws AuthenticationException { BigInteger low, r0, alphar0, alphar1, alphar0r1; Certificate hiscert, alphacert; Msgio io = new Msgio(fd); byte[] buf, hispkbuf, alphabuf; InfPublicKey hispk; byte[] secret; int vers; try{ io.sendmsg("1"); // version buf = io.getmsg(); vers = Integer.parseInt(Strings.S(buf)); if(vers != 1 || buf.length > 4) throw new LocalAuthErr("incompatible authentication protocol"); if(info == null) throw new LocalAuthErr("no authentication information"); if(info.p == null) throw new LocalAuthErr("missing diffie hellman mod"); if(info.alpha == null) throw new LocalAuthErr("missing diffie hellman base"); if(info.mysk == null || info.mypk == null || info.cert == null || info.spk == null) // could check key details throw new LocalAuthErr("invalid authentication information"); if(info.p.compareTo(BigInteger.ZERO) <= 0) throw new LocalAuthErr("-ve modulus"); low = info.p.shiftRight(info.p.bitLength()/4); r0 = rand(low, info.p); alphar0 = info.alpha.modPow(r0, info.p); io.sendmsg(b64(alphar0)); io.sendmsg(info.cert.text()); io.sendmsg(info.mypk.text()); alphar1 = s2big64(io.gets()); if(info.p.compareTo(alphar1) <= 0) throw new LocalAuthErr("implausible parameter value"); if(alphar0.compareTo(alphar1) == 0) throw new LocalAuthErr("possible replay attack"); hiscert = new Certificate(io.gets()); hispkbuf = io.getmsg(); hispk = new InfPublicKey(Strings.S(hispkbuf)); if(!verify(info.spk, hiscert, hispkbuf)) throw new LocalAuthErr("pk doesn't match certificate"); if(hiscert.exp != 0 && hiscert.exp <= now()) throw new LocalAuthErr("certificate expired"); alphabuf = bytes(b64(alphar0) + b64(alphar1)); alphacert = sign(info.mysk, 0, alphabuf); io.sendmsg(alphacert.text()); alphacert = new Certificate(io.gets()); alphabuf = bytes(b64(alphar1) + b64(alphar0)); if(!verify(hispk, alphacert, alphabuf)) throw new LocalAuthErr("signature did not match pk"); alphar0r1 = alphar1.modPow(r0, info.p); secret = trim0(alphar0r1.toByteArray()); io.sendmsg("OK"); }catch(ClosedByInterruptException e){ throw new LocalAuthErr("interrupted by time-out", e); }catch(IOException e){ throw new LocalAuthErr("i/o error: "+e.getMessage()); // could distinguish a few cases }catch(InvalidCertificate e){ io.senderrmsg("remote: "+e.getMessage()); throw e; }catch(InvalidKey e){ io.senderrmsg("remote: "+e.getMessage()); throw e; }catch(NoSuchAlgorithmException e){ String msg = "unsupported algorithm: "+e.getMessage(); io.senderrmsg("remote: "+msg); throw new AuthenticationException(msg); }catch(LocalAuthErr e){ io.senderrmsg("remote: "+e.getMessage()); throw e; }catch(RemoteError e){ io.senderrmsg("missing your authentication data"); // strange but true throw new AuthenticationException(e.getMessage()); } try{ String s; do{ s = io.gets(); }while(!s.equals("OK")); }catch(ClosedByInterruptException e){ throw new AuthenticationException("interrupted by time-out", e); }catch(IOException e){ throw new AuthenticationException("i/o error: "+e.getMessage()); }catch(RemoteError e){ throw new AuthenticationException("remote: "+e.getMessage()); } return new AuthResult(new Authinfo(null, hispk, hiscert, info.spk, info.alpha, info.p), secret); } private static int now(){ return (int)((new Date()).getTime()/1000); } public static final BigInteger rand(BigInteger p, BigInteger q) throws IllegalArgumentException { if(p.compareTo(q) > 0){ BigInteger t = p; p = q; q = t; } BigInteger diff = q.subtract(p); BigInteger two = BigInteger.ONE.add(BigInteger.ONE); if(diff.compareTo(two) < 0) throw new IllegalArgumentException("range must be at least two"); int l = diff.bitLength(); BigInteger t = BigInteger.ONE.shiftLeft(l); l = (l + 7) & ~7; // nearest byte BigInteger slop = t.mod(diff); BigInteger r; do{ byte[] buf = new byte[l]; Aids.memrandom(buf, 0, l); r = new BigInteger(1, buf); }while(r.compareTo(slop) < 0); return r.mod(diff).add(p); } public final void setlinecrypt(ByteChannel fd, String role, String[] algs) throws IOException { String alg; Msgio io; io = new Msgio(fd); if(role.equals("client")){ if(algs != null && algs.length > 0) alg = algs[0]; else alg = "none"; // alg = "md5/rc4_256"; // no idea how to make use of SSL without its handshake io.sendmsg(alg); }else if(role.equals("server")){ try{ alg = io.gets(); }catch(RemoteError e){ throw new IOException("remote: "+e.getMessage(), e); // can't happen } if(!alg.equals("none")) throw new IOException("unsupported algorithm: "+alg); }else throw new IOException("invalid role: "+role); } public final AuthResult auth(ByteChannel fd, String role, Authinfo info, String[] algs) throws AuthenticationException, IOException { AuthResult a; a = basicauth(fd, info); setlinecrypt(fd, role, algs); return a; } private static final BigInteger rsaencrypt(RSAPublicKey pk, BigInteger data){ return data.modPow(pk.getPublicExponent(), pk.getModulus()); } public final Certificate sign(InfPrivateKey sk, int exp, byte a[]) throws NoSuchAlgorithmException { MessageDigest sha1 = MessageDigest.getInstance("SHA1"); BigInteger sig, digest; sha1.update(a); // add signer's name and expiration to hash sha1.update(bytes(sk.owner+" "+exp)); // "%s %d" digest = new BigInteger(1, sha1.digest()); sig = rsadecrypt(digest, sk.sk); return new Certificate("rsa", "sha1", sk.owner, exp, sig); } public final boolean verify(InfPublicKey pk, Certificate c, byte a[]) throws NoSuchAlgorithmException { if(!c.sa.equals("rsa") || !c.ha.equals("sha1") && !c.ha.equals("sha")) return false; MessageDigest sha1 = MessageDigest.getInstance("SHA1"); sha1.update(a); sha1.update(bytes(c.signer+" "+c.exp)); return rsaverify(new BigInteger(1, sha1.digest()), c.rsa, (RSAPublicKey)pk.pk); } public static final BigInteger rsadecrypt(BigInteger n, PrivateKey rsa1){ RSAPrivateCrtKey rsa = (RSAPrivateCrtKey)rsa1; BigInteger p, q, v1, v2; p = rsa.getPrimeP(); v1 = n.mod(p); q = rsa.getPrimeQ(); v2 = n.mod(q); v1 = v1.modPow(rsa.getPrimeExponentP(), p); v2 = v2.modPow(rsa.getPrimeExponentQ(), q); // out = v1 + p*((v2-v1)*c2 mod q) return v2.subtract(v1).multiply(rsa.getCrtCoefficient()).mod(q).multiply(p).add(v1); } public static final boolean rsaverify(BigInteger m, BigInteger sig, RSAPublicKey key){ return rsaencrypt(key, sig).equals(m); }}
package plan9.auth;
/*
* Inferno public key authentication
*
* Copyright © 2005 Vita Nuova Holdings Limited
*
* to do
* attr=val keys
* secstore interface
* Auth.auth
*/
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.*;
import java.security.spec.*;
import java.nio.*;
import java.nio.channels.*;
import java.io.IOException;
import javax.crypto.Cipher;
import java.io.*;
import java.util.Date;
import static plan9.lib.Strings.bytes;
import plan9.lib.Strings;
import plan9.lib.Msgio;
import plan9.lib.RemoteError;
//java.security.interfaces.RSAKey, RSAPrivateCrtKey, RSAPrivateKey, RSAPublicKey
// use instanceof applied to java.security.PublicKey
//java.security.spec
// use with java.security.KeyFactory and java.security.AlgorithmParameters
// RSAKeyGenParameterSpec(modulus, pubexp, privexp, primeP, primeQ, primeExpP, primeExpQ, crtCoeff)
// getCrtCoefficient, getPrimeExponentP, getPrimeExponentQ, getPrimeP, getPrimeQ, getPublicExponent
// RSAPrivateKeySpec(modulus, privateexp)
// getModulus, getPrivateExponent
// RSAPublicKeySpec(modulus, publicexp)
// getModulus, getPublicExponent
import static plan9.auth.Aids.*;
public class Infauth {
final static int Maxmsg = 4096;
static KeyFactory keyfactory;
public Infauth() throws NoSuchAlgorithmException {
if(keyfactory == null)
keyfactory = KeyFactory.getInstance("RSA");
}
public static class InfPublicKey {
public PublicKey pk;
public String owner;
public InfPublicKey(PublicKey pk, String owner){
this.pk = pk; this.owner = owner;
}
public InfPublicKey(String s) throws InvalidKey {
if(s == null)
throw new InvalidKey("missing public key");
String[] a = Strings.getfields(s, "\n");
if(a.length < 4)
throw new InvalidKey("bad public key syntax");
if(!a[0].equals("rsa"))
throw new InvalidKey("unknown key algorithm: "+a[0]);
BigInteger modulus = s2big64(a[2]);
BigInteger publicexp = s2big64(a[3]);
try{
this.pk = keyfactory.generatePublic(new RSAPublicKeySpec(modulus, publicexp));
}catch(InvalidKeySpecException e){
throw new InvalidKey("bad key spec: "+e.getMessage(), e);
}
this.owner = a[1];
}
public String text(){
RSAPublicKey pk = (RSAPublicKey)this.pk;
return "rsa\n"+this.owner+"\n"+b64(pk.getModulus())+"\n"+b64(pk.getPublicExponent())+"\n";
}
public String toString(){
return pk.toString()+"\nowner="+owner;
}
}
public static class InfPrivateKey {
PrivateKey sk;
public String owner;
public InfPrivateKey(PrivateKey sk, String owner){
this.sk = sk; this.owner = owner;
}
public InfPrivateKey(String s) throws InvalidKey {
if(s == null)
throw new InvalidKey("missing private key");
String[] a = Strings.getfields(s, "\n");
if(a.length < 10)
throw new InvalidKey("bad private key syntax");
if(!a[0].equals("rsa"))
throw new InvalidKey("unknown key algorithm: "+a[0]);
BigInteger n = s2big64(a[2]);
BigInteger e = s2big64(a[3]);
BigInteger dk = s2big64(a[4]);
BigInteger p = s2big64(a[5]);
BigInteger q = s2big64(a[6]);
BigInteger kp = s2big64(a[7]);
BigInteger kq = s2big64(a[8]);
BigInteger c12 = s2big64(a[9]);
// mind your p's and q's: libsec's p is java's q! (Java follows PKCS#1 in reversing their roles)
// if using Java's RSA implementation directly, reverse p and q, and kp and kq
// we can't use it here because it imposes PKCS#1, so we do the calculation ourselves
try{
this.sk = keyfactory.generatePrivate(new RSAPrivateCrtKeySpec(n, e, dk, p, q, kp, kq, c12));
}catch(InvalidKeySpecException ex){
throw new InvalidKey("bad key spec: "+ex.getMessage(), e);
}
this.owner = a[1];
}
public InfPublicKey getpk() throws InvalidKey {
RSAPrivateCrtKey rsk = (RSAPrivateCrtKey)this.sk;
try{
PublicKey pk = keyfactory.generatePublic(new RSAPublicKeySpec(rsk.getModulus(), rsk.getPublicExponent()));
return new InfPublicKey(pk, this.owner);
}catch(InvalidKeySpecException e){
throw new InvalidKey("bad key spec: "+e.getMessage(), e);
}
}
public String text(){
RSAPrivateCrtKey sk = (RSAPrivateCrtKey)this.sk;
return "rsa\n"+
b64(sk.getModulus())+"\n"+
b64(sk.getPublicExponent())+"\n"+
b64(sk.getPrivateExponent())+"\n"+
b64(sk.getPrimeP())+"\n"+
b64(sk.getPrimeQ())+"\n"+
b64(sk.getPrimeExponentP())+"\n"+
b64(sk.getPrimeExponentQ())+"\n"+
b64(sk.getCrtCoefficient())+"\n";
}
public String toString(){
return sk.toString()+"\nowner="+owner;
}
}
public static class Authinfo {
public InfPrivateKey mysk;
public InfPublicKey mypk;
public Certificate cert; // signature of my public key
public InfPublicKey spk; // signer's public key
public BigInteger alpha; // diffie-hellman parameters
public BigInteger p;
public Authinfo(InfPrivateKey sk, InfPublicKey pk, Certificate cert, InfPublicKey spk, BigInteger alpha, BigInteger p){
this.mysk = sk; this.mypk = pk; this.cert = cert; this.spk = spk; this.alpha = alpha; this.p = p;
}
}
public final Authinfo readauthinfo(ByteChannel fd) throws Exception {
// signer's public key, certificate, secret key (use sk.getpk to get public one), alpha, p
InfPublicKey spk;
Certificate cert;
InfPrivateKey mysk;
BigInteger alpha, p;
Msgio io;
io = new Msgio(fd);
spk = new InfPublicKey(io.gets());
cert = new Certificate(io.gets());
mysk = new InfPrivateKey(io.gets());
alpha = s2big64(io.gets());
p = s2big64(io.gets());
return new Authinfo(mysk, mysk.getpk(), cert, spk, alpha, p);
}
public static class Certificate {
public String sa; // signature algorithm
public String ha; // hash algorithm
public String signer; // name of signer
public int exp; // expiration date (seconds from Epoch, 0=never)
BigInteger rsa; // only RSA signatures supported
public Certificate(String sa, String ha, String signer, int exp, BigInteger rsa){
this.sa = sa; this.ha = ha; this.signer = signer; this.exp = exp; this.rsa = rsa;
}
public Certificate(String s) throws InvalidCertificate {
if(s == null)
throw new InvalidCertificate("missing certificate");
String[] a = Strings.getfields(s, "\n");
if(a.length < 5)
throw new InvalidCertificate("bad certificate syntax"+":"+a.length);
this.sa = a[0];
this.ha = a[1];
this.signer = a[2];
this.exp = Integer.parseInt(a[3]);
this.rsa = s2big64(a[4]);
}
public final String text(){
return this.sa+"\n"+this.ha+"\n"+this.signer+"\n"+this.exp+"\n"+b64(this.rsa)+"\n";
}
}
public static class AuthResult {
public Authinfo info;
public byte[] secret;
AuthResult(Authinfo info, byte[] secret){
this.info = info; this.secret = secret;
}
}
public final AuthResult basicauth(ByteChannel fd, Authinfo info) throws AuthenticationException {
BigInteger low, r0, alphar0, alphar1, alphar0r1;
Certificate hiscert, alphacert;
Msgio io = new Msgio(fd);
byte[] buf, hispkbuf, alphabuf;
InfPublicKey hispk;
byte[] secret;
int vers;
try{
io.sendmsg("1"); // version
buf = io.getmsg();
vers = Integer.parseInt(Strings.S(buf));
if(vers != 1 || buf.length > 4)
throw new LocalAuthErr("incompatible authentication protocol");
if(info == null)
throw new LocalAuthErr("no authentication information");
if(info.p == null)
throw new LocalAuthErr("missing diffie hellman mod");
if(info.alpha == null)
throw new LocalAuthErr("missing diffie hellman base");
if(info.mysk == null || info.mypk == null || info.cert == null || info.spk == null) // could check key details
throw new LocalAuthErr("invalid authentication information");
if(info.p.compareTo(BigInteger.ZERO) <= 0)
throw new LocalAuthErr("-ve modulus");
low = info.p.shiftRight(info.p.bitLength()/4);
r0 = rand(low, info.p);
alphar0 = info.alpha.modPow(r0, info.p);
io.sendmsg(b64(alphar0));
io.sendmsg(info.cert.text());
io.sendmsg(info.mypk.text());
alphar1 = s2big64(io.gets());
if(info.p.compareTo(alphar1) <= 0)
throw new LocalAuthErr("implausible parameter value");
if(alphar0.compareTo(alphar1) == 0)
throw new LocalAuthErr("possible replay attack");
hiscert = new Certificate(io.gets());
hispkbuf = io.getmsg();
hispk = new InfPublicKey(Strings.S(hispkbuf));
if(!verify(info.spk, hiscert, hispkbuf))
throw new LocalAuthErr("pk doesn't match certificate");
if(hiscert.exp != 0 && hiscert.exp <= now())
throw new LocalAuthErr("certificate expired");
alphabuf = bytes(b64(alphar0) + b64(alphar1));
alphacert = sign(info.mysk, 0, alphabuf);
io.sendmsg(alphacert.text());
alphacert = new Certificate(io.gets());
alphabuf = bytes(b64(alphar1) + b64(alphar0));
if(!verify(hispk, alphacert, alphabuf))
throw new LocalAuthErr("signature did not match pk");
alphar0r1 = alphar1.modPow(r0, info.p);
secret = trim0(alphar0r1.toByteArray());
io.sendmsg("OK");
}catch(ClosedByInterruptException e){
throw new LocalAuthErr("interrupted by time-out", e);
}catch(IOException e){
throw new LocalAuthErr("i/o error: "+e.getMessage()); // could distinguish a few cases
}catch(InvalidCertificate e){
io.senderrmsg("remote: "+e.getMessage());
throw e;
}catch(InvalidKey e){
io.senderrmsg("remote: "+e.getMessage());
throw e;
}catch(NoSuchAlgorithmException e){
String msg = "unsupported algorithm: "+e.getMessage();
io.senderrmsg("remote: "+msg);
throw new AuthenticationException(msg);
}catch(LocalAuthErr e){
io.senderrmsg("remote: "+e.getMessage());
throw e;
}catch(RemoteError e){
io.senderrmsg("missing your authentication data"); // strange but true
throw new AuthenticationException(e.getMessage());
}
try{
String s;
do{
s = io.gets();
}while(!s.equals("OK"));
}catch(ClosedByInterruptException e){
throw new AuthenticationException("interrupted by time-out", e);
}catch(IOException e){
throw new AuthenticationException("i/o error: "+e.getMessage());
}catch(RemoteError e){
throw new AuthenticationException("remote: "+e.getMessage());
}
return new AuthResult(new Authinfo(null, hispk, hiscert, info.spk, info.alpha, info.p), secret);
}
private static int now(){
return (int)((new Date()).getTime()/1000);
}
public static final BigInteger rand(BigInteger p, BigInteger q) throws IllegalArgumentException {
if(p.compareTo(q) > 0){
BigInteger t = p; p = q; q = t;
}
BigInteger diff = q.subtract(p);
BigInteger two = BigInteger.ONE.add(BigInteger.ONE);
if(diff.compareTo(two) < 0)
throw new IllegalArgumentException("range must be at least two");
int l = diff.bitLength();
BigInteger t = BigInteger.ONE.shiftLeft(l);
l = (l + 7) & ~7; // nearest byte
BigInteger slop = t.mod(diff);
BigInteger r;
do{
byte[] buf = new byte[l];
Aids.memrandom(buf, 0, l);
r = new BigInteger(1, buf);
}while(r.compareTo(slop) < 0);
return r.mod(diff).add(p);
}
public final void setlinecrypt(ByteChannel fd, String role, String[] algs) throws IOException {
String alg;
Msgio io;
io = new Msgio(fd);
if(role.equals("client")){
if(algs != null && algs.length > 0)
alg = algs[0];
else
alg = "none"; // alg = "md5/rc4_256"; // no idea how to make use of SSL without its handshake
io.sendmsg(alg);
}else if(role.equals("server")){
try{
alg = io.gets();
}catch(RemoteError e){
throw new IOException("remote: "+e.getMessage(), e); // can't happen
}
if(!alg.equals("none"))
throw new IOException("unsupported algorithm: "+alg);
}else
throw new IOException("invalid role: "+role);
}
public final AuthResult auth(ByteChannel fd, String role, Authinfo info, String[] algs) throws AuthenticationException, IOException {
AuthResult a;
a = basicauth(fd, info);
setlinecrypt(fd, role, algs);
return a;
}
private static final BigInteger rsaencrypt(RSAPublicKey pk, BigInteger data){
return data.modPow(pk.getPublicExponent(), pk.getModulus());
}
public final Certificate sign(InfPrivateKey sk, int exp, byte a[]) throws NoSuchAlgorithmException {
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
BigInteger sig, digest;
sha1.update(a);
// add signer's name and expiration to hash
sha1.update(bytes(sk.owner+" "+exp)); // "%s %d"
digest = new BigInteger(1, sha1.digest());
sig = rsadecrypt(digest, sk.sk);
return new Certificate("rsa", "sha1", sk.owner, exp, sig);
}
public final boolean verify(InfPublicKey pk, Certificate c, byte a[]) throws NoSuchAlgorithmException {
if(!c.sa.equals("rsa") || !c.ha.equals("sha1") && !c.ha.equals("sha"))
return false;
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
sha1.update(a);
sha1.update(bytes(c.signer+" "+c.exp));
return rsaverify(new BigInteger(1, sha1.digest()), c.rsa, (RSAPublicKey)pk.pk);
}
public static final BigInteger rsadecrypt(BigInteger n, PrivateKey rsa1){
RSAPrivateCrtKey rsa = (RSAPrivateCrtKey)rsa1;
BigInteger p, q, v1, v2;
p = rsa.getPrimeP();
v1 = n.mod(p);
q = rsa.getPrimeQ();
v2 = n.mod(q);
v1 = v1.modPow(rsa.getPrimeExponentP(), p);
v2 = v2.modPow(rsa.getPrimeExponentQ(), q);
// out = v1 + p*((v2-v1)*c2 mod q)
return v2.subtract(v1).multiply(rsa.getCrtCoefficient()).mod(q).multiply(p).add(v1);
}
public static final boolean rsaverify(BigInteger m, BigInteger sig, RSAPublicKey key){
return rsaencrypt(key, sig).equals(m);
}
}