Changeset 6be7c46


Ignore:
Timestamp:
Feb 3, 2016 6:39:49 PM (4 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
d4d7205
Parents:
7901784
Message:

EdDSA:

  • Implement one-shot methods in EdDSAEngine so we don't copy the data if all the data is available (ticket #1750)
  • Use EdDSA one-shot methods in DSAEngine
  • Fix API violation if EdDSAEngine object is reused for signing (ticket #1750)
  • Javadocs
Location:
core/java/src/net/i2p/crypto
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • core/java/src/net/i2p/crypto/DSAEngine.java

    r7901784 r6be7c46  
    516516            return altVerifySigSHA1(signature, data, offset, len, verifyingKey);
    517517
    518         java.security.Signature jsig;
    519         if (type.getBaseAlgorithm() == SigAlgo.EdDSA)
    520             jsig = new EdDSAEngine(type.getDigestInstance());
    521         else
    522             jsig = java.security.Signature.getInstance(type.getAlgorithmName());
    523518        PublicKey pubKey = SigUtil.toJavaKey(verifyingKey);
    524         jsig.initVerify(pubKey);
    525         jsig.update(data, offset, len);
    526         boolean rv = jsig.verify(SigUtil.toJavaSig(signature));
     519        byte[] sigbytes = SigUtil.toJavaSig(signature);
     520        boolean rv;
     521        if (type.getBaseAlgorithm() == SigAlgo.EdDSA) {
     522            // take advantage of one-shot mode
     523            EdDSAEngine jsig = new EdDSAEngine(type.getDigestInstance());
     524            jsig.initVerify(pubKey);
     525            rv = jsig.verifyOneShot(data, offset, len, sigbytes);
     526        } else {
     527            java.security.Signature jsig = java.security.Signature.getInstance(type.getAlgorithmName());
     528            jsig.initVerify(pubKey);
     529            jsig.update(data, offset, len);
     530            rv = jsig.verify(sigbytes);
     531        }
    527532        return rv;
    528533    }
     
    564569            throw new IllegalArgumentException("type mismatch hash=" + hash.getClass() + " key=" + type);
    565570
    566         String algo = getRawAlgo(type);
    567         java.security.Signature jsig;
    568         if (type.getBaseAlgorithm() == SigAlgo.EdDSA)
    569             jsig = new EdDSAEngine(); // Ignore algo, EdDSAKey includes a hash specification.
    570         else
    571             jsig = java.security.Signature.getInstance(algo);
    572         jsig.initVerify(pubKey);
    573         jsig.update(hash.getData());
    574         boolean rv = jsig.verify(SigUtil.toJavaSig(signature));
     571        byte[] sigbytes = SigUtil.toJavaSig(signature);
     572        boolean rv;
     573        if (type.getBaseAlgorithm() == SigAlgo.EdDSA) {
     574            // take advantage of one-shot mode
     575            // Ignore algo, EdDSAKey includes a hash specification.
     576            EdDSAEngine jsig = new EdDSAEngine();
     577            jsig.initVerify(pubKey);
     578            rv = jsig.verifyOneShot(hash.getData(), sigbytes);
     579        } else {
     580            String algo = getRawAlgo(type);
     581            java.security.Signature jsig = java.security.Signature.getInstance(algo);
     582            jsig.initVerify(pubKey);
     583            jsig.update(hash.getData());
     584            rv = jsig.verify(sigbytes);
     585        }
    575586        return rv;
    576587    }
     
    607618            return altSignSHA1(data, offset, len, privateKey);
    608619
    609         java.security.Signature jsig;
    610         if (type.getBaseAlgorithm() == SigAlgo.EdDSA)
    611             jsig = new EdDSAEngine(type.getDigestInstance());
    612         else
    613             jsig = java.security.Signature.getInstance(type.getAlgorithmName());
    614620        PrivateKey privKey = SigUtil.toJavaKey(privateKey);
    615         jsig.initSign(privKey, _context.random());
    616         jsig.update(data, offset, len);
    617         return SigUtil.fromJavaSig(jsig.sign(), type);
     621        byte[] sigbytes;
     622        if (type.getBaseAlgorithm() == SigAlgo.EdDSA) {
     623            // take advantage of one-shot mode
     624            EdDSAEngine jsig = new EdDSAEngine(type.getDigestInstance());
     625            jsig.initSign(privKey);
     626            sigbytes = jsig.signOneShot(data, offset, len);
     627        } else {
     628            java.security.Signature jsig = java.security.Signature.getInstance(type.getAlgorithmName());
     629            jsig.initSign(privKey, _context.random());
     630            jsig.update(data, offset, len);
     631            sigbytes = jsig.sign();
     632        }
     633        return SigUtil.fromJavaSig(sigbytes, type);
    618634    }
    619635
     
    650666            throw new IllegalArgumentException("type mismatch hash=" + hash.getClass() + " key=" + type);
    651667
    652         java.security.Signature jsig;
    653         if (type.getBaseAlgorithm() == SigAlgo.EdDSA)
    654             jsig = new EdDSAEngine(); // Ignore algo, EdDSAKey includes a hash specification.
    655         else
    656             jsig = java.security.Signature.getInstance(algo);
    657         jsig.initSign(privKey, _context.random());
    658         jsig.update(hash.getData());
    659         return SigUtil.fromJavaSig(jsig.sign(), type);
     668        byte[] sigbytes;
     669        if (type.getBaseAlgorithm() == SigAlgo.EdDSA) {
     670            // take advantage of one-shot mode
     671            // Ignore algo, EdDSAKey includes a hash specification.
     672            EdDSAEngine jsig = new EdDSAEngine();
     673            jsig.initSign(privKey);
     674            sigbytes = jsig.signOneShot(hash.getData());
     675        } else {
     676            java.security.Signature jsig = java.security.Signature.getInstance(type.getAlgorithmName());
     677            jsig.initSign(privKey, _context.random());
     678            jsig.update(hash.getData());
     679            sigbytes = jsig.sign();
     680        }
     681        return SigUtil.fromJavaSig(sigbytes, type);
    660682    }
    661683
  • core/java/src/net/i2p/crypto/SigUtil.java

    r7901784 r6be7c46  
    665665     *  add a SigType with bigger signatures.
    666666     *
    667      *  @param sig length must be even
    668667     *  @throws IllegalArgumentException if too big
    669668     *  @since 0.9.25, split out from sigBytesToASN1(byte[])
  • core/java/src/net/i2p/crypto/eddsa/EdDSAEngine.java

    r7901784 r6be7c46  
    33import java.io.ByteArrayOutputStream;
    44import java.nio.ByteBuffer;
     5import java.security.InvalidAlgorithmParameterException;
    56import java.security.InvalidKeyException;
    67import java.security.MessageDigest;
     
    1011import java.security.Signature;
    1112import java.security.SignatureException;
     13import java.security.spec.AlgorithmParameterSpec;
    1214import java.util.Arrays;
    1315
     
    1719
    1820/**
     21 * Signing and verification for EdDSA.
     22 *<p>
     23 * The EdDSA sign and verify algorithms do not interact well with
     24 * the Java Signature API, as one or more update() methods must be
     25 * called before sign() or verify(). Using the standard API,
     26 * this implementation must copy and buffer all data passed in
     27 * via update().
     28 *</p><p>
     29 * This implementation offers two ways to avoid this copying,
     30 * but only if all data to be signed or verified is available
     31 * in a single byte array.
     32 *</p><p>
     33 *Option 1:
     34 *</p><ol>
     35 *<li>Call initSign() or initVerify() as usual.
     36 *</li><li>Call setParameter(ONE_SHOT_MOE)
     37 *</li><li>Call update(byte[]) or update(byte[], int, int) exactly once
     38 *</li><li>Call sign() or verify() as usual.
     39 *</li><li>If doing additional one-shot signs or verifies with this object, you must
     40 *         call setParameter(ONE_SHOT_MODE) each time
     41 *</li></ol>
     42 *
     43 *<p>
     44 *Option 2:
     45 *</p><ol>
     46 *<li>Call initSign() or initVerify() as usual.
     47 *</li><li>Call one of the signOneShot() or verifyOneShot() methods.
     48 *</li><li>If doing additional one-shot signs or verifies with this object,
     49 *         just call signOneShot() or verifyOneShot() again.
     50 *</li></ol>
     51 *
    1952 * @since 0.9.15
    2053 * @author str4d
     
    2356public final class EdDSAEngine extends Signature {
    2457    private MessageDigest digest;
    25     private final ByteArrayOutputStream baos;
     58    private ByteArrayOutputStream baos;
    2659    private EdDSAKey key;
     60    private boolean oneShotMode;
     61    private byte[] oneShotBytes;
     62    private int oneShotOffset;
     63    private int oneShotLength;
     64
     65    /**
     66     *  To efficiently sign or verify data in one shot, pass this to setParameters()
     67     *  after initSign() or initVerify() but BEFORE THE FIRST AND ONLY
     68     *  update(data) or update(data, off, len). The data reference will be saved
     69     *  and then used in sign() or verify() without copying the data.
     70     *  Violate these rules and you will get a SignatureException.
     71     *
     72     *  @since 0.9.25
     73     */
     74    public static final AlgorithmParameterSpec ONE_SHOT_MODE = new OneShotSpec();
     75
     76    private static class OneShotSpec implements AlgorithmParameterSpec {}
    2777
    2878    /**
     
    3181    public EdDSAEngine() {
    3282        super("EdDSA");
    33         baos = new ByteArrayOutputStream(256);
    3483    }
    3584
     
    4392    }
    4493
    45     @Override
    46     protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
     94    /**
     95     * @since 0.9.25
     96     */
     97    private void reset() {
    4798        if (digest != null)
    4899            digest.reset();
    49         baos.reset();
    50 
     100        if (baos != null)
     101            baos.reset();
     102        oneShotMode = false;
     103        oneShotBytes = null;
     104    }
     105
     106    @Override
     107    protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
     108        reset();
    51109        if (privateKey instanceof EdDSAPrivateKey) {
    52110            EdDSAPrivateKey privKey = (EdDSAPrivateKey) privateKey;
     
    62120            } else if (!key.getParams().getHashAlgorithm().equals(digest.getAlgorithm()))
    63121                throw new InvalidKeyException("Key hash algorithm does not match chosen digest");
    64 
    65             // Preparing for hash
    66             // r = H(h_b,...,h_2b-1,M)
    67             int b = privKey.getParams().getCurve().getField().getb();
    68             digest.update(privKey.getH(), b/8, b/4 - b/8);
    69         } else
    70             throw new InvalidKeyException("cannot identify EdDSA private key.");
     122            digestInitSign(privKey);
     123        } else {
     124            throw new InvalidKeyException("cannot identify EdDSA private key: " + privateKey.getClass());
     125        }
     126    }
     127
     128    private void digestInitSign(EdDSAPrivateKey privKey) {
     129        // Preparing for hash
     130        // r = H(h_b,...,h_2b-1,M)
     131        int b = privKey.getParams().getCurve().getField().getb();
     132        digest.update(privKey.getH(), b/8, b/4 - b/8);
    71133    }
    72134
    73135    @Override
    74136    protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
    75         if (digest != null)
    76             digest.reset();
    77         baos.reset();
    78 
     137        reset();
    79138        if (publicKey instanceof EdDSAPublicKey) {
    80139            key = (EdDSAPublicKey) publicKey;
     
    89148            } else if (!key.getParams().getHashAlgorithm().equals(digest.getAlgorithm()))
    90149                throw new InvalidKeyException("Key hash algorithm does not match chosen digest");
    91         } else
    92             throw new InvalidKeyException("cannot identify EdDSA public key.");
    93     }
    94 
     150        } else {
     151            throw new InvalidKeyException("cannot identify EdDSA public key: " + publicKey.getClass());
     152        }
     153    }
     154
     155    /**
     156     * @throws SignatureException if in one-shot mode
     157     */
    95158    @Override
    96159    protected void engineUpdate(byte b) throws SignatureException {
    97         // We need to store the message because it is used in several hashes
    98         // XXX Can this be done more efficiently?
     160        if (oneShotMode)
     161            throw new SignatureException("unsupported in one-shot mode");
     162        if (baos == null)
     163            baos = new ByteArrayOutputStream(256);
    99164        baos.write(b);
    100165    }
    101166
     167    /**
     168     * @throws SignatureException if one-shot rules are violated
     169     */
    102170    @Override
    103171    protected void engineUpdate(byte[] b, int off, int len)
    104172            throws SignatureException {
    105         // We need to store the message because it is used in several hashes
    106         // XXX Can this be done more efficiently?
    107         baos.write(b, off, len);
     173        if (oneShotMode) {
     174            if (oneShotBytes != null)
     175                throw new SignatureException("update() already called");
     176            oneShotBytes = b;
     177            oneShotOffset = off;
     178            oneShotLength = len;
     179        } else {
     180            if (baos == null)
     181                baos = new ByteArrayOutputStream(256);
     182            baos.write(b, off, len);
     183        }
    108184    }
    109185
    110186    @Override
    111187    protected byte[] engineSign() throws SignatureException {
     188        try {
     189            return x_engineSign();
     190        } finally {
     191            reset();
     192            // must leave the object ready to sign again with
     193            // the same key, as required by the API
     194            EdDSAPrivateKey privKey = (EdDSAPrivateKey) key;
     195            digestInitSign(privKey);
     196        }
     197    }
     198
     199    private byte[] x_engineSign() throws SignatureException {
    112200        Curve curve = key.getParams().getCurve();
    113201        ScalarOps sc = key.getParams().getScalarOps();
    114202        byte[] a = ((EdDSAPrivateKey) key).geta();
    115203
    116         byte[] message = baos.toByteArray();
     204        byte[] message;
     205        int offset, length;
     206        if (oneShotMode) {
     207            if (oneShotBytes == null)
     208                throw new SignatureException("update() not called first");
     209            message = oneShotBytes;
     210            offset = oneShotOffset;
     211            length = oneShotLength;
     212        } else {
     213            if (baos == null)
     214                message = new byte[0];
     215            else
     216                message = baos.toByteArray();
     217            offset = 0;
     218            length = message.length;
     219        }
    117220        // r = H(h_b,...,h_2b-1,M)
    118         byte[] r = digest.digest(message);
     221        digest.update(message, offset, length);
     222        byte[] r = digest.digest();
    119223
    120224        // r mod l
     
    129233        digest.update(Rbyte);
    130234        digest.update(((EdDSAPrivateKey) key).getAbyte());
    131         byte[] h = digest.digest(message);
     235        digest.update(message, offset, length);
     236        byte[] h = digest.digest();
    132237        h = sc.reduce(h);
    133238        byte[] S = sc.multiplyAndAdd(h, a, r);
     
    142247    @Override
    143248    protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
     249        try {
     250            return x_engineVerify(sigBytes);
     251        } finally {
     252            reset();
     253        }
     254    }
     255
     256    private boolean x_engineVerify(byte[] sigBytes) throws SignatureException {
    144257        Curve curve = key.getParams().getCurve();
    145258        int b = curve.getField().getb();
     
    151264        digest.update(((EdDSAPublicKey) key).getAbyte());
    152265        // h = H(Rbar,Abar,M)
    153         byte[] message = baos.toByteArray();
    154         byte[] h = digest.digest(message);
     266        byte[] message;
     267        int offset, length;
     268        if (oneShotMode) {
     269            if (oneShotBytes == null)
     270                throw new SignatureException("update() not called first");
     271            message = oneShotBytes;
     272            offset = oneShotOffset;
     273            length = oneShotLength;
     274        } else {
     275            if (baos == null)
     276                message = new byte[0];
     277            else
     278                message = baos.toByteArray();
     279            offset = 0;
     280            length = message.length;
     281        }
     282        digest.update(message, offset, length);
     283        byte[] h = digest.digest();
    155284
    156285        // h mod l
     
    173302
    174303    /**
     304     *  To efficiently sign all the data in one shot, if it is available,
     305     *  use this method, which will avoid copying the data.
     306     *
     307     * Same as:
     308     *<pre>
     309     *  setParameter(ONE_SHOT_MODE)
     310     *  update(data)
     311     *  sig = sign()
     312     *</pre>
     313     *
     314     * @throws SignatureException if update() already called
     315     * @see #ONE_SHOT_MODE
     316     * @since 0.9.25
     317     */
     318    public byte[] signOneShot(byte[] data) throws SignatureException {
     319        return signOneShot(data, 0, data.length);
     320    }
     321
     322    /**
     323     *  To efficiently sign all the data in one shot, if it is available,
     324     *  use this method, which will avoid copying the data.
     325     *
     326     * Same as:
     327     *<pre>
     328     *  setParameter(ONE_SHOT_MODE)
     329     *  update(data, off, len)
     330     *  sig = sign()
     331     *</pre>
     332     *
     333     * @throws SignatureException if update() already called
     334     * @see #ONE_SHOT_MODE
     335     * @since 0.9.25
     336     */
     337    public byte[] signOneShot(byte[] data, int off, int len) throws SignatureException {
     338        oneShotMode = true;
     339        update(data, off, len);
     340        return sign();
     341    }
     342
     343    /**
     344     *  To efficiently verify all the data in one shot, if it is available,
     345     *  use this method, which will avoid copying the data.
     346     *
     347     * Same as:
     348     *<pre>
     349     *  setParameter(ONE_SHOT_MODE)
     350     *  update(data)
     351     *  ok = verify(signature)
     352     *</pre>
     353     *
     354     * @throws SignatureException if update() already called
     355     * @see #ONE_SHOT_MODE
     356     * @since 0.9.25
     357     */
     358    public boolean verifyOneShot(byte[] data, byte[] signature) throws SignatureException {
     359        return verifyOneShot(data, 0, data.length, signature, 0, signature.length);
     360    }
     361
     362    /**
     363     *  To efficiently verify all the data in one shot, if it is available,
     364     *  use this method, which will avoid copying the data.
     365     *
     366     * Same as:
     367     *<pre>
     368     *  setParameter(ONE_SHOT_MODE)
     369     *  update(data, off, len)
     370     *  ok = verify(signature)
     371     *</pre>
     372     *
     373     * @throws SignatureException if update() already called
     374     * @see #ONE_SHOT_MODE
     375     * @since 0.9.25
     376     */
     377    public boolean verifyOneShot(byte[] data, int off, int len, byte[] signature) throws SignatureException {
     378        return verifyOneShot(data, off, len, signature, 0, signature.length);
     379    }
     380
     381    /**
     382     *  To efficiently verify all the data in one shot, if it is available,
     383     *  use this method, which will avoid copying the data.
     384     *
     385     * Same as:
     386     *<pre>
     387     *  setParameter(ONE_SHOT_MODE)
     388     *  update(data)
     389     *  ok = verify(signature, sigoff, siglen)
     390     *</pre>
     391     *
     392     * @throws SignatureException if update() already called
     393     * @see #ONE_SHOT_MODE
     394     * @since 0.9.25
     395     */
     396    public boolean verifyOneShot(byte[] data, byte[] signature, int sigoff, int siglen) throws SignatureException {
     397        return verifyOneShot(data, 0, data.length, signature, sigoff, siglen);
     398    }
     399
     400    /**
     401     *  To efficiently verify all the data in one shot, if it is available,
     402     *  use this method, which will avoid copying the data.
     403     *
     404     * Same as:
     405     *<pre>
     406     *  setParameter(ONE_SHOT_MODE)
     407     *  update(data, off, len)
     408     *  ok = verify(signature, sigoff, siglen)
     409     *</pre>
     410     *
     411     * @throws SignatureException if update() already called
     412     * @see #ONE_SHOT_MODE
     413     * @since 0.9.25
     414     */
     415    public boolean verifyOneShot(byte[] data, int off, int len, byte[] signature, int sigoff, int siglen) throws SignatureException {
     416        oneShotMode = true;
     417        update(data, off, len);
     418        return verify(signature, sigoff, siglen);
     419    }
     420
     421    /**
     422     * @throws InvalidAlgorithmParameterException if spec is ONE_SHOT_MODE and update() already called
     423     * @see #ONE_SHOT_MODE
     424     * @since 0.9.25
     425     */
     426    @Override
     427    protected void engineSetParameter(AlgorithmParameterSpec spec) throws InvalidAlgorithmParameterException {
     428        if (spec.equals(ONE_SHOT_MODE)) {
     429            if (oneShotBytes != null || baos != null && baos.size() > 0)
     430                throw new InvalidAlgorithmParameterException("update() already called");
     431            oneShotMode = true;
     432        } else {
     433            super.engineSetParameter(spec);
     434        }
     435    }
     436
     437    /**
    175438     * @deprecated replaced with <a href="#engineSetParameter(java.security.spec.AlgorithmParameterSpec)">
    176439     */
  • core/java/src/net/i2p/crypto/eddsa/EdDSAPrivateKey.java

    r7901784 r6be7c46  
    1313/**
    1414 * An EdDSA private key.
     15 *<p>
     16 * Warning: Private key encoding is not fully specified in the
     17 * current IETF draft. This implementation uses PKCS#8 encoding,
     18 * and is subject to change. See getEncoded().
     19 *</p><p>
     20 * Ref: https://tools.ietf.org/html/draft-josefsson-pkix-eddsa-04
     21 *</p>
    1522 *
    1623 * @since 0.9.15
     
    5360
    5461    /**
    55      *  This follows the spec at
    56      *  https://tools.ietf.org/html/draft-josefsson-pkix-eddsa-04
    57      *  AND the docs from
     62     *  This follows the docs from
    5863     *  java.security.spec.PKCS8EncodedKeySpec
    5964     *  quote:
     
    7883     *  }
    7984     *</pre>
     85     *
     86     *  Ref: https://tools.ietf.org/html/draft-josefsson-pkix-eddsa-04
    8087     *
    8188     *  Note that the private key encoding is not fully specified in the Josefsson draft version 04,
  • core/java/src/net/i2p/crypto/eddsa/EdDSAPublicKey.java

    r7901784 r6be7c46  
    1313/**
    1414 * An EdDSA public key.
     15 *<p>
     16 * Warning: Public key encoding is is based on the
     17 * current IETF draft, and is subject to change. See getEncoded().
     18 *</p><p>
     19 * Ref: https://tools.ietf.org/html/draft-josefsson-pkix-eddsa-04
     20 *</p>
    1521 *
    1622 * @since 0.9.15
Note: See TracChangeset for help on using the changeset viewer.