FileDocCategorySizeDatePackage
ISO9796d2PSSSigner.javaAPI DocAndroid 1.5 API16392Wed May 06 22:41:06 BST 2009org.bouncycastle.crypto.signers

ISO9796d2PSSSigner

public class ISO9796d2PSSSigner extends Object implements org.bouncycastle.crypto.SignerWithRecovery
ISO9796-2 - mechanism using a hash function with recovery (scheme 2 and 3).

Note: the usual length for the salt is the length of the hash function used in bytes.

Fields Summary
public static final int
TRAILER_IMPLICIT
public static final int
TRAILER_RIPEMD160
public static final int
TRAILER_RIPEMD128
public static final int
TRAILER_SHA1
private org.bouncycastle.crypto.Digest
digest
private org.bouncycastle.crypto.AsymmetricBlockCipher
cipher
private SecureRandom
random
private byte[]
standardSalt
private int
hLen
private int
trailer
private int
keyBits
private byte[]
block
private byte[]
mBuf
private int
messageLength
private int
saltLength
private boolean
fullMessage
private byte[]
recoveredMessage
Constructors Summary
public ISO9796d2PSSSigner(org.bouncycastle.crypto.AsymmetricBlockCipher cipher, org.bouncycastle.crypto.Digest digest, int saltLength, boolean implicit)
Generate a signer for the with either implicit or explicit trailers for ISO9796-2, scheme 2 or 3.

param
cipher base cipher to use for signature creation/verification
param
digest digest to use.
param
saltLength length of salt in bytes.
param
implicit whether or not the trailer is implicit or gives the hash.


                                                             
     
           
                          
                             
                         
    
        this.cipher = cipher;
        this.digest = digest;
        this.hLen = digest.getDigestSize();
        this.saltLength = saltLength;

        if (implicit)
        {
            trailer = TRAILER_IMPLICIT;
        }
        else
        {
            if (digest instanceof SHA1Digest)
            {
                trailer = TRAILER_SHA1;
            }
            // BEGIN android-removed
            // else if (digest instanceof RIPEMD160Digest)
            // {
            //     trailer = TRAILER_RIPEMD160;
            // }
            // else if (digest instanceof RIPEMD128Digest)
            // {
            //     trailer = TRAILER_RIPEMD128;
            // }
            // END android-removed
            else
            {
                throw new IllegalArgumentException("no valid trailer for digest");
            }
        }
    
public ISO9796d2PSSSigner(org.bouncycastle.crypto.AsymmetricBlockCipher cipher, org.bouncycastle.crypto.Digest digest, int saltLength)
Constructor for a signer with an explicit digest trailer.

param
cipher cipher to use.
param
digest digest to sign with.
param
saltLength length of salt in bytes.

        this(cipher, digest, saltLength, false);
    
Methods Summary
private voidItoOSP(int i, byte[] sp)
int to octet string.

        sp[0] = (byte)(i >>> 24);
        sp[1] = (byte)(i >>> 16);
        sp[2] = (byte)(i >>> 8);
        sp[3] = (byte)(i >>> 0);
    
private voidLtoOSP(long l, byte[] sp)
long to octet string.

        sp[0] = (byte)(l >>> 56);
        sp[1] = (byte)(l >>> 48);
        sp[2] = (byte)(l >>> 40);
        sp[3] = (byte)(l >>> 32);
        sp[4] = (byte)(l >>> 24);
        sp[5] = (byte)(l >>> 16);
        sp[6] = (byte)(l >>> 8);
        sp[7] = (byte)(l >>> 0);
    
private voidclearBlock(byte[] block)
clear possible sensitive data

        for (int i = 0; i != block.length; i++)
        {
            block[i] = 0;
        }
    
public byte[]generateSignature()
generate a signature for the loaded message using the key we were initialised with.

        int     digSize = digest.getDigestSize();

        byte[]    m2Hash = new byte[digSize];
  
        digest.doFinal(m2Hash, 0);

        byte[]  C = new byte[8];
        LtoOSP(messageLength * 8, C);

        digest.update(C, 0, C.length);

        digest.update(mBuf, 0, messageLength);
        
        digest.update(m2Hash, 0, m2Hash.length);
        
        byte[]    salt;
        
        if (standardSalt != null)
        {
            salt = standardSalt;
        }
        else
        {
            salt = new byte[saltLength];
            random.nextBytes(salt);
        }
        
        digest.update(salt, 0, salt.length);

        byte[]    hash = new byte[digest.getDigestSize()];
        
        digest.doFinal(hash, 0);
        
        int tLength = 2;
        if (trailer == TRAILER_IMPLICIT)
        {
            tLength = 1;
        }
        
        int    off = block.length - messageLength - salt.length - hLen - tLength - 1;
        
        block[off] = 0x01;
  
        System.arraycopy(mBuf, 0, block, off + 1, messageLength);
        System.arraycopy(salt, 0, block, off + 1 + messageLength, salt.length);

        byte[] dbMask = maskGeneratorFunction1(hash, 0, hash.length, block.length - hLen - tLength);
        for (int i = 0; i != dbMask.length; i++)
        {
            block[i] ^= dbMask[i];
        }
        
        System.arraycopy(hash, 0, block, block.length - hLen - tLength, hLen);
        
        if (trailer == TRAILER_IMPLICIT)
        {
            block[block.length - 1] = (byte)TRAILER_IMPLICIT;
        }
        else
        {
            block[block.length - 2] = (byte)(trailer >>> 8);
            block[block.length - 1] = (byte)trailer;
        }
        
        block[0] &= 0x7f;

        byte[]  b = cipher.processBlock(block, 0, block.length);

        clearBlock(mBuf);
        clearBlock(block);
        messageLength = 0;

        return b;
    
public byte[]getRecoveredMessage()
Return a reference to the recoveredMessage message.

return
the full/partial recoveredMessage message.
see
org.bouncycastle.crypto.SignerWithRecovery#getRecoveredMessage()

        return recoveredMessage;
    
public booleanhasFullMessage()
Return true if the full message was recoveredMessage.

return
true on full message recovery, false otherwise, or if not sure.
see
org.bouncycastle.crypto.SignerWithRecovery#hasFullMessage()

        return fullMessage;
    
public voidinit(boolean forSigning, org.bouncycastle.crypto.CipherParameters param)
Initialise the signer.

param
forSigning true if for signing, false if for verification.
param
param parameters for signature generation/verification. If the parameters are for generation they should be a ParametersWithRandom, a ParametersWithSalt, or just an RSAKeyParameters object. If RSAKeyParameters are passed in a SecureRandom will be created.
exception
IllegalArgumentException if wrong parameter type or a fixed salt is passed in which is the wrong length.

        RSAKeyParameters    kParam = null;
        int                    lengthOfSalt = saltLength;

        if (param instanceof ParametersWithRandom)
        {
            ParametersWithRandom    p = (ParametersWithRandom)param;

            kParam = (RSAKeyParameters)p.getParameters();
            random = p.getRandom();
        }
        else if (param instanceof ParametersWithSalt)
        {
            ParametersWithSalt    p = (ParametersWithSalt)param;

            kParam = (RSAKeyParameters)p.getParameters();
            standardSalt = p.getSalt();
            lengthOfSalt = standardSalt.length;
        }
        else
        {
            kParam = (RSAKeyParameters)param;
            if (forSigning)
            {
                random = new SecureRandom();
            }
        }
        
        cipher.init(forSigning, kParam);

        keyBits = kParam.getModulus().bitLength();

        block = new byte[(keyBits + 7) / 8];
        
        if (trailer == TRAILER_IMPLICIT)
        {
            mBuf = new byte[block.length - digest.getDigestSize() - lengthOfSalt - 1 - 1];
        }
        else
        {
            mBuf = new byte[block.length - digest.getDigestSize() - lengthOfSalt - 1 - 2];
        }

        reset();
    
private booleanisSameAs(byte[] a, byte[] b)
compare two byte arrays.

        if (messageLength != b.length)
        {
            return false;
        }
        
        for (int i = 0; i != b.length; i++)
        {
            if (a[i] != b[i])
            {
                return false;
            }
        }
        
        return true;
    
private byte[]maskGeneratorFunction1(byte[] Z, int zOff, int zLen, int length)
mask generator function, as described in PKCS1v2.

        byte[]  mask = new byte[length];
        byte[]  hashBuf = new byte[hLen];
        byte[]  C = new byte[4];
        int     counter = 0;

        digest.reset();

        while (counter < (length / hLen))
        {
            ItoOSP(counter, C);

            digest.update(Z, zOff, zLen);
            digest.update(C, 0, C.length);
            digest.doFinal(hashBuf, 0);

            System.arraycopy(hashBuf, 0, mask, counter * hLen, hLen);
            
            counter++;
        }

        if ((counter * hLen) < length)
        {
            ItoOSP(counter, C);

            digest.update(Z, zOff, zLen);
            digest.update(C, 0, C.length);
            digest.doFinal(hashBuf, 0);

            System.arraycopy(hashBuf, 0, mask, counter * hLen, mask.length - (counter * hLen));
        }

        return mask;
    
public voidreset()
reset the internal state

        digest.reset();
        messageLength = 0;
        if (recoveredMessage != null)
        {
            clearBlock(recoveredMessage);
        }
        recoveredMessage = null;
        fullMessage = false;
    
public voidupdate(byte b)
update the internal digest with the byte b

        if (messageLength < mBuf.length)
        {
            mBuf[messageLength++] = b;
        }
        else
        {
            digest.update(b);
        }
    
public voidupdate(byte[] in, int off, int len)
update the internal digest with the byte array in

        while (len > 0 && messageLength < mBuf.length)
        {
            this.update(in[off]);
            off++;
            len--;
        }
        
        if (len > 0)
        {
            digest.update(in, off, len);
        }
    
public booleanverifySignature(byte[] signature)
return true if the signature represents a ISO9796-2 signature for the passed in message.

        byte[]      block = null;

        try
        {
            block = cipher.processBlock(signature, 0, signature.length);
        }
        catch (Exception e)
        {
            return false;
        }
        
        //
        // adjust block size for leading zeroes if necessary
        //
        if (block.length < (keyBits + 7) / 8)
        {
            byte[] tmp = new byte[(keyBits + 7) / 8];

            System.arraycopy(block, 0, tmp, tmp.length - block.length, block.length);
            block = tmp;
        }

        int     tLength = 0;

        if (((block[block.length - 1] & 0xFF) ^ 0xBC) == 0)
        {
            tLength = 1;
        }
        else
        {
            int sigTrail = ((block[block.length - 2] & 0xFF) << 8) | (block[block.length - 1] & 0xFF);

            switch (sigTrail)
            {
            // BEGIN android-removed
            // case TRAILER_RIPEMD160:
            //         if (!(digest instanceof RIPEMD160Digest))
            //         {
            //             throw new IllegalStateException("signer should be initialised with RIPEMD160");
            //         }
            //         break;
            // END android-removed
            case TRAILER_SHA1:
                    if (!(digest instanceof SHA1Digest))
                    {
                        throw new IllegalStateException("signer should be initialised with SHA1");
                    }
                    break;
            // BEGIN android-removed
            // case TRAILER_RIPEMD128:
            //         if (!(digest instanceof RIPEMD128Digest))
            //         {
            //             throw new IllegalStateException("signer should be initialised with RIPEMD128");
            //         }
            //        break;
            // END android-removed
            default:
                throw new IllegalArgumentException("unrecognised hash in signature");
            }

            tLength = 2;
        }

        //
        // calculate H(m2)
        //
        byte[]    m2Hash = new byte[hLen];
        digest.doFinal(m2Hash, 0);
        
        //
        // remove the mask
        //
        byte[] dbMask = maskGeneratorFunction1(block, block.length - hLen - tLength, hLen, block.length - hLen - tLength);
        for (int i = 0; i != dbMask.length; i++)
        {
            block[i] ^= dbMask[i];
        }

        block[0] &= 0x7f;
        
        //
        // find out how much padding we've got
        //
        int mStart = 0;

        for (mStart = 0; mStart != block.length; mStart++)
        {
            if (block[mStart] == 0x01)
            {
                break;
            }
        }

        mStart++;

        if (mStart >= block.length)
        {
            clearBlock(block);
            return false;
        }
        
        if (mStart > 1)
        {
            fullMessage = true;
        }
        else
        {
            fullMessage = false;
        }
        
        recoveredMessage = new byte[dbMask.length - mStart - saltLength];

        System.arraycopy(block, mStart, recoveredMessage, 0, recoveredMessage.length);

        //
        // check the hashes
        //
        byte[]  C = new byte[8];
        LtoOSP(recoveredMessage.length * 8, C);
        
        digest.update(C, 0, C.length);

        if (recoveredMessage.length != 0)
        {
            digest.update(recoveredMessage, 0, recoveredMessage.length);
        }

        digest.update(m2Hash, 0, m2Hash.length);
        byte[]  hash = new byte[digest.getDigestSize()];

        digest.update(block, mStart + recoveredMessage.length, dbMask.length - mStart - recoveredMessage.length);
        
        digest.doFinal(hash, 0);

        int off = block.length - tLength - hash.length;
        
        for (int i = 0; i != hash.length; i++)
        {
            if (hash[i] != block[off + i])
            {
                clearBlock(block);
                clearBlock(hash);
                clearBlock(recoveredMessage);
                fullMessage = false;
                
                return false;
            }
        }

        //
        // if they've input a message check what we've recovered against
        // what was input.
        //
        if (messageLength != 0)
        {
            if (!isSameAs(mBuf, recoveredMessage))
            {
                clearBlock(mBuf);
                clearBlock(block);
               
                return false;
            }
        }
        
        clearBlock(mBuf);
        clearBlock(block);
        messageLength = 0;

        return true;