package org.bouncycastle.openpgp;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchProviderException;
import java.security.Signature;
import java.security.SignatureException;

import org.bouncycastle.bcpg.BCPGInputStream;
import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.bcpg.OnePassSignaturePacket;

/**
 * A one pass signature object.
 */
public class PGPOnePassSignature
{
    private OnePassSignaturePacket sigPack;
    private int                    signatureType;
    
    private Signature              sig;
    
    PGPOnePassSignature(
        BCPGInputStream    pIn)
        throws IOException, PGPException
    {
        this((OnePassSignaturePacket)pIn.readPacket());
    }
    
    PGPOnePassSignature(
        OnePassSignaturePacket    sigPack)
        throws PGPException
    {
        this.sigPack = sigPack;
        this.signatureType = sigPack.getSignatureType();
        
        try
        {
            this.sig = Signature.getInstance(PGPUtil.getSignatureName(sigPack.getKeyAlgorithm(), sigPack.getHashAlgorithm()), PGPUtil.getDefaultProvider());
        }
        catch (Exception e)
        {    
            throw new PGPException("can't set up signature object.",  e);
        }
    }
    
    /**
     * Initialise the signature object for verification.
     * 
     * @param pubKey
     * @param provider
     * @throws NoSuchProviderException
     * @throws PGPException
     */
    public void initVerify(
        PGPPublicKey    pubKey,
        String          provider)
        throws NoSuchProviderException, PGPException
    {    
        try
        {
            sig.initVerify(pubKey.getKey(provider));
        }
        catch (InvalidKeyException e)
        {
            throw new PGPException("invalid key.", e);
        }
    }
    
    public void update(
        byte    b)
        throws SignatureException
    {
        if (signatureType == PGPSignature.CANONICAL_TEXT_DOCUMENT)
        {
            if (b == '\n')
            {
                sig.update((byte)'\r');
                sig.update((byte)'\n');
                return;
            }
            else if (b == '\r')
            {
                return;
            }
        }
        
        sig.update(b);
    }
    
    public void update(
        byte[]    bytes)
        throws SignatureException
    {
        if (signatureType == PGPSignature.CANONICAL_TEXT_DOCUMENT)
        {
            for (int i = 0; i != bytes.length; i++)
            {
                this.update(bytes[i]);
            }
        }
        else
        {
            sig.update(bytes);
        }
    }
    
    public void update(
        byte[]    bytes,
        int       off,
        int       length)
        throws SignatureException
    {
        if (signatureType == PGPSignature.CANONICAL_TEXT_DOCUMENT)
        {
            int finish = off + length;
            
            for (int i = off; i != finish; i++)
            {
                this.update(bytes[i]);
            }
        }
        else
        {
            sig.update(bytes, off, length);
        }
    }

    /**
     * Verify the calculated signature against the passed in PGPSignature.
     * 
     * @param pgpSig
     * @return boolean
     * @throws PGPException
     * @throws SignatureException
     */
    public boolean verify(
        PGPSignature    pgpSig)
        throws PGPException, SignatureException
    {
        sig.update(pgpSig.getSignatureTrailer());
        
        return sig.verify(pgpSig.getSignature());
    }
    
    public long getKeyID()
    {
        return sigPack.getKeyID();
    }
    
    public int getSignatureType()
    {
        return sigPack.getSignatureType();
    }
    
    public byte[] getEncoded() 
        throws IOException
    {
        ByteArrayOutputStream    bOut = new ByteArrayOutputStream();
        
        this.encode(bOut);
        
        return bOut.toByteArray();
    }
    
    public void encode(
        OutputStream    outStream) 
        throws IOException
    {
        BCPGOutputStream    out;
        
        if (outStream instanceof BCPGOutputStream)
        {
            out = (BCPGOutputStream)outStream;
        }
        else
        {
            out = new BCPGOutputStream(outStream);
        }

        out.writePacket(sigPack);
    }
}

@@ -646,20 +646,41 @@
    sig;%0A
+%0A
     
+private byte lastb;%0A
 %0A    PGP
@@ -1709,28 +1709,44 @@
 eption%0A    %7B
+%0A
     
+    lastb = 0;%0A
 %0A        try
@@ -2126,17 +2126,17 @@
 (b == '%5C
-n
+r
 ')%0A     
@@ -2232,35 +2232,57 @@
             
-    return;
+%7D%0A            else if (b == '%5Cn')
 %0A           
@@ -2274,33 +2274,33 @@
 n')%0A            
-%7D
+%7B
 %0A            els
@@ -2300,28 +2300,31 @@
     
-else
+   
  if (
+last
 b 
-=
+!
 = '%5Cr')%0A
     
@@ -2311,32 +2311,36 @@
 (lastb != '%5Cr')%0A
+    
             %7B%0A  
@@ -2357,29 +2357,211 @@
     
-return;%0A            %7D
+    sig.update((byte)'%5Cr');%0A                    sig.update((byte)'%5Cn');%0A                %7D%0A            %7D%0A            else%0A            %7B%0A                sig.update(b);%0A            %7D%0A%0A            lastb = b;
 %0A   
@@ -2567,33 +2567,51 @@
       %7D%0A        
+else
 %0A
+        %7B%0A    
         sig.upda
@@ -2617,26 +2617,32 @@
 ate(b);%0A
+        %7D%0A
     %7D%0A
-    
 %0A    pub

Update canonicalisation code in line with PGPSignature