/* $Id: ISO9796p1.java,v 1.1 2011/05/04 22:37:58 willuhn Exp $ This file is part of CryptAlgs4Java Copyright (C) 2001-2010 Stefan Palme CryptAlgs4Java is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. CryptAlgs4Java is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.kapott.cryptalgs; import java.math.BigInteger; import java.security.InvalidAlgorithmParameterException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SignatureException; import java.security.SignatureSpi; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.AlgorithmParameterSpec; import java.util.Arrays; import java.util.logging.Logger; public class ISO9796p1 extends SignatureSpi { private RSAPublicKey pubKey; private PrivateKey privKey; private MessageDigest dig; private SignatureParamSpec param; protected Logger getLogger() { return Logger.getLogger(this.getClass().getName()); } @Override @Deprecated protected Object engineGetParameter(String parameter) { return null; } @Override protected void engineInitSign(PrivateKey privateKey) { try { this.dig=MessageDigest.getInstance(this.param.getHashAlg(), this.param.getProvider()); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } catch (NoSuchProviderException e) { throw new RuntimeException(e); } this.privKey=privateKey; } @Override protected void engineInitVerify(PublicKey publicKey) { try { this.dig=MessageDigest.getInstance(this.param.getHashAlg(), this.param.getProvider()); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } catch (NoSuchProviderException e) { throw new RuntimeException(e); } this.pubKey=(RSAPublicKey)publicKey; } @Override @Deprecated protected void engineSetParameter(String param1, Object value) { // do nothing } @Override protected void engineSetParameter(AlgorithmParameterSpec param1) throws InvalidAlgorithmParameterException { if (param1 instanceof SignatureParamSpec) this.param=(SignatureParamSpec)(param1); else { throw new InvalidAlgorithmParameterException(); } } @Override protected byte[] engineSign() throws SignatureException { BigInteger bModulus; if (this.privKey instanceof RSAPrivateKey) { bModulus=((RSAPrivateKey)this.privKey).getModulus(); } else { RSAPrivateCrtKey2 key2=(RSAPrivateCrtKey2)this.privKey; bModulus=key2.getP().multiply(key2.getQ()); } byte[] modulus=bModulus.toByteArray(); byte[] buffer=this.dig.digest(); byte[] rr=prepareForSig(buffer,bModulus); byte[] is; if (this.privKey instanceof RSAPrivateKey) { getLogger().fine("signing with (n,d)-algorithm"); BigInteger bPrivExponent=((RSAPrivateKey)this.privKey).getPrivateExponent(); BigInteger bIS=(new BigInteger(+1,rr)).modPow(bPrivExponent,bModulus); is=bIS.toByteArray(); } else { getLogger().fine("signing with (p,q,dP,dQ,qInv)-algorithm"); RSAPrivateCrtKey2 key2=(RSAPrivateCrtKey2)this.privKey; BigInteger p=key2.getP(); BigInteger q=key2.getQ(); BigInteger dP=key2.getdP(); BigInteger dQ=key2.getdQ(); BigInteger qInv=key2.getQInv(); BigInteger encData=new BigInteger(+1,rr); BigInteger m1=encData.modPow(dP,p); BigInteger m2=encData.modPow(dQ,q); BigInteger h=m1.subtract(m2).multiply(qInv).mod(p); is=m2.add(q.multiply(h)).toByteArray(); } // adjust value byte[] sig=getSigFromIS(is,modulus); return sig; } public static byte[] prepareForSig(byte[] buffer,BigInteger bModulus) throws SignatureException { /* padding; 'cause my buffer is already byte-aligned, there are no padding bits to be prepended */ byte[] mp=new byte[buffer.length]; System.arraycopy(buffer,0,mp,0,buffer.length); int k=bModulus.bitLength()-1; int z=mp.length; int r=1; if (!((z<<4)<=(k+3))) { throw new SignatureException("16*z is greater than k"); } // extension (concatenate MP multiple times) /*double t2=((double)(k))/8; if ((k&0x07) != 0) t2 += 1.0; int t = ((int)(t2)) >> 1;*/ int t=(k-1)>>4; if (((k-1)&0x0F)!=0) t++; byte[] me=getMEfromMP(mp, t); /* creating redundancy by interleaving the extended message bytes with redundancy bytes */ byte[] mr=getMRfromME(me,t,z,r); // get intermediate integer byte[] ir=getIRfromMR(mr,k); // calculate signature byte[] rr=ir; // getRRfromIR(ir, k, modulus, privExponent); return rr; } @Override protected int engineSign(byte[] output,int offset,int len) throws SignatureException { byte[] sig=engineSign(); if (offset+len>output.length) throw new SignatureException("output result too large for buffer"); System.arraycopy(sig,0,output,offset,sig.length); return sig.length; } @Override protected void engineUpdate(byte b) { this.dig.update(b); } @Override protected void engineUpdate(byte[] b,int offset,int length) { for (int i=0;i<length;i++) { engineUpdate(b[offset+i]); } } @Override protected boolean engineVerify(byte[] sig) throws SignatureException { BigInteger bExponent = this.pubKey.getPublicExponent(); byte[] exponent = bExponent.toByteArray(); BigInteger bModulus = this.pubKey.getModulus(); byte[] modulus = bModulus.toByteArray(); byte[] is=getISfromSig(sig,exponent,modulus); int[] ks=new int[1]; byte[] ir=getIRfromIS(is,exponent,modulus,ks); int k=ks[0]; int[] ts=new int[1]; byte[] mr=getMRfromIR(ir,k,ts); int t=ts[0]; int[] zs=new int[1]; int[] rs=new int[1]; byte[] mp=getMPfromMR(mr,t,zs,rs); int z=zs[0]; int r=rs[0]; int datalen=(z<<3)+1-r; int databytes=(datalen>>3); if ((datalen&0x07)!=0) { databytes++; } byte[] recHash=new byte[databytes]; System.arraycopy(mp,mp.length-databytes,recHash,0,databytes); if ((datalen&0x07)!=0) { recHash[0]&=(1<<(datalen&0x07))-1; } BigInteger hash=new BigInteger(+1, recHash); BigInteger hash2=new BigInteger(+1, this.dig.digest()); byte[] me2=getMEfromMP(mp,t); byte[] mr2=getMRfromME(me2,t,z,r); mr[0] &=(1<<(7-((mr.length <<3)-k)))-1; mr2[0]&=(1<<(7-((mr2.length<<3)-k)))-1; return hash.equals(hash2) && Arrays.equals(mr,mr2); } // --------------------------------------------------------------------------------- private static byte[] getISfromSig(byte[] sig,byte[] exp,byte[] mod) { return (new BigInteger(+1,sig)).modPow(new BigInteger(+1,exp),new BigInteger(+1,mod)).toByteArray(); } private static byte[] getIRfromIS(byte[] is,byte[] exp,byte[] mod,int[] ks) throws SignatureException { BigInteger is_b=new BigInteger(+1,is); BigInteger mod_b=new BigInteger(+1,mod); BigInteger exp_b=new BigInteger(+1,exp); BigInteger ret=null; if (is_b.mod(new BigInteger("16")).equals(new BigInteger("6"))) ret=is_b; else if (mod_b.subtract(is_b).mod(new BigInteger("16")).equals(new BigInteger("6"))) ret=mod_b.subtract(is_b); if (ret==null && exp_b.mod(new BigInteger("2")).compareTo(new BigInteger("0"))==0) if (is_b.mod(new BigInteger("8")).equals(new BigInteger("3"))) ret=is_b.multiply(new BigInteger("2")); else if (mod_b.subtract(is_b).mod(new BigInteger("8")).equals(new BigInteger("3"))) ret=mod_b.subtract(is_b).multiply(new BigInteger("2")); if (ret==null) { throw new SignatureException("can not convert IS to IR"); } byte[] reta=ret.toByteArray(); int k=reta.length<<3; for (int idx=0,pos=0;;) { if ((reta[idx]&(1<<(7-pos)))!=0) break; k--; if (++pos==8) { pos=0; idx++; } } ks[0]=k; if ((reta[reta.length-1]&0x0F)!=0x06) throw new SignatureException("last nibble is not 0x06"); return reta; } private static byte Perm(int b) { return (new byte[]{ 0x0E, 0x03, 0x05, 0x08, 0x09, 0x04, 0x02, 0x0F, 0x00, 0x0D, 0x0B, 0x06, 0x07, 0x0A, 0x0C, 0x01})[b]; } private static byte Perm1(int b) { return (new byte[]{ 0x08, 0x0F, 0x06, 0x01, 0x05, 0x02, 0x0B, 0x0C, 0x03, 0x04, 0x0D, 0x0A, 0x0E, 0x09, 0x00, 0x07})[b]; } private static byte[] getMRfromIR(byte[] ir,int k,int[] ts) { /* double t2 = ((double)(k)) / 8; if ((k & 0x07) != 0) t2 += 1.0; int t = ((int)(t2)) >> 1;*/ int t=(k-1)>>4; if (((k-1)&0x0F)!=0) t++; ts[0]=t; byte[] mr=new byte[2*t]; int bitsum=0; for (int i=0;i<2*t;i++) { mr[2*t-1-i]=ir[2*t-1-i]; bitsum+=8; if (bitsum>=k) mr[2*t-1-i]&=(1<<(7-(bitsum-k)))-1; } mr[2*t-1]=(byte)((Perm1((mr[2*t-2]>>4)&0x0F)<<4) | ((mr[2*t-1]>>4)&0x0F)); return mr; } private static byte S(int x) { return (byte)((Perm((x>>4)&0x0F)<<4) | Perm(x&0x0F)); } private static byte[] getMPfromMR(byte[] mr,int t,int[] zs,int[] rs) throws SignatureException { int i; for (i=0;i<t;i++) { byte sum=(byte)(S(mr[2*t-1 - (2*i)]) ^ mr[2*t-1 - (2*i+1)]); if (sum!=0) { zs[0]=i+1; rs[0]=sum&0x0F; break; } } if (i==t) { throw new SignatureException("all sums are 0"); } if (rs[0]<1 || rs[0]>8) throw new SignatureException("r is not in range 1..8"); int z=zs[0]; byte[] mp=new byte[z]; for (i=0;i<z;i++) { mp[z-1-i]=mr[2*t-1-(2*i)]; } return mp; } private static byte[] getMEfromMP(byte[] mp,int t) { byte[] ret=new byte[t]; int sum=0; while (sum<t) { if (sum+mp.length<=t) { System.arraycopy(mp,0,ret,ret.length-sum-mp.length,mp.length); sum+=mp.length; } else { int diff=t-sum; System.arraycopy(mp,mp.length-diff,ret,0,diff); sum+=diff; } } return ret; } private static byte[] getMRfromME(byte[] me,int t,int z,int r) { byte[] mr=new byte[2*t]; for (int i=0;i<t;i++) { mr[2*t-1 - (2*i)] =me[t-1-i]; mr[2*t-1 - (2*i+1)]=S(me[t-1-i]); } mr[(t-z)<<1]^=r; return mr; } private static byte[] getIRfromMR(byte[] mr,int k) { int len=mr.length; byte[] ir=new byte[len]; System.arraycopy(mr,0,ir,0,len); ir[0]&=(1<<(7-((len<<3)-k)))-1; ir[0]|=(1<<(7-((len<<3)-k))); ir[len-1]=(byte)(((ir[len-1]&0x0F)<<4) | 0x06); return ir; } /* private int jacobi(BigInteger a,BigInteger n) { int j=1; while (a.compareTo(new BigInteger("0"))!=0) { while (a.mod(new BigInteger("2")).compareTo(new BigInteger("0"))==0) { a=a.divide(new BigInteger("2")); BigInteger mod8=n.mod(new BigInteger("8")); if (mod8.compareTo(new BigInteger("3"))==0 || mod8.compareTo(new BigInteger("5"))==0) { j=-j; } } BigInteger temp=a; a=n; n=temp; if (a.mod(new BigInteger("4")).compareTo(new BigInteger("3"))==0 && n.mod(new BigInteger("4")).compareTo(new BigInteger("3"))==0) { j=-j; } a=a.mod(n); } int ret; if (n.compareTo(new BigInteger("1"))==0) ret=j; else ret=0; return ret; }*/ /*private byte[] getRRfromIR(byte[] ir, int k, byte[] modulus, byte[] privExponent) { byte[] ir2 = new byte[ir.length]; System.arraycopy(ir, 0, ir2, 0, ir.length); ir2[0] &= ((1 << (8 - ((ir.length << 3) - k))) - 1); BigInteger bIR = new BigInteger(+1, ir2); BigInteger bModulus = new BigInteger(+1, modulus); BigInteger bRR = null; bRR = bIR; return bRR.toByteArray(); }*/ private static byte[] getSigFromIS(byte[] is,byte[] modulus) { BigInteger bIS=new BigInteger(+1,is); BigInteger bModulus=new BigInteger(+1,modulus); BigInteger bIS2=bModulus.subtract(bIS); BigInteger bSig=null; if (bIS.compareTo(bIS2)<0) bSig=bIS; else bSig=bIS2; return bSig.toByteArray(); } }