using System;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Security;
namespace Org.BouncyCastle.Crypto.Signers{
/**
* EC-NR as described in IEEE 1363-2000
*/
public class ECNRSigner
: IDsa
{
private bool forSigning;
private ECKeyParameters key;
private SecureRandom random;
public string AlgorithmName
{
get { return "ECNR"; }
}
public void Init(
bool forSigning,
ICipherParameters parameters)
{
this.forSigning = forSigning;
if (forSigning)
{
if (parameters is ParametersWithRandom)
{
ParametersWithRandom rParam = (ParametersWithRandom) parameters;
this.random = rParam.Random;
parameters = rParam.Parameters;
}
else
{
this.random = new SecureRandom();
}
if (!(parameters is ECPrivateKeyParameters))
throw new InvalidKeyException("EC private key required for signing");
this.key = (ECPrivateKeyParameters) parameters;
}
else
{
if (!(parameters is ECPublicKeyParameters))
throw new InvalidKeyException("EC public key required for verification");
this.key = (ECPublicKeyParameters) parameters;
}
}
// Section 7.2.5 ECSP-NR, pg 34
/**
* generate a signature for the given message using the key we were
* initialised with. Generally, the order of the curve should be at
* least as long as the hash of the message of interest, and with
* ECNR it *must* be at least as long.
*
* @param digest the digest to be signed.
* @exception DataLengthException if the digest is longer than the key allows
*/
public BigInteger[] GenerateSignature(
byte[] message)
{
if (!this.forSigning)
{
// not properly initilaized... deal with it
throw new InvalidOperationException("not initialised for signing");
}
BigInteger n = ((ECPrivateKeyParameters) this.key).Parameters.N;
int nBitLength = n.BitLength;
BigInteger e = new BigInteger(1, message);
int eBitLength = e.BitLength;
ECPrivateKeyParameters privKey = (ECPrivateKeyParameters)key;
if (eBitLength > nBitLength)
{
throw new DataLengthException("input too large for ECNR key.");
}
BigInteger r = null;
BigInteger s = null;
AsymmetricCipherKeyPair tempPair;
do // generate r
{
// generate another, but very temporary, key pair using
// the same EC parameters
ECKeyPairGenerator keyGen = new ECKeyPairGenerator();
keyGen.Init(new ECKeyGenerationParameters(privKey.Parameters, this.random));
tempPair = keyGen.GenerateKeyPair();
// BigInteger Vx = tempPair.getPublic().getW().getAffineX();
ECPublicKeyParameters V = (ECPublicKeyParameters) tempPair.Public; // get temp's public key
BigInteger Vx = V.Q.X.ToBigInteger(); // get the point's x coordinate
r = Vx.Add(e).Mod(n);
}
while (r.SignValue == 0);
// generate s
BigInteger x = privKey.D; // private key value
BigInteger u = ((ECPrivateKeyParameters) tempPair.Private).D; // temp's private key value
s = u.Subtract(r.Multiply(x)).Mod(n);
return new BigInteger[]{ r, s };
}
// Section 7.2.6 ECVP-NR, pg 35
/**
* return true if the value r and s represent a signature for the
* message passed in. Generally, the order of the curve should be at
* least as long as the hash of the message of interest, and with
* ECNR, it *must* be at least as long. But just in case the signer
* applied mod(n) to the longer digest, this implementation will
* apply mod(n) during verification.
*
* @param digest the digest to be verified.
* @param r the r value of the signature.
* @param s the s value of the signature.
* @exception DataLengthException if the digest is longer than the key allows
*/
public bool VerifySignature(
byte[] message,
BigInteger r,
BigInteger s)
{
if (this.forSigning)
{
// not properly initilaized... deal with it
throw new InvalidOperationException("not initialised for verifying");
}
ECPublicKeyParameters pubKey = (ECPublicKeyParameters)key;
BigInteger n = pubKey.Parameters.N;
int nBitLength = n.BitLength;
BigInteger e = new BigInteger(1, message);
int eBitLength = e.BitLength;
if (eBitLength > nBitLength)
{
throw new DataLengthException("input too large for ECNR key.");
}
// r in the range [1,n-1]
if (r.CompareTo(BigInteger.One) < 0 || r.CompareTo(n) >= 0)
{
return false;
}
// TODO So why is this different from the spec?
// s in the range [0,n-1] NB: ECNR spec says 0
if (s.CompareTo(BigInteger.Zero) < 0 || s.CompareTo(n) >= 0)
{
return false;
}
// compute P = sG + rW
ECPoint G = pubKey.Parameters.G;
ECPoint W = pubKey.Q;
// calculate P using Bouncy math
ECPoint P = ECAlgorithms.SumOfTwoMultiplies(G, s, W, r);
BigInteger x = P.X.ToBigInteger();
BigInteger t = r.Subtract(x).Mod(n);
return t.Equals(e);
}
}
}
|