[Webfunds-commits] java/webfunds/token AbstractParams.java AbstractPrivateParams.java AbstractPublicParams.java BaseToken.java Constants.java Factory.java TokenBuilder.java TokenDead.java TokenException.java TokenKeyException.java TokenPacketException.java TokenSigner.java TokenSpender.java TokenVersionException.java TokenTest.java

Ian Grigg iang@cypherpunks.ai
Sun, 1 Apr 2001 18:25:27 -0400 (AST)


iang        01/04/01 18:25:27

  Modified:    webfunds/token TokenTest.java
  Added:       webfunds/token/util Log.java
               webfunds/token AbstractParams.java
                        AbstractPrivateParams.java
                        AbstractPublicParams.java BaseToken.java
                        Constants.java Factory.java TokenBuilder.java
                        TokenDead.java TokenException.java
                        TokenKeyException.java TokenPacketException.java
                        TokenSigner.java TokenSpender.java
                        TokenVersionException.java
  Log:
  3rd generation model for tokens

Revision  Changes    Path
1.1                  java/webfunds/token/util/Log.java

Index: Log.java
===================================================================
/* $Id: Log.java,v 1.1 2001/04/01 22:25:23 iang Exp $
 *
 * Copyright (c) 2001 Systemics Inc. on behalf of
 * The WebFunds Development Team. All Rights Reserved.
 */
package webfunds.token.util;

public final class Log
{

    private Log() {}




    /**
     *  @return the quantity from a coin size, which is always logarithmic,
     *          if outside range, -1 is returned.
     */
    public static long log2qty(int log)
    {
        if (! (0 <= log && log <= 63) )
            return -1;
        if (log == 0)
            return 0;

        long q = 1;
        while (--log > 0)
            q <<= 1;
        return q;
    }

    /**
     *  @return a single largest coin size from a quantity.
     */
    public static int qty2log(long qty)
    {
        if (qty < 0)
            throw new IllegalArgumentException("qty < 0: " + qty);

        int i = 0;
        while (qty != 0)
        {
            qty >>= 1;
            i++;
        }
        return i;
    }

    /**
     *  @return an array representing the coin sizes
     *          required for this quantity
     */
    public static int[] qty2coins(long qty)
    {
        if (qty < 0)
            throw new IllegalArgumentException("qty < 0: " + qty);

        int num = 0;
        long q;
        for (q = qty; q != 0; q >>= 1)
        {
            if ( (q & 01) == 01 )
                num++;
        }
        int[] coins = new int[num];

        int i = 0;
        int size = 0;
        for (q = qty; q != 0; q >>= 1)
        {
            size++;
            if ( (q & 01) == 01 )
                coins[i++] = size;
        }

        return coins;
    }



////// Test Code /////////////////////////////////////

    public static void main(String[] args)
    {
        if (log2qty(0) != 0)
            throw new RuntimeException("log(0) != 0 : " + log2qty(0));
        if (log2qty(1) != 1)
            throw new RuntimeException("log(1) != 1 : " + log2qty(1));
        if (log2qty(4) != 8)
            throw new RuntimeException("log(4) != 8 : " + log2qty(4));
        if (log2qty(63) != 4611686018427387904L)
            throw new RuntimeException("log(63) != 4611686018427387904 : " +
                                       log2qty(63));
        if (qty2log(0) != 0)
            throw new RuntimeException("qty(0) != 0 : " + qty2log(0));
        if (qty2log(1) != 1)
            throw new RuntimeException("qty(1) != 1 : " + qty2log(1));
        if (qty2log(31) != 5)
            throw new RuntimeException("qty(31) != 5 : " + qty2log(31));
        if (qty2log(2147483647) != 31)
            throw new RuntimeException("qty(2147483647) != 31 : " +
                                       qty2log(2147483647));

        if (qty2coins(0).length != 0)
            throw new RuntimeException("coins(0) != 0 : " + qty2coins(0).length);
        if (qty2coins(1).length != 1)
            throw new RuntimeException("coins(1) != 1 : " + qty2coins(1).length);
        if (qty2coins(16).length != 1)
            throw new RuntimeException("coins(16) != 1 : " + qty2coins(16).length);
        if (qty2coins(17).length != 2)
            throw new RuntimeException("coins(17) != 2 : " + qty2coins(17).length);
        if (qty2coins(19).length != 3)
            throw new RuntimeException("coins(19) != 3 : " + qty2coins(19).length);

        for (int i = 0; i <= 16; i++)
        {
            int log = qty2log(i);
            long qty = log2qty(i);
            System.out.print(i + ": " + log + "\t" + qty + "\t" +
                                        qty2log(qty) + "\t" + log2qty(log));

            int[] coins = qty2coins(i);
            for (int j = coins.length - 1; j >= 0; j--)
                 System.out.print("\t" + coins[j]);

            System.out.println("");
        }
    }

}



1.3       +59 -11    java/webfunds/token/TokenTest.java

Index: TokenTest.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/token/TokenTest.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- TokenTest.java	2001/01/24 20:25:26	1.2
+++ TokenTest.java	2001/04/01 22:25:25	1.3
@@ -1,4 +1,4 @@
-/* $Id: TokenTest.java,v 1.2 2001/01/24 20:25:26 iang Exp $
+/* $Id: TokenTest.java,v 1.3 2001/04/01 22:25:25 iang Exp $
  *
  * Copyright (c) 2000-2001 Systemics Inc on behalf of
  * the WebFunds Development Team.  All Rights Reserved.
@@ -30,29 +30,39 @@
     private static PublicKey signkey;
 
     protected static void logmsg(String s)  { System.err.println(s); }
+
+    private static final String SIGN_ALG = "DSA";
     
-    public static void generate() throws DataFormatException, Exception {
-        // executed by Ivan
+    /**
+     *  Make a Keypair for below tests.
+     */
+    private static KeyPair generateDSAKeyPair() throws Exception {
         
         /*
-         *  Server generates a DSA sign key.
+         *  Server generates a DSA sign key for signing params.
          *  Normally the application would use an
          *  already established key for signing.
          */
         logmsg(". random");
         sr.nextInt();
-        KeyPairGenerator gen = KeyPairGenerator.getInstance("DSA");
+        KeyPairGenerator gen;
+        gen = KeyPairGenerator.getInstance(SIGN_ALG);
         logmsg(". DSA key " + gen);
         if (gen == null)
             throw new Exception("KPG returned null?");
         KeyPair pair = gen.generateKeyPair();
-        if (pair == null)
-            throw new Exception("generate returned null?");
+        return pair;
+    }
+
+    public static void generate(AbstractPrivateTokenParameters params)
+    throws DataFormatException, Exception {
+        // executed by Ivan
+        
+        KeyPair pair = generateDSAKeyPair();
         signkey = pair.getPublic();
         
         // The real work
-        logmsg(". Chaum params");
-        ChaumPrivateTokenParameters params = new ChaumPrivateTokenParameters();
+        logmsg(". sign params");
         params.generate(sr, denominations, currencyID, series, expiry);
         params.sign(pair.getPrivate(), "DSA", null, sr);
         
@@ -62,6 +72,24 @@
         privparams = params.encodePrivateData();
     }
 
+    public static void generateChaum() throws DataFormatException, Exception {
+        // executed by Ivan
+        
+        // The real work
+        logmsg(". Chaum params");
+        ChaumPrivateTokenParameters params = new ChaumPrivateTokenParameters();
+        generate(params);
+    }
+
+    public static void generateRandom() throws DataFormatException, Exception {
+        // executed by Ivan
+        
+        // The real work
+        logmsg(". Random params");
+        RandomPrivateTokenParameters params = new RandomPrivateTokenParameters();
+        generate(params);
+    }
+
 
 // Step 2: generating prototokens on the client
 //.............................................................................
@@ -206,8 +234,28 @@
 //.............................................................................
 
     public static void main(String[] args) throws Exception {
-        logmsg("generate");
-        generate();
+        testRandom();
+        testChaum();
+    }
+
+    public static void testChaum() throws Exception {
+        logmsg("Chaum generate");
+        generateChaum();
+        logmsg("createprotos");
+        createprotos();
+        logmsg("signblinded");
+        signblinded();
+        logmsg("unblindsigned");
+        unblindsigned();
+        logmsg("transfercoinclient");
+        transfercoinclient();
+        logmsg("transfercoinsserver");
+        transfercoinsserver();
+    }
+    
+    public static void testRandom() throws Exception {
+        logmsg("Random generate");
+        generateRandom();
         logmsg("createprotos");
         createprotos();
         logmsg("signblinded");



1.1                  java/webfunds/token/AbstractParams.java

Index: AbstractParams.java
===================================================================
/*
 * $Id: AbstractParams.java,v 1.1 2001/04/01 22:25:24 iang Exp $
 *
 * Copyright (c) Systemics Inc 1995-2000 on behalf of
 * the WebFunds Development Team.  All Rights Reserved.
 */
package webfunds.token;

import java.io.InputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.DataInputStream;
import java.io.OutputStream;
import java.io.DataOutputStream;

import webfunds.utils.Hex;
import webfunds.utils.Support;

import webfunds.sox.Encodable;
import webfunds.sox.SOXPacketException;

import webfunds.token.util.Log;

/**
 *  Represents a Private (signing) key for a token
 *  {item, series, expiry, log} tuple.
 *
 *  Refer to AbstractPublicParams for common methods.
 */
public abstract class AbstractParams
    extends Encodable
{
    /**
     *  The version number for this structure:
     *         0:     current
     */
    public static final int PARAMS_VERSION = 0;
    protected int version = PARAMS_VERSION;
    public final int getVersion()               { return version; }

    /**
     * The type of token of this class
     * See Factory for current definitions.
     */
    protected int type;
    public int getType()                        { return type; }

    /**
     *  The version of the subclass.
     *
     */
    protected int subversion;
    public int getSubVersion()                  { return subversion; }




    /**
     *  The quantity of the item that this token represents.   
     *  Encoded in logarithm - base 2 of the quantity.
     *  
     *         Log    Qty
     *          0      0
     *          1      1
     *          2      2
     *          3      4
     *          4      8
     *
     *  and so forth.  -1 could be used for "not stated", meaning it is
     *  encoded in the magic parts (by use of certain key, for example).
     */
    protected int log;
    public int getLog()             { return log; }

    /**
     *  Note (1) that this may be derived information, the real
     *  token value may be encoded in the signature key or
     *  some other way.
     *
     *  Note (2) that the unit of account is not encoded within
     *  the token, the higher layer code should remember that.
     *  See Payments for the encoded Item (a.k.a. Ricardian Contract).
     *
     *  @return quantity of the item that this token represents,
     *          being 0 - n representing which bit,
     *          or -1 if "not so encoded"  
     */
    public long getQty()             { return Log.log2qty(log); }

    /**
     *  Tokens commonly come from a batch that is identified
     *  with a series label or an expiry date.
     *  These are interpreted by the subclass, may be ignored.
     */
    protected long expiry;
    public long getExpiry()                     { return expiry; }
    protected byte[] series;
    public byte[] getSeries()                   { return series; }

    protected byte[] item;
    public byte[] getItem()                     { return item; }



    /**
     *  Create an uninitialised Private Params.
     *
     *  @param subversion is the version of the parent coin class
     *  @param type of token, being the blinding or coin scheme
     *  @param item is the description of which instrument relates
     *  @param series is the short descriptor of which set of tokens this is
     *  @param expiry date on which this series will expire
     *  @param log the coin size, log base 2 of quantity
     */
    AbstractParams(int subversion,
                         int tokenType,
                         byte[] series, long expiry,
                         byte[] item,
                         byte log)
    {
        this.type       = tokenType;
        this.subversion = subversion;
        this.series     = series;
        this.expiry     = expiry;
        this.item       = item;
        this.log        = log;
    }

    /**
     *  Reconstruct the object from a previously encoded byte array.
     *
     *  @param buf the previously encoded object
     *  @excep TokenPacketException if the data is badly formatted
     */
    public AbstractParams(byte[] buf)
        throws TokenPacketException
    {
        try {
            decode(buf);
        } catch (SOXPacketException ex) {
            throw new TokenPacketException("IOEx: " + ex);
        }
    }

    /**
     *  Reconstruct the object from data in an input stream.
     *
     *  @param is the input stream from which to read the data
     *  @excep TokenPacketException if the data is badly formatted
     */
    public AbstractParams(InputStream is)
        throws TokenPacketException
    {
        try {
            decode(is);
        } catch (IOException ex) {   // compiler doesn't insist, but thrown!
            throw new TokenPacketException("IOEx: " + ex);
        }
    }


    /**
     *  Update this params object with the values from
     *  a token encoded as a byte array (such as previously
     *  returned from the encode() method of a token object).
     *
     *  @param token the previosly encoded token
     */
    public void decode(InputStream is)
        throws IOException
    {
        DataInputStream dis = new DataInputStream(is);

        int v = dis.readUnsignedByte();
        if (v != PARAMS_VERSION)
            throw new IOException /*TokenPacketException*/ ("Invalid version in token: "+
                                         v + " != " + PARAMS_VERSION);
        version = v;

        /*
         *  Type and subversion should be interpreted by subclass.
         */
        type = dis.readUnsignedByte();
        subversion = dis.readUnsignedByte();


        log = dis.readUnsignedByte();
        expiry = dis.readInt();
        series = readByteArray(dis);
        item = readByteArray(dis);

    }

    /**
     * Encode a params as a byte array, suitable for
     * sending to third parties for depositing.
     * If the signature is not present, an unsigned
     * token will be encoded.
     *
     * @return byte[] the token in encoded form
     */
    public void encode(OutputStream os)
        throws IOException
    {
        DataOutputStream dos = new DataOutputStream(os);
        dos.writeByte(version);
        dos.writeByte(type);
        dos.writeByte(subversion);

        dos.writeByte(log);
        dos.writeInt((int)expiry);
        writeByteArray(dos, series);
        writeByteArray(dos, item);
    }



////// Self-Test //////////////////////////////////

    public String toString()
    {
        String s = vString();
        s += " type: (" + type + ") " + Factory.getTypeString(type);
        s += " qty: (" + log + ") " + Log.log2qty(log);
        if (expiry > 0)
            s += " exp: " + expiry;
        if ((series != null) && (series.length > 0))
            s += Hex.printable(series);
        if ((item != null) && (item.length > 0))
            s += Hex.data2hex(item);

        return s;
    }

    public String vString()
    {
        return "V" + subversion + " (" + version + ")";
    }

    protected boolean equals(AbstractParams other)
    {
        if (other.version != version)
            return false;
        if (other.type != type)
            return false;
        if (other.subversion != subversion)
            return false;

        if (other.log != log)
            return false;
        if (other.expiry != expiry)
            return false;
        if (!Support.equals(series, other.series))
            return false;
        if (!Support.equals(item, other.item))
            return false;

        return true;
    }


}



1.1                  java/webfunds/token/AbstractPrivateParams.java

Index: AbstractPrivateParams.java
===================================================================
/*
 * $Id: AbstractPrivateParams.java,v 1.1 2001/04/01 22:25:24 iang Exp $
 *
 * Copyright (c) Systemics Inc 1995-2000 on behalf of
 * the WebFunds Development Team.  All Rights Reserved.
 */
package webfunds.token;

import java.io.InputStream;


/**
 *  Represents a Private (signing) capability for a token
 *  where each capability includes a {item, series, expiry, log} tuple.
 *
 *  Refer to AbstractPublicParams for common methods.
 */
public abstract class AbstractPrivateParams
    extends AbstractParams
{



    /**
     *  Create an uninitialised Private Params.
     */
    public AbstractPrivateParams(int subversion,
                         int tokenType,
                         byte[] series, long expiry,
                         byte[] item,
                         byte log)
    {
        super(subversion,
                         tokenType,
                         series, expiry,
                         item,
                         log);
    }

    /**
     *  Reconstruct the object from a byte array.
     *
     *  @param buf the previously encoded object
     *  @excep TokenPacketException if the data is badly formatted
     */
    public AbstractPrivateParams(byte[] buf)
        throws TokenPacketException
    {
        super(buf);
    }

    /**
     *  Reconstruct the object from data in an input stream.
     *
     *  @param is the input stream from which to read the data
     *  @excep TokenPacketException if the data is badly formatted
     */
    public AbstractPrivateParams(InputStream is)
        throws TokenPacketException
    {
        super(is);
    }





////// Self-Test //////////////////////////////////

    public String vString()
    {
        return "priv V" + subversion + " (" + version + ")";
    }

}



1.1                  java/webfunds/token/AbstractPublicParams.java

Index: AbstractPublicParams.java
===================================================================
/*
 * $Id: AbstractPublicParams.java,v 1.1 2001/04/01 22:25:24 iang Exp $
 *
 * Copyright (c) Systemics Inc 1995-2000 on behalf of
 * the WebFunds Development Team.  All Rights Reserved.
 */
package webfunds.token;

import java.io.InputStream;

/**
 *  This class represents a Public (verifying) key for a token.
 *
 */
public abstract class AbstractPublicParams
    extends AbstractParams
{


    /**
     *  Create an uninitialised Public Params.
     *
     *  @param subversion is the version of the parent coin class
     *  @param type of token, being the blinding or coin scheme
     *  @param item is the description of which instrument relates
     *  @param series is the short descriptor of which set of tokens this is
     *  @param expiry date on which this series will expire
     *  @param log the coin size, log base 2 of quantity
     */
    public AbstractPublicParams(int subversion,
                         int tokenType,
                         byte[] series, long expiry,
                         byte[] item,
                         byte log)
    {
        super(subversion,
                         tokenType,
                         series, expiry,
                         item,
                         log);
    }

    /**
     *  Construct a token object from a byte array
     *  that was previously returned from the encode()
     *  method of a token object.
     *
     *  @param token the previously encoded token
     *  @excep SOXPacketException The token data is badly formatted
     */
    public AbstractPublicParams(byte[] buf)
        throws TokenPacketException
    {
        super(buf);
    }

    /**
     *  Construct a token object from data in an input stream,
     *  where the data was previously returned from the encode()
     *  method of a token object.
     *
     *  @param is the input stream from which to read the token data
     *  @excep SOXPacketException The token data is badly formatted
     */
    public AbstractPublicParams(InputStream is)
        throws TokenPacketException
    {
        super(is);
    }




}



1.1                  java/webfunds/token/BaseToken.java

Index: BaseToken.java
===================================================================
/*
 * $Id: BaseToken.java,v 1.1 2001/04/01 22:25:24 iang Exp $
 *
 * Copyright (c) Systemics Inc 1995-2000 on behalf of
 * the WebFunds Development Team.  All Rights Reserved.
 */
package webfunds.token;

import java.io.InputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.DataInputStream;
import java.io.OutputStream;
import java.io.DataOutputStream;

import java.security.SecureRandom;
import java.security.PrivateKey;
import java.security.KeyException;
import java.security.NoSuchAlgorithmException;
import java.security.MessageDigest;

import webfunds.utils.Hex;
import webfunds.utils.Panic;

import webfunds.sox.Encodable;
import webfunds.sox.SOXPacketException;

import webfunds.token.utils.Log;

/**
 *  This class represents a token (a.k.a. coin) throughout its lifecycle.
 *  Token objects are usually created by means of a Withdrawal
 *  protocol between client and mint-capable SOXServer.
 *  They go through several phases, being potentially:
 *            proto
 *            signed
 *            blinded
 *            spent
 *  which are handled by extending classes as phases.
 */
public abstract class BaseToken
    extends Encodable
{
    /**
     *  The version number for BaseToken structure:
     *         0:     current
     */
    public static final int TOK_ORIGINAL = 0;
    private int version;
    public final int getVersion()               { return version; }

    /**
     *  The Phase of the Token structure:
     *
     *  The Token Phase structure is the immediately extending one:
     *  e.g., TokenBuilder, TokenSigner, TokenSpender, TokenDead.
     */
    public static final int            PHASE_BUILDER = 0, // not valued as yet
                                       PHASE_SIGNER  = 1, // at mint, valued
                                       PHASE_SPEND   = 2, // in spender
                                       PHASE_DEAD    = 3; // delete this

    protected int phase;
    public final int getPhase()         { return phase; }
    public String getPhaseString() {return isValidPhase()?ps[phase]:"? "+phase;}

    public static final String[] ps = {"builder", "signer", "spender", "dead"};

    public final boolean isValidPhase() { return ((0<=phase) && (phase<=3)); }
    public final boolean isBuilder()    { return state == PHASE_BUILDER; }
    public final boolean isSigner()     { return state == PHASE_SIGNER; }
    public final boolean isSpender()    { return state == PHASE_SPEND; }
    public final boolean isDead()       { return state == PHASE_DEAD; }


    /**
     *  The version number for Token Phase structure is defined by
     *  the phase.
     */
    protected int phaseVersion;
    public final int getPhaseVersion()          { return phaseVersion; }

    /**
     *  The type of token of this class, e.g., Wagner.
     *  See Factory for current definitions.
     */
    protected int type;
    public int getType()                        { return type; }

    /**
     *  The version of the subclass Token that sets the type.
     */
    protected int subversion;
    public int getSubVersion()                  { return subversion; }



    /**
     *  The state that the token is the state machine within the phase.
     *  How it is set and advanced is up to the phase class.
     *  Only the low-order single unsigned byte is saved & restored.
     */
    protected int state;
    public int getState()                       { return state; }
    public void setState(int state)             { this.state = state; }



    /**
     *  The quantity of the item that this token represents.
     *  Encoded in logarithm - base 2 of the quantity.
     *
     *         Log    Qty
     *          0      0
     *          1      1
     *          2      2
     *          3      4
     *          4      8
     *
     *  and so forth.  -1 could be used for "not stated", meaning it is
     *  encoded in the magic parts (by use of certain key, for example).
     */
    protected int log;
    public int getLog()             { return log; }

    /**
     *  Note (1) that this may be derived information, the real
     *  token value may be encoded in the signature key or
     *  some other way.
     *
     *  Note (2) that the unit of account is not encoded within
     *  the token, the higher layer code should remember that.
     *  See Payments for the encoded Item (a.k.a. Ricardian Contract).
     *
     *  @return quantity of the item that this token represents,
     *          being 0 - n representing which bit,
     *          or -1 if "not so encoded"
     */
    public long getQty()             { return Log.log2qty(log); }

    /**
     *  Tokens commonly come from a batch that is identified
     *  with a series label or an expiry date.
     *  These are interpreted by the subclass, may be ignored.
     */
    protected long expiry;
    public long getExpiry()                     { return expiry; }
    protected byte[] series;
    public byte[] getSeries()                   { return series; }

    /**
     *  The item that this token relates to.
     *  Normally a Ricardian hash.
     */
    protected byte[] item;
    public byte[] getItem()                     { return item; }


    /**
     *  Create an uninitialised token.
     */
    public BaseToken()
    {
        this.version = TOK_ORIGINAL;
        this.phase   = PHASE_BUILDER;
    }

    /**
     *  Construct a token object from a byte array
     *  that was previously returned from the encode()
     *  method of a token object.
     *
     *  @param token the previously encoded token
     *  @excep SOXPacketException The token data is badly formatted
     */
    public BaseToken(byte[] buf)
        throws TokenPacketException
    {
        try {
            decode(buf);
        } catch (SOXPacketException ex) {
            throw new TokenPacketException("SOXPEx: " + ex);
        }
    }

    /**
     *  Construct a token object from data in an input stream,
     *  where the data was previously returned from the encode()
     *  method of a token object.
     *
     *  @param is the input stream from which to read the token data
     *  @excep SOXPacketException The token data is badly formatted
     */
    public BaseToken(InputStream is)
        throws TokenPacketException
    {
        try {
            decode(is);
        } catch (IOException ex) {   // compiler doesn't insist, but thrown!
            throw new TokenPacketException("IOEx: " + ex);
        }
    }

 
// Init methods
//.............................................................................
 


    private byte[] id = null;
       
    /**
     *  Get the unique identifier that is used for checking against
     *  double-spending.
     *  Normally called by server on depositing.
     *  Only makes sense if in PHASE_SIGNER or later?
     *  Or useful as id in all phases?
     *
     *  @return a byte array with the unique id, else null if not good state
    public abstract byte[] getUniqueId();
     */
    public byte[] getUniqueId()
    {
        return id ;
    }

    public final static String noMD = "Hash algorithm not found. Probably a "+
                                       "provider problem.";
       

    /**
     *  Calculate a unique id on a string of bytes.
     *  Hmm, does this mean anyone can set the id?
     */
    public void setUniqueId(byte[] sourceData)
    {
        if (id != null)              // once only
            return ;
        
        MessageDigest md;
        try {
            md = MessageDigest.getInstance(Constants.HASH_ALG);
        } catch (NoSuchAlgorithmException nsae) {
            throw new Panic(noMD);
        }
            
        id = md.digest(sourceData);
    }



// Input/Output methods
//.............................................................................

    /**
     *  Update this token object with the values from
     *  a token encoded as a byte array (such as previously
     *  returned from the encode() method of a token object).
     *
     *  @param token the previously encoded token
     */
    public void decode(InputStream is)
        throws IOException
    {
        DataInputStream dis = new DataInputStream(is);

        int v = dis.readUnsignedByte();
        if (v != TOK_ORIGINAL)
            throw new IOException("Invalid version in token: "+
                                         v + " != " + TOK_ORIGINAL);
        version = v;

        /*
         *  Phase, phaseV, Type and subV should be interpreted by subclass.
         */
        phase         = dis.readUnsignedByte();
        phaseVersion  = dis.readUnsignedByte();
        type          = dis.readUnsignedByte();
        subversion    = dis.readUnsignedByte();
        state         = dis.readUnsignedByte();
        log           = dis.readUnsignedByte();
    }

    /**
     * Encode a token as a byte array, suitable for
     * saving state.
     *
     * @return byte[] the token in encoded form
     */
    public void encode(OutputStream os)
        throws IOException
    {
        DataOutputStream dos = new DataOutputStream(os);
        dos.writeByte(version);

        dos.writeByte(phase);
        dos.writeByte(phaseVersion);
        dos.writeByte(type);
        dos.writeByte(subversion);
        dos.writeByte(state);
        dos.writeByte(log);
//expiry?
    }



////// Self-Test //////////////////////////////////

    public String toString()
    {
        String s = " phase == " + getPhaseString();
        s += "    state == " + state;
        s += "   " + vString();

        return s;
    }

    public String vString()
    {
        return "V" + subversion + " (" + phaseVersion + "," + version + ")";
    }

    public boolean equals(java.lang.Object obj)
    {
        if (obj == null || !(obj instanceof BaseToken))
            return false;

        BaseToken other = (BaseToken)obj;

        if (other.version != version)
            return false;
        if (other.phase != phase)
            return false;
        if (other.phaseVersion != phaseVersion)
            return false;
        if (other.type != type)
            return false;
        if (other.subversion != subversion)
            return false;
        if (other.state != state)
            return false;
        if (other.log != log)
            return false;
        if (other.expiry != expiry)
            return false;

        return true;
    }

    /* public abstract STATIC BaseToken example(); */

}



1.1                  java/webfunds/token/Constants.java

Index: Constants.java
===================================================================
/* $Id: Constants.java,v 1.1 2001/04/01 22:25:24 iang Exp $
 *
 * Copyright (c) 2000-2001 Systemics Inc on behalf of
 * the WebFunds Development Team.  All Rights Reserved.
 */
package webfunds.token;

/**
 * Constants for the tokens
 *
 * @version $Revision: 1.1 $
 * @author Edwin Woudt <edwin@webfunds.org>
 */
public final class Constants {

    private Constants() {} // static constants only
    
    public static final int    HASH_SIZE   =  160/8;
    public static final String HASH_ALG    = "SHA-1";
    
}



1.1                  java/webfunds/token/Factory.java

Index: Factory.java
===================================================================
/* $Id: Factory.java,v 1.1 2001/04/01 22:25:24 iang Exp $
 *
 * Copyright (c) 2001 Systemics Inc. on behalf of
 * The WebFunds Development Team. All Rights Reserved.
 */
package webfunds.token;

import java.security.SecureRandom;

import webfunds.token.random.RandomPair;
import webfunds.token.chaum.ChaumPair;

public final class Factory
{
    /**
     *  Each Token technology type gets a number.
     *
     *  0 and 1 are not Tokens, just there to make it compatible
     *  with the Payment types.  May have to change when new
     *  Payment types are added.
     *
     *  Use the numbers 100 - 110 for experimental methods,
     *  not to be used outside your own private circle.
     *  Once you are ready, ask the WebFunds team to allocate
     *  you a fixed number from the fixed blocks.
     */
    public static final int        NONE = 0,
                                   SOX_CHEQUE = 1,
                                   RANDOM_TOKEN = 2,
                                   WAGNER_TOKEN = 3,
                                   CHAUM_TOKEN = 4,

                                   START_EXPERIMENTAL = 100,
                                   END_EXPERIMENTAL = 110;

    public static boolean isTokenPayment(int i) { return (2<=i) && (i<=4); }

    public static final String[] typeNames = {
                                   "None?",
                                   "SOX Cheque?",
                                   "Random Token", 
                                   "Wagner Token",
                                   "Chaum Token"
                                  };

    /**
     *  A short string that can be used in an encoding.
     *  Used to generate Float account names.
     */
    public static final String[] base64Type = {
                                   null,
                                   null,
                                   "Random", 
                                   "Wagner",
                                   "Chaum"
                                  };

    protected Factory() {}



    public static BaseToken decode(byte[] buf)
        throws TokenPacketException
    {
        
        if (buf == null)
            throw new IllegalArgumentException("PaymentFactory.decode(null)");

        int av = buf[0];            // abstract version 0;
        int typ = buf[1];           // within factory, type of AbstractPayment
        int sv = buf[2];            // within type of Token, subversion
        String s = "av=" + av + ", tok-type=" + typ + ", sv=" + sv;
System.err.println("analysing token: " + s);

        /*
         *  First, check if it is the old pre-factory form of payment.
         *  The first byte was zero for those, so the Factory version
         *  starts at 1.
         */
        if (av != BaseToken.TOK_ORIGINAL)
            throw new TokenPacketException("Not Token: " + s);

        /*
         *  It's a Factory Token version.
         *  The second byte written by BaseToken determines
         *  which one to recover.
         */
        BaseToken t;
        /*
        if (typ == RANDOM_TOKEN)
            t = new RandomToken(buf);
        else if (typ == WAGNER_TOKEN)
            t =  new WagnerToken(buf);
        else if (typ == CHAUM_TOKEN)
            t =  new ChaumToken(buf);
        else
        */
            throw new TokenPacketException("Unknown FactoryPayment: " + s);

        // return t;
    }

    /**
     *  Is this a valid type, at least according to the ones known
     *  about here?
     *
     *  @return false if not a known type, else true
     */
    public static boolean valid(int type)
    {
        if (type <= 1)
            return false;
        if (type >= 5)
            return false;
        return true ;
    }

    /**
     *  Return a diags string for this type.
     */
    public static String getTypeString(int type)
    {
        if (!valid(type))
        {
            if ((START_EXPERIMENTAL <= type) && (type <= END_EXPERIMENTAL))
                return "experimental " + " (" + type + ")";

            return "<" + type + " invalid>";
        }
        return typeNames[type] + " (" + type + ")";
    }

    /**
     *  Return a the type number for this payment name.
     *  Meant for converting user-land strings to the number.
     *
     *  @return the type number constant, or NONE if unknown
     */
    public static int getType(String name)
    {
        if (name == null || name.length() < 0)
            return NONE;

        for (int i = 0; i < base64Type.length; i++)
        {
            if (name.equals(base64Type[i]))
                return i;
        }

        for (int i = 0; i < typeNames.length; i++)
        {
            if (name.equals(typeNames[i]))
                return i;
        }

        return NONE;
    }

    /**
     *  Get me a PairParams for some token series.
     */
    public static ParamsPair createPair(int typ)
    {
        
        if (!valid(typ))
            throw new IllegalArgumentException("PF.createKeyPair("+typ+"???)");

        ParamsPair kp;

        if (typ == RANDOM_TOKEN)
            kp = new RandomPair();
        /*
        else if (typ == WAGNER_TOKEN)
            kp = new WagnerPair();
        */
        else if (typ == CHAUM_TOKEN)
            kp = new ChaumPair();
        else
            throw new IllegalArgumentException("createKeyPair("+typ+")");

        return kp;
    }

    /**
     *  Get me a ParamsPair for some token series.
     */
    public static ParamsPair createPair(SecureRandom sr, int typ,
                                                 byte[] item,
                                                 byte[] series,
                                                 long expiry,
                                                 byte log)
        throws TokenKeyException
    {
        
        if (series == null)
            throw new IllegalArgumentException("PF.createKeyPair("+series+"?)");
        if (item == null)
            throw new IllegalArgumentException("PF.createKeyPair("+item+"??)");
        if (log < 0)
            throw new IllegalArgumentException("PF.createKeyPair("+log+"????)");

        ParamsPair kp = createPair(typ);
        kp.generate(sr, item, series, expiry, log);
        return kp;
    }

    /**
     *  Get me a list of proto Tokens for a particular type.
     *  This is the first step in withdrawing coins.
     *  @throws IllegalArgumentException if typ unknown or amount < 0
     */
    public static BaseToken[] getProtoTokens(int typ, long amount)
        throws IllegalArgumentException
    {
        
        if (!valid(typ))
            throw new IllegalArgumentException("PF.createKeyPair("+typ+"?)");
        if (amount < 0)
            throw new IllegalArgumentException("PF.createKeyPair("+amount+"?)");

        BaseToken[] tokens = null;

        /*
        if (typ == RANDOM_TOKEN)
            tokens = RandomProtoToken.getProtoTokens(amount);
        else if (typ == WAGNER_TOKEN)
            tokens = WagnerToken.getProtoTokens(amount);
        else
        if (typ == CHAUM_TOKEN)
            tokens = ChaumProtoToken.getProtoTokens(amount);
        else
            throw new IllegalArgumentException("getProtoTokens("+typ+")");
        */

        return tokens;
    }





////// Test Code /////////////////////////////////////

    public static int exampleType()
    {
        return RANDOM_TOKEN;
    }

    public static BaseToken example()
    {
        return example(exampleType());
    }

    public static BaseToken example(int type)
    {
        BaseToken t = null;

        /*
        if (type == RANDOM_TOKEN)
            t = RandomToken.example();
        else if (type == WAGNER_TOKEN)
            t = WagnerToken(buf);
        else if (type == CHAUM_TOKEN)
            t = ChaumToken.example();
        else
            throw new RuntimeException("unknown AbstractPayment: " + type);
        */

        return t;

    }

}



1.1                  java/webfunds/token/TokenBuilder.java

Index: TokenBuilder.java
===================================================================
/*
 * $Id: TokenBuilder.java,v 1.1 2001/04/01 22:25:24 iang Exp $
 *
 * Copyright (c) Systemics Inc 1995-2000 on behalf of
 * the WebFunds Development Team.  All Rights Reserved.
 */
package webfunds.token;

import java.io.InputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.DataInputStream;
import java.io.OutputStream;
import java.io.DataOutputStream;

import java.security.SecureRandom;
import java.security.PrivateKey;
import java.security.KeyException;

import webfunds.utils.Hex;

import webfunds.sox.Encodable;
import webfunds.sox.SOXPacketException;

import webfunds.token.util.Log;

/**
 *  This class represents a proto token -- a coin before withdrawal.
 *
 */
public abstract class TokenBuilder
    extends BaseToken
{
    /**
     *  The version number for this structure:
     *         0:     current
     */
    public static final int BUILDER_ORIGINAL = 0;



    /**
     *  The state that the token is in within this phase
     *  (generally made by the mint according to the protocol).
     *  How it is set and advanced is up to the higher layers.
     *  May be ignored, may use the following TOK numbers.
     *  Only the low-order single unsigned byte is saved & restored.
     */
    public static final int            TOK_RAW      = 0,  // empty, valueless
                                       TOK_UNSAVED  = 1,  // blinded, unsaved
                                       TOK_PROTO    = 2;  // clean, valueless

    public boolean isRaw()             { return state == TOK_RAW; }
    public boolean isUnSaved()         { return state == TOK_UNSAVED; }
    public boolean isProto()           { return state == TOK_PROTO; }

    protected void setBlinded()        { this.state = TOK_UNSAVED; }
    /** Can be called to permit output for withdrawal.  */
    protected void setProto()          { this.state = TOK_PROTO; }



    /**
     *  User blinds a raw coin and turns it into a Protocoin.
     *  Some sort of signing process involved, done internally.
     *
     *  Normally called by client (user), so it is standardised.
     *  How it is done is up to the individual type of token, but
     *  we hope we can limit it to this simple interface, as there
     *  are half a dozen different token crypto-schemes out there.
     *
     *  The sub TokenBuilder needs to save any blinding params and
     *  acquire the signed information from the unblind call, below.
     *
     *  @param sr is the source of randomness needed by some methods
     *  @param params includes the blinding paramaters from the mint
     */
    public abstract void proto(SecureRandom sr, AbstractPublicParams params)
        throws KeyException;

    /**
     *  Called by extending class to save the params details.
     *
     *  The series is unknown until signing (mint can use a different key).
     *  IS THIS TRUE?  BLINDING FACTORS MUST BE KNOWN IN ADVANCE?
     *
     *  @param params includes the blinding paramaters from the mint
     *  @except IllegalArgumentException if wrong extended params are passed
     */
    protected void proto(AbstractPublicParams params)
    {
        this.type       = params.getType();
        this.subversion = params.getSubVersion();
        this.log        = params.getLog();
        this.item       = params.getItem();

        this.expiry     = 0;
        this.series     = null;
        this.state = TOK_UNSAVED;
    }



    /**
     *  Create a list of proto-Tokens ready for withdrawal.
     *  This is a static call.  See PaymentFactory.getProtoTokens()
     *
     *  @param amount is the total quantity of value in the tokens
     *  @except SOXArgsException if amount is invalid
     *
    public static abstract BaseToken[] getProtoTokens(long amount)
        throws SOXArgsException;
     */



    /**
     *  Create an uninitialised token builder.
     *  Call prototype() with some params to make it a real proto-token.
     *
     *  @param type of token, being the blinding or coin scheme
     *  @param subversion is the version of the parent coin class
     *  @param log the coin size, log base 2 of quantity
     */
    public TokenBuilder()
    {
        super();

        this.phase        = PHASE_BUILDER;
        this.phaseVersion = BUILDER_ORIGINAL;
        this.state        = TOK_RAW;
    }

    /**
     *  Construct a token object from a byte array
     *  that was previously returned from the encode()
     *  method of a token object.
     *
     *  @param token the previously encoded token
     *  @excep SOXPacketException The token data is badly formatted
     */
    public TokenBuilder(byte[] buf)
        throws TokenPacketException
    {
        super(buf);
    }

    /**
     *  Construct a token object from data in an input stream,
     *  where the data was previously returned from the encode()
     *  method of a token object.
     *
     *  @param is the input stream from which to read the token data
     *  @excep SOXPacketException The token data is badly formatted
     */
    public TokenBuilder(InputStream is)
        throws TokenPacketException
    {
        super(is);
    }



    /**
     *  Update this token object with the values from
     *  a token encoded as a byte array (such as previously
     *  returned from the encode() method of a token object).
     *
     *  @param token the previosly encoded token
     */
    public void decode(InputStream is)
        throws IOException
    {
        DataInputStream dis = new DataInputStream(is);

        super.decode(dis);
        if (phase != PHASE_BUILDER)
            throw new IOException("Invalid phase in token: "+
                                phase + " != " + PHASE_BUILDER);
        if (phaseVersion != BUILDER_ORIGINAL)
            throw new IOException("Invalid phase version in token: "+
                                phaseVersion + " != " + BUILDER_ORIGINAL);

    }

    /**
     * Encode a token as a byte array, suitable for
     * sending to third parties for depositing.
     * If the signature is not present, an unsigned
     * token will be encoded.
     *
     * @return byte[] the token in encoded form
     */
    public void encode(OutputStream os)
        throws IOException
    {
        DataOutputStream dos = new DataOutputStream(os);
        super.encode(dos);
    }



////// Self-Test //////////////////////////////////

    public String toString()
    {
        String s = super.toString();
        if (isRaw())
            return s;
        s += "    L: " + log + "\tQ: " + Log.log2qty(log);

        return s;
    }



}



1.1                  java/webfunds/token/TokenDead.java

Index: TokenDead.java
===================================================================
/*
 * $Id: TokenDead.java,v 1.1 2001/04/01 22:25:24 iang Exp $
 *
 * Copyright (c) Systemics Inc 1995-2000 on behalf of
 * the WebFunds Development Team.  All Rights Reserved.
 */
package webfunds.token;

import java.io.InputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.DataInputStream;
import java.io.OutputStream;
import java.io.DataOutputStream;

import java.security.SecureRandom;
import java.security.PrivateKey;
import java.security.KeyException;

import webfunds.utils.Hex;

import webfunds.sox.Encodable;
import webfunds.sox.SOXPacketException;

/**
 *  This class represents a dead token for recovery purposes.
 *  Once spent and the user no longer requires a record of
 *  the coin, this can be used as a place marker with no info.
 *  If found in recovery, kill it.  Hmm.  Should have an ID.
 */
public abstract class TokenDead
    extends BaseToken
{
    /**
     *  The version number for this structure:
     *         0:     current
     */
    public static final int DEAD_ORIGINAL = 0;



    /**
     *  The state that the token is in within this phase
     *  (generally made by the mint according to the protocol).
     *  How it is set and advanced is up to the higher layers.
     *  May be ignored, may use the following TOK numbers.
     *  Only the low-order single unsigned byte is saved & restored.
     */
    public static final int            TOK_DEAD     = 0;  // drop this one



    /**
     *
     *  @param uniqueId is the one to kill during recovery
     */
    public abstract void dead(byte[] uniqueId);



    /**
     *  Create an uninitialised token.
     *  Call prototype() with some params to make it a real proto-token.
     *
     *  The series is unknown until signing (mint can use a different key).
     *
     *  @param type of token, being the blinding or coin scheme
     *  @param subversion is the version of the parent coin class
     *  @param log the coin size, log base 2 of quantity
     */
    public TokenDead()
    {
        super();

        this.phase        = PHASE_DEAD;
        this.phaseVersion = DEAD_ORIGINAL;
        this.state        = TOK_DEAD;
    }

    /**
     *  Construct a token object from a byte array
     *  that was previously returned from the encode()
     *  method of a token object.
     *
     *  @param token the previously encoded token
     *  @excep SOXPacketException The token data is badly formatted
     */
    public TokenDead(byte[] buf)
        throws TokenPacketException
    {
        super(buf);
    }

    /**
     *  Construct a token object from data in an input stream,
     *  where the data was previously returned from the encode()
     *  method of a token object.
     *
     *  @param is the input stream from which to read the token data
     *  @excep SOXPacketException The token data is badly formatted
     */
    public TokenDead(InputStream is)
        throws TokenPacketException
    {
        super(is);
    }



    /**
     *  Update this token object with the values from
     *  a token encoded as a byte array (such as previously
     *  returned from the encode() method of a token object).
     *
     *  @param token the previosly encoded token
     */
    public void decode(InputStream is)
        throws IOException
    {
        DataInputStream dis = new DataInputStream(is);

        super.decode(dis);
        if (phase != PHASE_DEAD)
            throw new IOException("Invalid phase in token: "+
                                phase + " != " + PHASE_DEAD);
        if (phaseVersion != DEAD_ORIGINAL)
            throw new IOException("Invalid phase version in token: "+
                                phaseVersion + " != " + DEAD_ORIGINAL);

    }

    /**
     * Encode a token as a byte array, suitable for
     * sending to third parties for depositing.
     * If the signature is not present, an unsigned
     * token will be encoded.
     *
     * @return byte[] the token in encoded form
     */
    public void encode(OutputStream os)
        throws IOException
    {
        DataOutputStream dos = new DataOutputStream(os);
        super.encode(dos);
    }



////// Self-Test //////////////////////////////////

    public String toString()
    {
        String s = "    state == " + state;
        if (isDead())
            return s;

        return s;
    }

}



1.1                  java/webfunds/token/TokenException.java

Index: TokenException.java
===================================================================
/*
 * $Id: TokenException.java,v 1.1 2001/04/01 22:25:24 iang Exp $
 *
 * Copyright (c) Systemics Ltd 1995-1999 on behalf of
 * the WebFunds Development Team.  All Rights Reserved.
 */
package webfunds.token;

/**
 *  This exception base class is inherited by all Token Exceptions.
 *  Model taken from SOXException.
 */
public class TokenException
    extends Exception
{
    protected int number;
    public int    getNumber() { return number; }
    public String toString() { return "(" + number + ") " + super.toString(); }

    /**
     *  Internal errors detected.
     *  Positive numbers are reserved for Token reply errors.
     */
    public static final int   UNKNOWN        = -1,
                              NOT_SIGNED     = -2,

                              LAST_ERROR     = -2;
 
    public boolean isNotSigned()        { return (number == NOT_SIGNED); }



    /**
     *  Is this in our recognised block?
     *  Not a complete test, there are some gaps.
     */
    public static final boolean isTokenException(int errno)
    {
         if ( (LAST_ERROR <= errno) && (errno < -1) )
             return true ;

         return false ;
    }

    /**
     *  getNumber() will return 0
     */
    public TokenException(String msg)
    {
        super(msg);
	number = 0;
    }

    /**
     *  @param num is an error code, undefined here,
     *  extending classes to define, excepting 0 which means "it wasn't set"
     */
    public TokenException(int num, String msg)
    {
        super(msg);
        number = num;
    }

}



1.1                  java/webfunds/token/TokenKeyException.java

Index: TokenKeyException.java
===================================================================
/*
 * $Id: TokenKeyException.java,v 1.1 2001/04/01 22:25:25 iang Exp $
 *
 * Copyright (c) Systemics Ltd 1995-1999 on behalf of
 * the WebFunds Development Team.  All Rights Reserved.
 */
package webfunds.token;


/**
 * This exception class is thrown when a badly formatted
 * local key is encountered.
 * It is generally fatal, as indicates local keys are bad.
 *
 * It should not be thrown with respect to Server side.
 */
public class TokenKeyException
    extends TokenException
{

    public TokenKeyException(String msg)            { super(UNKNOWN, msg); }
    public TokenKeyException(int errno, String msg) { super(errno, msg); }
    public TokenKeyException(int errno)             { super(errno, ""); }
}



1.1                  java/webfunds/token/TokenPacketException.java

Index: TokenPacketException.java
===================================================================
/*
 * $Id: TokenPacketException.java,v 1.1 2001/04/01 22:25:25 iang Exp $
 *
 * Copyright (c) Systemics Ltd 1995-1999 on behalf of
 * the WebFunds Development Team.  All Rights Reserved.
 */
package webfunds.token;

/**
 *  This exception class is thrown when a badly formatted
 *  or unknown token is passed for decoding.
 *
 */
public class TokenPacketException
    extends TokenException
{

    public TokenPacketException(String msg)            { super(UNKNOWN, msg); }
    public TokenPacketException(int errno, String msg) { super(errno, msg); }
    public TokenPacketException(int errno)             { super(errno, ""); }
}



1.1                  java/webfunds/token/TokenSigner.java

Index: TokenSigner.java
===================================================================
/*
 * $Id: TokenSigner.java,v 1.1 2001/04/01 22:25:25 iang Exp $
 *
 * Copyright (c) Systemics Inc 1995-2000 on behalf of
 * the WebFunds Development Team.  All Rights Reserved.
 */
package webfunds.token;

import java.io.InputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.DataInputStream;
import java.io.OutputStream;
import java.io.DataOutputStream;

import java.security.SecureRandom;

import webfunds.utils.Hex;

import webfunds.token.util.Log;

/**
 *  This class represents a proto token -- a coin before withdrawal.
 *
 */
public abstract class TokenSigner
    extends BaseToken
{
    /**
     *  The version number for this structure:
     *         0:     current
     */
    public static final int SIGNER_ORIGINAL = 0;



    /**
     *  The state that the token is in within this phase
     *  (generally made by the mint according to the protocol).
     *  How it is set and advanced is up to the higher layers.
     *  May be ignored, may use the following TOK numbers.
     *  Only the low-order single unsigned byte is saved & restored.
     */
    public static final int            TOK_NEW      = 0,  // received
                                       TOK_VERIFIED = 1,  // user sig checked
                                       TOK_SIGNED   = 2,  // now is cash
                                       TOK_TRANS    = 3;  // accounted for

    public boolean isNew()             { return state == TOK_NEW; }
    public boolean isVerified()        { return state == TOK_VERIFIED; }
    public boolean isSigned()          { return state == TOK_SIGNED; }
    public boolean isTransacted()      { return state == TOK_TRANS; }



    /**
     *  User blinds a raw coin and turns it into a Protocoin.
     *  Some sort of signing process involved, done internally.
     *
     *  Normally called by mint (server), so it is standardised.
     *
     *  @param params includes the blinding paramaters from the mint
     */
    public abstract void sign(SecureRandom sr, AbstractPrivateParams params)
        throws TokenKeyException;

    protected void setSigned()        { this.state = TOK_SIGNED; }
    public void setTransacted()       { this.state = TOK_TRANS; }




    /**
     *  Create an uninitialised token.
     *  Call prototype() with some params to make it a real proto-token.
     *
     *  The series is unknown until signing (mint can use a different key).
     *
     *  @param type of token, being the blinding or coin scheme
     *  @param subversion is the version of the parent coin class
     *  @param log the coin size, log base 2 of quantity
     */
    public TokenSigner()
    {
        super();

        this.phase        = PHASE_SIGNER;
        this.phaseVersion = SIGNER_ORIGINAL;
        this.state        = TOK_NEW;
    }

    /**
     *  Construct a token object from a byte array
     *  that was previously returned from the encode()
     *  method of a token object.
     *
     *  @param token the previously encoded token
     *  @excep SOXPacketException The token data is badly formatted
     */
    public TokenSigner(byte[] buf)
        throws TokenPacketException
    {
        super(buf);
    }

    /**
     *  Construct a token object from data in an input stream,
     *  where the data was previously returned from the encode()
     *  method of a token object.
     *
     *  @param is the input stream from which to read the token data
     *  @excep SOXPacketException The token data is badly formatted
     */
    public TokenSigner(InputStream is)
        throws TokenPacketException
    {
        super(is);
    }



    /**
     *  Update this token object with the values from
     *  a token encoded as a byte array (such as previously
     *  returned from the encode() method of a token object).
     *
     *  @param token the previosly encoded token
     */
    public void decode(InputStream is)
        throws IOException
    {
        DataInputStream dis = new DataInputStream(is);

        super.decode(dis);
        if (phase != PHASE_SIGNER)
            throw new IOException("Invalid phase in token: "+
                                phase + " != " + PHASE_SIGNER);
        if (phaseVersion != SIGNER_ORIGINAL)
            throw new IOException("Invalid phase version in token: "+
                                phaseVersion + " != " + SIGNER_ORIGINAL);

    }

    /**
     * Encode a token as a byte array, suitable for
     * sending to third parties for depositing.
     * If the signature is not present, an unsigned
     * token will be encoded.
     *
     * @return byte[] the token in encoded form
     */
    public void encode(OutputStream os)
        throws IOException
    {
        DataOutputStream dos = new DataOutputStream(os);
        super.encode(dos);
    }



////// Self-Test //////////////////////////////////

    public String toString()
    {
        String s = "    state == " + state;
        s += "    L: " + log + "\tQ: " + Log.log2qty(log);

        return s;
    }

}



1.1                  java/webfunds/token/TokenSpender.java

Index: TokenSpender.java
===================================================================
/*
 * $Id: TokenSpender.java,v 1.1 2001/04/01 22:25:25 iang Exp $
 *
 * Copyright (c) Systemics Inc 1995-2000 on behalf of
 * the WebFunds Development Team.  All Rights Reserved.
 */
package webfunds.token;

import java.io.InputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.DataInputStream;
import java.io.OutputStream;
import java.io.DataOutputStream;

import java.security.SecureRandom;

import webfunds.utils.Hex;

import webfunds.token.util.Log;

/**
 *  This class represents a valuable token -- a withdrawn coin that
 *  may or may not at this stage be spent.
 *
 */
public abstract class TokenSpender
    extends BaseToken
{
    /**
     *  The version number for this structure:
     *         0:     current
     */
    public static final int SPENDER_ORIGINAL = 0;



    /**
     *  The state that the token is in within this phase
     *  (received, verified, spending, receipted.)
     *  How it is set and advanced is up to the higher layers.
     *  May be ignored, may use the following TOK numbers.
     *  Only the low-order single unsigned byte is saved & restored.
     */
    public static final int            TOK_START     = 0, // received from mint
                                       TOK_VERIFIED  = 1, // signed by mint
                                       TOK_SENT      = 2, // seen by outsiders
                                       TOK_CANCELLING= 3, // i spend too
                                       TOK_RECEIPTED = 4; // receipt is in

    public boolean isStart()           { return state == TOK_START; }
    public boolean isVerified()        { return state == TOK_VERIFIED; }
    public boolean isSent()            { return state == TOK_SENT; }
    public boolean isCancelling()      { return state == TOK_CANCELLING; }
    public boolean isReceipted()       { return state == TOK_RECEIPTED; }

    private byte[] token;



    /**
     *  Mint returns a blinded, signed coin for Client to unblind and spend.
     *
     *  Normally called by client (user), so it is standardised.
     *  How it is done is up to the individual type of token, but
     *  we hope we can limit it to this simple interface, as there
     *  are half a dozen different token crypto-schemes out there.
     *
     *  The sub class needs to save the entire token data, even when
     *  spent to some place, until it is receipted.
     *
     *  @param params includes the blinding paramaters from the mint
     */
    public abstract void unblind(SecureRandom sr, byte[] token,
                                 AbstractPublicParams params)
        throws TokenKeyException;

    public void unblind(byte[] token, AbstractPublicParams params)
        throws TokenKeyException
    {
        this.state = TOK_VERIFIED;
    }

    public void setSent()        { this.state = TOK_SENT; }
    public void setCancelling()  { this.state = TOK_CANCELLING; }
    public void setReceipted()   { this.state = TOK_RECEIPTED; }




    /**
     *  Create an uninitialised token.
     *  Call prototype() with some params to make it a real proto-token.
     *
     *  The series is unknown until signing (mint can use a different key).
     *
     *  @param type of token, being the blinding or coin scheme
     *  @param subversion is the version of the parent coin class
     *  @param log the coin size, log base 2 of quantity
     */
    public TokenSpender()
    {
        this.state = TOK_START;
    }

    /**
     *  Construct a token object from a byte array
     *  that was previously returned from the encode()
     *  method of a token object.
     *
     *  @param token the previously encoded token
     *  @excep SOXPacketException The token data is badly formatted
     */
    public TokenSpender(byte[] buf)
        throws TokenPacketException
    {
        super(buf);
    }

    /**
     *  Construct a token object from data in an input stream,
     *  where the data was previously returned from the encode()
     *  method of a token object.
     *
     *  @param is the input stream from which to read the token data
     *  @excep SOXPacketException The token data is badly formatted
     */
    public TokenSpender(InputStream is)
        throws TokenPacketException
    {
        super(is);
    }



    /**
     *  Update this token object with the values from
     *  a token encoded as a byte array (such as previously
     *  returned from the encode() method of a token object).
     *
     *  @param token the previosly encoded token
     */
    public void decode(InputStream is)
        throws IOException
    {
        DataInputStream dis = new DataInputStream(is);

        super.decode(dis);
        if (phase != PHASE_SPEND)
            throw new IOException("Invalid phase in token: "+
                                phase + " != " + PHASE_SPEND);
        if (phaseVersion != SPENDER_ORIGINAL)
            throw new IOException("Invalid phase version in token: "+
                                phaseVersion + " != " + SPENDER_ORIGINAL);


        token = readByteArray(dis);
    }

    /**
     * Encode a token as a byte array, suitable for
     * sending to third parties for depositing.
     * If the signature is not present, an unsigned
     * token will be encoded.
     *
     * @return byte[] the token in encoded form
     */
    public void encode(OutputStream os)
        throws IOException
    {
        DataOutputStream dos = new DataOutputStream(os);
        super.encode(dos);

        writeByteArray(dos, token);
    }



////// Self-Test //////////////////////////////////

    public String toString()
    {
        String s = "    state == " + state;
        s += "    L: " + log + "\tQ: " + Log.log2qty(log);

        return s;
    }



}



1.1                  java/webfunds/token/TokenVersionException.java

Index: TokenVersionException.java
===================================================================
/*
 * $Id: TokenVersionException.java,v 1.1 2001/04/01 22:25:25 iang Exp $
 *
 * Copyright (c) Systemics Ltd 1995-1999 on behalf of
 * the WebFunds Development Team.  All Rights Reserved.
 */
package webfunds.token;

/**
 *  This exception class is thrown when a token claims a version
 *  number that is unknown or unsupported.
 *
 */
public class TokenVersionException
    extends TokenException
{

    public TokenVersionException(String msg)            { super(UNKNOWN, msg); }
    public TokenVersionException(int errno, String msg) { super(errno, msg); }
    public TokenVersionException(int errno)             { super(errno, ""); }
}