001: package org.bouncycastle.crypto.signers;
002:
003: import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
004: import org.bouncycastle.crypto.CipherParameters;
005: import org.bouncycastle.crypto.DSA;
006: import org.bouncycastle.crypto.DataLengthException;
007: import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
008: import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
009: import org.bouncycastle.crypto.params.ECKeyParameters;
010: import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
011: import org.bouncycastle.crypto.params.ECPublicKeyParameters;
012: import org.bouncycastle.crypto.params.ParametersWithRandom;
013: import org.bouncycastle.math.ec.ECAlgorithms;
014: import org.bouncycastle.math.ec.ECConstants;
015: import org.bouncycastle.math.ec.ECPoint;
016:
017: import java.math.BigInteger;
018: import java.security.SecureRandom;
019:
020: /**
021: * EC-NR as described in IEEE 1363-2000
022: */
023: public class ECNRSigner implements DSA {
024: private boolean forSigning;
025: private ECKeyParameters key;
026: private SecureRandom random;
027:
028: public void init(boolean forSigning, CipherParameters param) {
029: this .forSigning = forSigning;
030:
031: if (forSigning) {
032: if (param instanceof ParametersWithRandom) {
033: ParametersWithRandom rParam = (ParametersWithRandom) param;
034:
035: this .random = rParam.getRandom();
036: this .key = (ECPrivateKeyParameters) rParam
037: .getParameters();
038: } else {
039: this .random = new SecureRandom();
040: this .key = (ECPrivateKeyParameters) param;
041: }
042: } else {
043: this .key = (ECPublicKeyParameters) param;
044: }
045: }
046:
047: // Section 7.2.5 ECSP-NR, pg 34
048: /**
049: * generate a signature for the given message using the key we were
050: * initialised with. Generally, the order of the curve should be at
051: * least as long as the hash of the message of interest, and with
052: * ECNR it *must* be at least as long.
053: *
054: * @param digest the digest to be signed.
055: * @exception DataLengthException if the digest is longer than the key allows
056: */
057: public BigInteger[] generateSignature(byte[] digest) {
058: if (!this .forSigning) {
059: throw new IllegalStateException(
060: "not initialised for signing");
061: }
062:
063: BigInteger n = ((ECPrivateKeyParameters) this .key)
064: .getParameters().getN();
065: int nBitLength = n.bitLength();
066:
067: BigInteger e = new BigInteger(1, digest);
068: int eBitLength = e.bitLength();
069:
070: ECPrivateKeyParameters privKey = (ECPrivateKeyParameters) key;
071:
072: if (eBitLength > nBitLength) {
073: throw new DataLengthException(
074: "input too large for ECNR key.");
075: }
076:
077: BigInteger r = null;
078: BigInteger s = null;
079:
080: AsymmetricCipherKeyPair tempPair;
081: do // generate r
082: {
083: // generate another, but very temporary, key pair using
084: // the same EC parameters
085: ECKeyPairGenerator keyGen = new ECKeyPairGenerator();
086:
087: keyGen.init(new ECKeyGenerationParameters(privKey
088: .getParameters(), this .random));
089:
090: tempPair = keyGen.generateKeyPair();
091:
092: // BigInteger Vx = tempPair.getPublic().getW().getAffineX();
093: ECPublicKeyParameters V = (ECPublicKeyParameters) tempPair
094: .getPublic(); // get temp's public key
095: BigInteger Vx = V.getQ().getX().toBigInteger(); // get the point's x coordinate
096:
097: r = Vx.add(e).mod(n);
098: } while (r.equals(ECConstants.ZERO));
099:
100: // generate s
101: BigInteger x = privKey.getD(); // private key value
102: BigInteger u = ((ECPrivateKeyParameters) tempPair.getPrivate())
103: .getD(); // temp's private key value
104: s = u.subtract(r.multiply(x)).mod(n);
105:
106: BigInteger[] res = new BigInteger[2];
107: res[0] = r;
108: res[1] = s;
109:
110: return res;
111: }
112:
113: // Section 7.2.6 ECVP-NR, pg 35
114: /**
115: * return true if the value r and s represent a signature for the
116: * message passed in. Generally, the order of the curve should be at
117: * least as long as the hash of the message of interest, and with
118: * ECNR, it *must* be at least as long. But just in case the signer
119: * applied mod(n) to the longer digest, this implementation will
120: * apply mod(n) during verification.
121: *
122: * @param digest the digest to be verified.
123: * @param r the r value of the signature.
124: * @param s the s value of the signature.
125: * @exception DataLengthException if the digest is longer than the key allows
126: */
127: public boolean verifySignature(byte[] digest, BigInteger r,
128: BigInteger s) {
129: if (this .forSigning) {
130: throw new IllegalStateException(
131: "not initialised for verifying");
132: }
133:
134: ECPublicKeyParameters pubKey = (ECPublicKeyParameters) key;
135: BigInteger n = pubKey.getParameters().getN();
136: int nBitLength = n.bitLength();
137:
138: BigInteger e = new BigInteger(1, digest);
139: int eBitLength = e.bitLength();
140:
141: if (eBitLength > nBitLength) {
142: throw new DataLengthException(
143: "input too large for ECNR key.");
144: }
145:
146: // r in the range [1,n-1]
147: if (r.compareTo(ECConstants.ONE) < 0 || r.compareTo(n) >= 0) {
148: return false;
149: }
150:
151: // s in the range [0,n-1] NB: ECNR spec says 0
152: if (s.compareTo(ECConstants.ZERO) < 0 || s.compareTo(n) >= 0) {
153: return false;
154: }
155:
156: // compute P = sG + rW
157:
158: ECPoint G = pubKey.getParameters().getG();
159: ECPoint W = pubKey.getQ();
160: // calculate P using Bouncy math
161: ECPoint P = ECAlgorithms.sumOfTwoMultiplies(G, s, W, r);
162:
163: BigInteger x = P.getX().toBigInteger();
164: BigInteger t = r.subtract(x).mod(n);
165:
166: return t.equals(e);
167: }
168: }
|