All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opendaylight.netconf.shaded.eddsa.EdDSAEngine Maven / Gradle / Ivy

There is a newer version: 8.0.3
Show newest version
/**
 * EdDSA-Java by str4d
 *
 * To the extent possible under law, the person who associated CC0 with
 * EdDSA-Java has waived all copyright and related or neighboring rights
 * to EdDSA-Java.
 *
 * You should have received a copy of the CC0 legalcode along with this
 * work. If not, see .
 *
 */
package org.opendaylight.netconf.shaded.eddsa;

import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;

import org.opendaylight.netconf.shaded.eddsa.math.Curve;
import org.opendaylight.netconf.shaded.eddsa.math.GroupElement;
import org.opendaylight.netconf.shaded.eddsa.math.ScalarOps;
import sun.security.x509.X509Key;

/**
 * Signing and verification for EdDSA.
 *

* The EdDSA sign and verify algorithms do not interact well with * the Java Signature API, as one or more update() methods must be * called before sign() or verify(). Using the standard API, * this implementation must copy and buffer all data passed in * via update(). *

* This implementation offers two ways to avoid this copying, * but only if all data to be signed or verified is available * in a single byte array. *

*Option 1: *

    *
  1. Call initSign() or initVerify() as usual. *
  2. Call setParameter(ONE_SHOT_MODE) *
  3. Call update(byte[]) or update(byte[], int, int) exactly once *
  4. Call sign() or verify() as usual. *
  5. If doing additional one-shot signs or verifies with this object, you must * call setParameter(ONE_SHOT_MODE) each time *
* *

*Option 2: *

    *
  1. Call initSign() or initVerify() as usual. *
  2. Call one of the signOneShot() or verifyOneShot() methods. *
  3. If doing additional one-shot signs or verifies with this object, * just call signOneShot() or verifyOneShot() again. *
* * @author str4d * */ public final class EdDSAEngine extends Signature { public static final String SIGNATURE_ALGORITHM = "NONEwithEdDSA"; private MessageDigest digest; private ByteArrayOutputStream baos; private EdDSAKey key; private boolean oneShotMode; private byte[] oneShotBytes; private int oneShotOffset; private int oneShotLength; /** * To efficiently sign or verify data in one shot, pass this to setParameters() * after initSign() or initVerify() but BEFORE THE FIRST AND ONLY * update(data) or update(data, off, len). The data reference will be saved * and then used in sign() or verify() without copying the data. * Violate these rules and you will get a SignatureException. */ public static final AlgorithmParameterSpec ONE_SHOT_MODE = new OneShotSpec(); private static class OneShotSpec implements AlgorithmParameterSpec {} /** * No specific EdDSA-internal hash requested, allows any EdDSA key. */ public EdDSAEngine() { super(SIGNATURE_ALGORITHM); } /** * Specific EdDSA-internal hash requested, only matching keys will be allowed. * @param digest the hash algorithm that keys must have to sign or verify. */ public EdDSAEngine(MessageDigest digest) { this(); this.digest = digest; } private void reset() { if (digest != null) digest.reset(); if (baos != null) baos.reset(); oneShotMode = false; oneShotBytes = null; } @Override protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { reset(); if (privateKey instanceof EdDSAPrivateKey) { EdDSAPrivateKey privKey = (EdDSAPrivateKey) privateKey; key = privKey; if (digest == null) { // Instantiate the digest from the key parameters try { digest = MessageDigest.getInstance(key.getParams().getHashAlgorithm()); } catch (NoSuchAlgorithmException e) { throw new InvalidKeyException("cannot get required digest " + key.getParams().getHashAlgorithm() + " for private key."); } } else if (!key.getParams().getHashAlgorithm().equals(digest.getAlgorithm())) throw new InvalidKeyException("Key hash algorithm does not match chosen digest"); digestInitSign(privKey); } else { throw new InvalidKeyException("cannot identify EdDSA private key: " + privateKey.getClass()); } } private void digestInitSign(EdDSAPrivateKey privKey) { // Preparing for hash // r = H(h_b,...,h_2b-1,M) int b = privKey.getParams().getCurve().getField().getb(); digest.update(privKey.getH(), b/8, b/4 - b/8); } @Override protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { reset(); if (publicKey instanceof EdDSAPublicKey) { key = (EdDSAPublicKey) publicKey; if (digest == null) { // Instantiate the digest from the key parameters try { digest = MessageDigest.getInstance(key.getParams().getHashAlgorithm()); } catch (NoSuchAlgorithmException e) { throw new InvalidKeyException("cannot get required digest " + key.getParams().getHashAlgorithm() + " for private key."); } } else if (!key.getParams().getHashAlgorithm().equals(digest.getAlgorithm())) throw new InvalidKeyException("Key hash algorithm does not match chosen digest"); } else if (publicKey instanceof X509Key) { // X509Certificate will sometimes contain an X509Key rather than the EdDSAPublicKey itself; the contained // key is valid but needs to be instanced as an EdDSAPublicKey before it can be used. EdDSAPublicKey parsedPublicKey; try { parsedPublicKey = new EdDSAPublicKey(new X509EncodedKeySpec(publicKey.getEncoded())); } catch (InvalidKeySpecException ex) { throw new InvalidKeyException("cannot handle X.509 EdDSA public key: " + publicKey.getAlgorithm()); } engineInitVerify(parsedPublicKey); } else { throw new InvalidKeyException("cannot identify EdDSA public key: " + publicKey.getClass()); } } /** * @throws SignatureException if in one-shot mode */ @Override protected void engineUpdate(byte b) throws SignatureException { if (oneShotMode) throw new SignatureException("unsupported in one-shot mode"); if (baos == null) baos = new ByteArrayOutputStream(256); baos.write(b); } /** * @throws SignatureException if one-shot rules are violated */ @Override protected void engineUpdate(byte[] b, int off, int len) throws SignatureException { if (oneShotMode) { if (oneShotBytes != null) throw new SignatureException("update() already called"); oneShotBytes = b; oneShotOffset = off; oneShotLength = len; } else { if (baos == null) baos = new ByteArrayOutputStream(256); baos.write(b, off, len); } } @Override protected byte[] engineSign() throws SignatureException { try { return x_engineSign(); } finally { reset(); // must leave the object ready to sign again with // the same key, as required by the API EdDSAPrivateKey privKey = (EdDSAPrivateKey) key; digestInitSign(privKey); } } private byte[] x_engineSign() throws SignatureException { Curve curve = key.getParams().getCurve(); ScalarOps sc = key.getParams().getScalarOps(); byte[] a = ((EdDSAPrivateKey) key).geta(); byte[] message; int offset, length; if (oneShotMode) { if (oneShotBytes == null) throw new SignatureException("update() not called first"); message = oneShotBytes; offset = oneShotOffset; length = oneShotLength; } else { if (baos == null) message = new byte[0]; else message = baos.toByteArray(); offset = 0; length = message.length; } // r = H(h_b,...,h_2b-1,M) digest.update(message, offset, length); byte[] r = digest.digest(); // r mod l // Reduces r from 64 bytes to 32 bytes r = sc.reduce(r); // R = rB GroupElement R = key.getParams().getB().scalarMultiply(r); byte[] Rbyte = R.toByteArray(); // S = (r + H(Rbar,Abar,M)*a) mod l digest.update(Rbyte); digest.update(((EdDSAPrivateKey) key).getAbyte()); digest.update(message, offset, length); byte[] h = digest.digest(); h = sc.reduce(h); byte[] S = sc.multiplyAndAdd(h, a, r); // R+S int b = curve.getField().getb(); ByteBuffer out = ByteBuffer.allocate(b/4); out.put(Rbyte).put(S); return out.array(); } @Override protected boolean engineVerify(byte[] sigBytes) throws SignatureException { try { return x_engineVerify(sigBytes); } finally { reset(); } } private boolean x_engineVerify(byte[] sigBytes) throws SignatureException { Curve curve = key.getParams().getCurve(); int b = curve.getField().getb(); if (sigBytes.length != b/4) throw new SignatureException("signature length is wrong"); // R is first b/8 bytes of sigBytes, S is second b/8 bytes digest.update(sigBytes, 0, b/8); digest.update(((EdDSAPublicKey) key).getAbyte()); // h = H(Rbar,Abar,M) byte[] message; int offset, length; if (oneShotMode) { if (oneShotBytes == null) throw new SignatureException("update() not called first"); message = oneShotBytes; offset = oneShotOffset; length = oneShotLength; } else { if (baos == null) message = new byte[0]; else message = baos.toByteArray(); offset = 0; length = message.length; } digest.update(message, offset, length); byte[] h = digest.digest(); // h mod l h = key.getParams().getScalarOps().reduce(h); byte[] Sbyte = Arrays.copyOfRange(sigBytes, b/8, b/4); // R = SB - H(Rbar,Abar,M)A GroupElement R = key.getParams().getB().doubleScalarMultiplyVariableTime( ((EdDSAPublicKey) key).getNegativeA(), h, Sbyte); // Variable time. This should be okay, because there are no secret // values used anywhere in verification. byte[] Rcalc = R.toByteArray(); for (int i = 0; i < Rcalc.length; i++) { if (Rcalc[i] != sigBytes[i]) return false; } return true; } /** * To efficiently sign all the data in one shot, if it is available, * use this method, which will avoid copying the data. * * Same as: *
     *  setParameter(ONE_SHOT_MODE)
     *  update(data)
     *  sig = sign()
     *
* * @param data the message to be signed * @return the signature * @throws SignatureException if update() already called * @see #ONE_SHOT_MODE */ public byte[] signOneShot(byte[] data) throws SignatureException { return signOneShot(data, 0, data.length); } /** * To efficiently sign all the data in one shot, if it is available, * use this method, which will avoid copying the data. * * Same as: *
     *  setParameter(ONE_SHOT_MODE)
     *  update(data, off, len)
     *  sig = sign()
     *
* * @param data byte array containing the message to be signed * @param off the start of the message inside data * @param len the length of the message * @return the signature * @throws SignatureException if update() already called * @see #ONE_SHOT_MODE */ public byte[] signOneShot(byte[] data, int off, int len) throws SignatureException { oneShotMode = true; update(data, off, len); return sign(); } /** * To efficiently verify all the data in one shot, if it is available, * use this method, which will avoid copying the data. * * Same as: *
     *  setParameter(ONE_SHOT_MODE)
     *  update(data)
     *  ok = verify(signature)
     *
* * @param data the message that was signed * @param signature of the message * @return true if the signature is valid, false otherwise * @throws SignatureException if update() already called * @see #ONE_SHOT_MODE */ public boolean verifyOneShot(byte[] data, byte[] signature) throws SignatureException { return verifyOneShot(data, 0, data.length, signature, 0, signature.length); } /** * To efficiently verify all the data in one shot, if it is available, * use this method, which will avoid copying the data. * * Same as: *
     *  setParameter(ONE_SHOT_MODE)
     *  update(data, off, len)
     *  ok = verify(signature)
     *
* * @param data byte array containing the message that was signed * @param off the start of the message inside data * @param len the length of the message * @param signature of the message * @return true if the signature is valid, false otherwise * @throws SignatureException if update() already called * @see #ONE_SHOT_MODE */ public boolean verifyOneShot(byte[] data, int off, int len, byte[] signature) throws SignatureException { return verifyOneShot(data, off, len, signature, 0, signature.length); } /** * To efficiently verify all the data in one shot, if it is available, * use this method, which will avoid copying the data. * * Same as: *
     *  setParameter(ONE_SHOT_MODE)
     *  update(data)
     *  ok = verify(signature, sigoff, siglen)
     *
* * @param data the message that was signed * @param signature byte array containing the signature * @param sigoff the start of the signature * @param siglen the length of the signature * @return true if the signature is valid, false otherwise * @throws SignatureException if update() already called * @see #ONE_SHOT_MODE */ public boolean verifyOneShot(byte[] data, byte[] signature, int sigoff, int siglen) throws SignatureException { return verifyOneShot(data, 0, data.length, signature, sigoff, siglen); } /** * To efficiently verify all the data in one shot, if it is available, * use this method, which will avoid copying the data. * * Same as: *
     *  setParameter(ONE_SHOT_MODE)
     *  update(data, off, len)
     *  ok = verify(signature, sigoff, siglen)
     *
* * @param data byte array containing the message that was signed * @param off the start of the message inside data * @param len the length of the message * @param signature byte array containing the signature * @param sigoff the start of the signature * @param siglen the length of the signature * @return true if the signature is valid, false otherwise * @throws SignatureException if update() already called * @see #ONE_SHOT_MODE */ public boolean verifyOneShot(byte[] data, int off, int len, byte[] signature, int sigoff, int siglen) throws SignatureException { oneShotMode = true; update(data, off, len); return verify(signature, sigoff, siglen); } /** * @throws InvalidAlgorithmParameterException if spec is ONE_SHOT_MODE and update() already called * @see #ONE_SHOT_MODE */ @Override protected void engineSetParameter(AlgorithmParameterSpec spec) throws InvalidAlgorithmParameterException { if (spec.equals(ONE_SHOT_MODE)) { if (oneShotBytes != null || (baos != null && baos.size() > 0)) throw new InvalidAlgorithmParameterException("update() already called"); oneShotMode = true; } else { super.engineSetParameter(spec); } } /** * @deprecated */ @Override protected void engineSetParameter(String param, Object value) { throw new UnsupportedOperationException("engineSetParameter unsupported"); } /** * @deprecated */ @Override protected Object engineGetParameter(String param) { throw new UnsupportedOperationException("engineSetParameter unsupported"); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy