diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2013-05-20 10:53:37 +0400 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2013-05-20 10:53:37 +0400 |
commit | 0f73e910b43507924c95c319116c49818aff1532 (patch) | |
tree | c509468b2e272cc91b6c4b1465bddb6c0fec1787 | |
parent | fa5b8d7b38278fd9da92a71f6c47987e12da0507 (diff) | |
parent | 01a4148cbb7db83113bd6b45528e413d0b1d40cc (diff) |
Merge branch 'master' into tls
118 files changed, 8606 insertions, 4685 deletions
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java b/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java index 4d6df7a3..98f46a6d 100644 --- a/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java +++ b/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java @@ -1,42 +1,42 @@ -package org.bouncycastle.asn1;
-
-public class ASN1ObjectIdentifier
- extends DERObjectIdentifier
-{
- public ASN1ObjectIdentifier(String identifier)
- {
- super(identifier);
- }
-
- ASN1ObjectIdentifier(byte[] bytes)
- {
- super(bytes);
- }
-
- ASN1ObjectIdentifier(ASN1ObjectIdentifier oid, String branch)
- {
- super(oid, branch);
- }
-
- /**
- * Return an OID that creates a branch under the current one.
- *
- * @param branchID node numbers for the new branch.
- * @return the OID for the new created branch.
- */
- public ASN1ObjectIdentifier branch(String branchID)
- {
- return new ASN1ObjectIdentifier(this, branchID);
- }
-
- /**
- * Return true if this oid is an extension of the passed in branch, stem.
- * @param stem the arc or branch that is a possible parent.
- * @return true if the branch is on the passed in stem, false otherwise.
- */
- public boolean on(ASN1ObjectIdentifier stem)
- {
- String id = getId(), stemId = stem.getId();
- return id.length() > stemId.length() && id.charAt(stemId.length()) == '.' && id.startsWith(stemId);
- }
-}
+package org.bouncycastle.asn1; + +public class ASN1ObjectIdentifier + extends DERObjectIdentifier +{ + public ASN1ObjectIdentifier(String identifier) + { + super(identifier); + } + + ASN1ObjectIdentifier(byte[] bytes) + { + super(bytes); + } + + ASN1ObjectIdentifier(ASN1ObjectIdentifier oid, String branch) + { + super(oid, branch); + } + + /** + * Return an OID that creates a branch under the current one. + * + * @param branchID node numbers for the new branch. + * @return the OID for the new created branch. + */ + public ASN1ObjectIdentifier branch(String branchID) + { + return new ASN1ObjectIdentifier(this, branchID); + } + + /** + * Return true if this oid is an extension of the passed in branch, stem. + * @param stem the arc or branch that is a possible parent. + * @return true if the branch is on the passed in stem, false otherwise. + */ + public boolean on(ASN1ObjectIdentifier stem) + { + String id = getId(), stemId = stem.getId(); + return id.length() > stemId.length() && id.charAt(stemId.length()) == '.' && id.startsWith(stemId); + } +} diff --git a/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java b/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java index 4f320b30..418f991b 100644 --- a/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java +++ b/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java @@ -1,425 +1,425 @@ -package org.bouncycastle.asn1;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.math.BigInteger;
-
-import org.bouncycastle.util.Arrays;
-
-public class DERObjectIdentifier
- extends ASN1Primitive
-{
- String identifier;
-
- private byte[] body;
-
- /**
- * return an OID from the passed in object
- *
- * @exception IllegalArgumentException if the object cannot be converted.
- */
- public static ASN1ObjectIdentifier getInstance(
- Object obj)
- {
- if (obj == null || obj instanceof ASN1ObjectIdentifier)
- {
- return (ASN1ObjectIdentifier)obj;
- }
-
- if (obj instanceof DERObjectIdentifier)
- {
- return new ASN1ObjectIdentifier(((DERObjectIdentifier)obj).getId());
- }
-
- if (obj instanceof ASN1Encodable && ((ASN1Encodable)obj).toASN1Primitive() instanceof ASN1ObjectIdentifier)
- {
- return (ASN1ObjectIdentifier)((ASN1Encodable)obj).toASN1Primitive();
- }
-
- if (obj instanceof byte[])
- {
- return ASN1ObjectIdentifier.fromOctetString((byte[])obj);
- }
-
- throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
- }
-
- /**
- * return an Object Identifier from a tagged object.
- *
- * @param obj the tagged object holding the object we want
- * @param explicit true if the object is meant to be explicitly
- * tagged false otherwise.
- * @exception IllegalArgumentException if the tagged object cannot
- * be converted.
- */
- public static ASN1ObjectIdentifier getInstance(
- ASN1TaggedObject obj,
- boolean explicit)
- {
- ASN1Primitive o = obj.getObject();
-
- if (explicit || o instanceof DERObjectIdentifier)
- {
- return getInstance(o);
- }
- else
- {
- return ASN1ObjectIdentifier.fromOctetString(ASN1OctetString.getInstance(obj.getObject()).getOctets());
- }
- }
-
- private static final long LONG_LIMIT = (Long.MAX_VALUE >> 7) - 0x7f;
-
- DERObjectIdentifier(
- byte[] bytes)
- {
- StringBuffer objId = new StringBuffer();
- long value = 0;
- BigInteger bigValue = null;
- boolean first = true;
-
- for (int i = 0; i != bytes.length; i++)
- {
- int b = bytes[i] & 0xff;
-
- if (value <= LONG_LIMIT)
- {
- value += (b & 0x7f);
- if ((b & 0x80) == 0) // end of number reached
- {
- if (first)
- {
- if (value < 40)
- {
- objId.append('0');
- }
- else if (value < 80)
- {
- objId.append('1');
- value -= 40;
- }
- else
- {
- objId.append('2');
- value -= 80;
- }
- first = false;
- }
-
- objId.append('.');
- objId.append(value);
- value = 0;
- }
- else
- {
- value <<= 7;
- }
- }
- else
- {
- if (bigValue == null)
- {
- bigValue = BigInteger.valueOf(value);
- }
- bigValue = bigValue.or(BigInteger.valueOf(b & 0x7f));
- if ((b & 0x80) == 0)
- {
- if (first)
- {
- objId.append('2');
- bigValue = bigValue.subtract(BigInteger.valueOf(80));
- first = false;
- }
-
- objId.append('.');
- objId.append(bigValue);
- bigValue = null;
- value = 0;
- }
- else
- {
- bigValue = bigValue.shiftLeft(7);
- }
- }
- }
-
- this.identifier = objId.toString();
- this.body = Arrays.clone(bytes);
- }
-
- public DERObjectIdentifier(
- String identifier)
- {
- if (identifier == null)
- {
- throw new IllegalArgumentException("'identifier' cannot be null");
- }
- if (!isValidIdentifier(identifier))
- {
- throw new IllegalArgumentException("string " + identifier + " not an OID");
- }
-
- this.identifier = identifier;
- }
-
- DERObjectIdentifier(DERObjectIdentifier oid, String branchID)
- {
- if (!isValidBranchID(branchID, 0))
- {
- throw new IllegalArgumentException("string " + branchID + " not a valid OID branch");
- }
-
- this.identifier = oid.getId() + "." + branchID;
- }
-
- public String getId()
- {
- return identifier;
- }
-
- private void writeField(
- ByteArrayOutputStream out,
- long fieldValue)
- {
- byte[] result = new byte[9];
- int pos = 8;
- result[pos] = (byte)((int)fieldValue & 0x7f);
- while (fieldValue >= (1L << 7))
- {
- fieldValue >>= 7;
- result[--pos] = (byte)((int)fieldValue & 0x7f | 0x80);
- }
- out.write(result, pos, 9 - pos);
- }
-
- private void writeField(
- ByteArrayOutputStream out,
- BigInteger fieldValue)
- {
- int byteCount = (fieldValue.bitLength()+6)/7;
- if (byteCount == 0)
- {
- out.write(0);
- }
- else
- {
- BigInteger tmpValue = fieldValue;
- byte[] tmp = new byte[byteCount];
- for (int i = byteCount-1; i >= 0; i--)
- {
- tmp[i] = (byte) ((tmpValue.intValue() & 0x7f) | 0x80);
- tmpValue = tmpValue.shiftRight(7);
- }
- tmp[byteCount-1] &= 0x7f;
- out.write(tmp, 0, tmp.length);
- }
- }
-
- private void doOutput(ByteArrayOutputStream aOut)
- {
- OIDTokenizer tok = new OIDTokenizer(identifier);
- int first = Integer.parseInt(tok.nextToken()) * 40;
-
- String secondToken = tok.nextToken();
- if (secondToken.length() <= 18)
- {
- writeField(aOut, first + Long.parseLong(secondToken));
- }
- else
- {
- writeField(aOut, new BigInteger(secondToken).add(BigInteger.valueOf(first)));
- }
-
- while (tok.hasMoreTokens())
- {
- String token = tok.nextToken();
- if (token.length() <= 18)
- {
- writeField(aOut, Long.parseLong(token));
- }
- else
- {
- writeField(aOut, new BigInteger(token));
- }
- }
- }
-
- protected synchronized byte[] getBody()
- {
- if (body == null)
- {
- ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
- doOutput(bOut);
-
- body = bOut.toByteArray();
- }
-
- return body;
- }
-
- boolean isConstructed()
- {
- return false;
- }
-
- int encodedLength()
- throws IOException
- {
- int length = getBody().length;
-
- return 1 + StreamUtil.calculateBodyLength(length) + length;
- }
-
- void encode(
- ASN1OutputStream out)
- throws IOException
- {
- byte[] enc = getBody();
-
- out.write(BERTags.OBJECT_IDENTIFIER);
- out.writeLength(enc.length);
- out.write(enc);
- }
-
- public int hashCode()
- {
- return identifier.hashCode();
- }
-
- boolean asn1Equals(
- ASN1Primitive o)
- {
- if (!(o instanceof DERObjectIdentifier))
- {
- return false;
- }
-
- return identifier.equals(((DERObjectIdentifier)o).identifier);
- }
-
- public String toString()
- {
- return getId();
- }
-
- private static boolean isValidBranchID(
- String branchID, int start)
- {
- boolean periodAllowed = false;
-
- int pos = branchID.length();
- while (--pos >= start)
- {
- char ch = branchID.charAt(pos);
-
- // TODO Leading zeroes?
- if ('0' <= ch && ch <= '9')
- {
- periodAllowed = true;
- continue;
- }
-
- if (ch == '.')
- {
- if (!periodAllowed)
- {
- return false;
- }
-
- periodAllowed = false;
- continue;
- }
-
- return false;
- }
-
- return periodAllowed;
- }
-
- private static boolean isValidIdentifier(
- String identifier)
- {
- if (identifier.length() < 3 || identifier.charAt(1) != '.')
- {
- return false;
- }
-
- char first = identifier.charAt(0);
- if (first < '0' || first > '2')
- {
- return false;
- }
-
- return isValidBranchID(identifier, 2);
- }
-
- private static ASN1ObjectIdentifier[][] cache = new ASN1ObjectIdentifier[256][];
-
- static ASN1ObjectIdentifier fromOctetString(byte[] enc)
- {
- if (enc.length < 3)
- {
- return new ASN1ObjectIdentifier(enc);
- }
-
- int idx1 = enc[enc.length - 2] & 0xff;
- // in this case top bit is always zero
- int idx2 = enc[enc.length - 1] & 0x7f;
-
- ASN1ObjectIdentifier possibleMatch;
-
- synchronized (cache)
- {
- ASN1ObjectIdentifier[] first = cache[idx1];
- if (first == null)
- {
- first = cache[idx1] = new ASN1ObjectIdentifier[128];
- }
-
- possibleMatch = first[idx2];
- if (possibleMatch == null)
- {
- return first[idx2] = new ASN1ObjectIdentifier(enc);
- }
-
- if (Arrays.areEqual(enc, possibleMatch.getBody()))
- {
- return possibleMatch;
- }
-
- idx1 = (idx1 + 1) & 0xff;
- first = cache[idx1];
- if (first == null)
- {
- first = cache[idx1] = new ASN1ObjectIdentifier[128];
- }
-
- possibleMatch = first[idx2];
- if (possibleMatch == null)
- {
- return first[idx2] = new ASN1ObjectIdentifier(enc);
- }
-
- if (Arrays.areEqual(enc, possibleMatch.getBody()))
- {
- return possibleMatch;
- }
-
- idx2 = (idx2 + 1) & 0x7f;
- possibleMatch = first[idx2];
- if (possibleMatch == null)
- {
- return first[idx2] = new ASN1ObjectIdentifier(enc);
- }
- }
-
- if (Arrays.areEqual(enc, possibleMatch.getBody()))
- {
- return possibleMatch;
- }
-
- return new ASN1ObjectIdentifier(enc);
- }
-}
+package org.bouncycastle.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; + +import org.bouncycastle.util.Arrays; + +public class DERObjectIdentifier + extends ASN1Primitive +{ + String identifier; + + private byte[] body; + + /** + * return an OID from the passed in object + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static ASN1ObjectIdentifier getInstance( + Object obj) + { + if (obj == null || obj instanceof ASN1ObjectIdentifier) + { + return (ASN1ObjectIdentifier)obj; + } + + if (obj instanceof DERObjectIdentifier) + { + return new ASN1ObjectIdentifier(((DERObjectIdentifier)obj).getId()); + } + + if (obj instanceof ASN1Encodable && ((ASN1Encodable)obj).toASN1Primitive() instanceof ASN1ObjectIdentifier) + { + return (ASN1ObjectIdentifier)((ASN1Encodable)obj).toASN1Primitive(); + } + + if (obj instanceof byte[]) + { + return ASN1ObjectIdentifier.fromOctetString((byte[])obj); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an Object Identifier from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static ASN1ObjectIdentifier getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + ASN1Primitive o = obj.getObject(); + + if (explicit || o instanceof DERObjectIdentifier) + { + return getInstance(o); + } + else + { + return ASN1ObjectIdentifier.fromOctetString(ASN1OctetString.getInstance(obj.getObject()).getOctets()); + } + } + + private static final long LONG_LIMIT = (Long.MAX_VALUE >> 7) - 0x7f; + + DERObjectIdentifier( + byte[] bytes) + { + StringBuffer objId = new StringBuffer(); + long value = 0; + BigInteger bigValue = null; + boolean first = true; + + for (int i = 0; i != bytes.length; i++) + { + int b = bytes[i] & 0xff; + + if (value <= LONG_LIMIT) + { + value += (b & 0x7f); + if ((b & 0x80) == 0) // end of number reached + { + if (first) + { + if (value < 40) + { + objId.append('0'); + } + else if (value < 80) + { + objId.append('1'); + value -= 40; + } + else + { + objId.append('2'); + value -= 80; + } + first = false; + } + + objId.append('.'); + objId.append(value); + value = 0; + } + else + { + value <<= 7; + } + } + else + { + if (bigValue == null) + { + bigValue = BigInteger.valueOf(value); + } + bigValue = bigValue.or(BigInteger.valueOf(b & 0x7f)); + if ((b & 0x80) == 0) + { + if (first) + { + objId.append('2'); + bigValue = bigValue.subtract(BigInteger.valueOf(80)); + first = false; + } + + objId.append('.'); + objId.append(bigValue); + bigValue = null; + value = 0; + } + else + { + bigValue = bigValue.shiftLeft(7); + } + } + } + + this.identifier = objId.toString(); + this.body = Arrays.clone(bytes); + } + + public DERObjectIdentifier( + String identifier) + { + if (identifier == null) + { + throw new IllegalArgumentException("'identifier' cannot be null"); + } + if (!isValidIdentifier(identifier)) + { + throw new IllegalArgumentException("string " + identifier + " not an OID"); + } + + this.identifier = identifier; + } + + DERObjectIdentifier(DERObjectIdentifier oid, String branchID) + { + if (!isValidBranchID(branchID, 0)) + { + throw new IllegalArgumentException("string " + branchID + " not a valid OID branch"); + } + + this.identifier = oid.getId() + "." + branchID; + } + + public String getId() + { + return identifier; + } + + private void writeField( + ByteArrayOutputStream out, + long fieldValue) + { + byte[] result = new byte[9]; + int pos = 8; + result[pos] = (byte)((int)fieldValue & 0x7f); + while (fieldValue >= (1L << 7)) + { + fieldValue >>= 7; + result[--pos] = (byte)((int)fieldValue & 0x7f | 0x80); + } + out.write(result, pos, 9 - pos); + } + + private void writeField( + ByteArrayOutputStream out, + BigInteger fieldValue) + { + int byteCount = (fieldValue.bitLength()+6)/7; + if (byteCount == 0) + { + out.write(0); + } + else + { + BigInteger tmpValue = fieldValue; + byte[] tmp = new byte[byteCount]; + for (int i = byteCount-1; i >= 0; i--) + { + tmp[i] = (byte) ((tmpValue.intValue() & 0x7f) | 0x80); + tmpValue = tmpValue.shiftRight(7); + } + tmp[byteCount-1] &= 0x7f; + out.write(tmp, 0, tmp.length); + } + } + + private void doOutput(ByteArrayOutputStream aOut) + { + OIDTokenizer tok = new OIDTokenizer(identifier); + int first = Integer.parseInt(tok.nextToken()) * 40; + + String secondToken = tok.nextToken(); + if (secondToken.length() <= 18) + { + writeField(aOut, first + Long.parseLong(secondToken)); + } + else + { + writeField(aOut, new BigInteger(secondToken).add(BigInteger.valueOf(first))); + } + + while (tok.hasMoreTokens()) + { + String token = tok.nextToken(); + if (token.length() <= 18) + { + writeField(aOut, Long.parseLong(token)); + } + else + { + writeField(aOut, new BigInteger(token)); + } + } + } + + protected synchronized byte[] getBody() + { + if (body == null) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + doOutput(bOut); + + body = bOut.toByteArray(); + } + + return body; + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + throws IOException + { + int length = getBody().length; + + return 1 + StreamUtil.calculateBodyLength(length) + length; + } + + void encode( + ASN1OutputStream out) + throws IOException + { + byte[] enc = getBody(); + + out.write(BERTags.OBJECT_IDENTIFIER); + out.writeLength(enc.length); + out.write(enc); + } + + public int hashCode() + { + return identifier.hashCode(); + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof DERObjectIdentifier)) + { + return false; + } + + return identifier.equals(((DERObjectIdentifier)o).identifier); + } + + public String toString() + { + return getId(); + } + + private static boolean isValidBranchID( + String branchID, int start) + { + boolean periodAllowed = false; + + int pos = branchID.length(); + while (--pos >= start) + { + char ch = branchID.charAt(pos); + + // TODO Leading zeroes? + if ('0' <= ch && ch <= '9') + { + periodAllowed = true; + continue; + } + + if (ch == '.') + { + if (!periodAllowed) + { + return false; + } + + periodAllowed = false; + continue; + } + + return false; + } + + return periodAllowed; + } + + private static boolean isValidIdentifier( + String identifier) + { + if (identifier.length() < 3 || identifier.charAt(1) != '.') + { + return false; + } + + char first = identifier.charAt(0); + if (first < '0' || first > '2') + { + return false; + } + + return isValidBranchID(identifier, 2); + } + + private static ASN1ObjectIdentifier[][] cache = new ASN1ObjectIdentifier[256][]; + + static ASN1ObjectIdentifier fromOctetString(byte[] enc) + { + if (enc.length < 3) + { + return new ASN1ObjectIdentifier(enc); + } + + int idx1 = enc[enc.length - 2] & 0xff; + // in this case top bit is always zero + int idx2 = enc[enc.length - 1] & 0x7f; + + ASN1ObjectIdentifier possibleMatch; + + synchronized (cache) + { + ASN1ObjectIdentifier[] first = cache[idx1]; + if (first == null) + { + first = cache[idx1] = new ASN1ObjectIdentifier[128]; + } + + possibleMatch = first[idx2]; + if (possibleMatch == null) + { + return first[idx2] = new ASN1ObjectIdentifier(enc); + } + + if (Arrays.areEqual(enc, possibleMatch.getBody())) + { + return possibleMatch; + } + + idx1 = (idx1 + 1) & 0xff; + first = cache[idx1]; + if (first == null) + { + first = cache[idx1] = new ASN1ObjectIdentifier[128]; + } + + possibleMatch = first[idx2]; + if (possibleMatch == null) + { + return first[idx2] = new ASN1ObjectIdentifier(enc); + } + + if (Arrays.areEqual(enc, possibleMatch.getBody())) + { + return possibleMatch; + } + + idx2 = (idx2 + 1) & 0x7f; + possibleMatch = first[idx2]; + if (possibleMatch == null) + { + return first[idx2] = new ASN1ObjectIdentifier(enc); + } + } + + if (Arrays.areEqual(enc, possibleMatch.getBody())) + { + return possibleMatch; + } + + return new ASN1ObjectIdentifier(enc); + } +} diff --git a/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java b/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java index c604d4a8..f005cfa8 100644 --- a/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java +++ b/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java @@ -1,132 +1,132 @@ -package org.bouncycastle.asn1.x9;
-
-import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-
-public interface X9ObjectIdentifiers
-{
- //
- // X9.62
- //
- // ansi-X9-62 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
- // us(840) ansi-x962(10045) }
- //
- static final ASN1ObjectIdentifier ansi_X9_62 = new ASN1ObjectIdentifier("1.2.840.10045");
- static final ASN1ObjectIdentifier id_fieldType = ansi_X9_62.branch("1");
-
- static final ASN1ObjectIdentifier prime_field = id_fieldType.branch("1");
-
- static final ASN1ObjectIdentifier characteristic_two_field = id_fieldType.branch("2");
-
- static final ASN1ObjectIdentifier gnBasis = characteristic_two_field.branch("3.1");
-
- static final ASN1ObjectIdentifier tpBasis = characteristic_two_field.branch("3.2");
-
- static final ASN1ObjectIdentifier ppBasis = characteristic_two_field.branch("3.3");
-
- static final ASN1ObjectIdentifier id_ecSigType = ansi_X9_62.branch("4");
-
- static final ASN1ObjectIdentifier ecdsa_with_SHA1 = new ASN1ObjectIdentifier(id_ecSigType + ".1");
-
- static final ASN1ObjectIdentifier id_publicKeyType = ansi_X9_62.branch("2");
-
- static final ASN1ObjectIdentifier id_ecPublicKey = id_publicKeyType.branch("1");
-
- static final ASN1ObjectIdentifier ecdsa_with_SHA2 = id_ecSigType.branch("3");
-
- static final ASN1ObjectIdentifier ecdsa_with_SHA224 = ecdsa_with_SHA2.branch("1");
-
- static final ASN1ObjectIdentifier ecdsa_with_SHA256 = ecdsa_with_SHA2.branch("2");
-
- static final ASN1ObjectIdentifier ecdsa_with_SHA384 = ecdsa_with_SHA2.branch("3");
-
- static final ASN1ObjectIdentifier ecdsa_with_SHA512 = ecdsa_with_SHA2.branch("4");
-
- //
- // named curves
- //
- static final ASN1ObjectIdentifier ellipticCurve = ansi_X9_62.branch("3");
-
- //
- // Two Curves
- //
- static final ASN1ObjectIdentifier cTwoCurve = ellipticCurve.branch("0");
-
- static final ASN1ObjectIdentifier c2pnb163v1 = cTwoCurve.branch("1");
- static final ASN1ObjectIdentifier c2pnb163v2 = cTwoCurve.branch("2");
- static final ASN1ObjectIdentifier c2pnb163v3 = cTwoCurve.branch("3");
- static final ASN1ObjectIdentifier c2pnb176w1 = cTwoCurve.branch("4");
- static final ASN1ObjectIdentifier c2tnb191v1 = cTwoCurve.branch("5");
- static final ASN1ObjectIdentifier c2tnb191v2 = cTwoCurve.branch("6");
- static final ASN1ObjectIdentifier c2tnb191v3 = cTwoCurve.branch("7");
- static final ASN1ObjectIdentifier c2onb191v4 = cTwoCurve.branch("8");
- static final ASN1ObjectIdentifier c2onb191v5 = cTwoCurve.branch("9");
- static final ASN1ObjectIdentifier c2pnb208w1 = cTwoCurve.branch("10");
- static final ASN1ObjectIdentifier c2tnb239v1 = cTwoCurve.branch("11");
- static final ASN1ObjectIdentifier c2tnb239v2 = cTwoCurve.branch("12");
- static final ASN1ObjectIdentifier c2tnb239v3 = cTwoCurve.branch("13");
- static final ASN1ObjectIdentifier c2onb239v4 = cTwoCurve.branch("14");
- static final ASN1ObjectIdentifier c2onb239v5 = cTwoCurve.branch("15");
- static final ASN1ObjectIdentifier c2pnb272w1 = cTwoCurve.branch("16");
- static final ASN1ObjectIdentifier c2pnb304w1 = cTwoCurve.branch("17");
- static final ASN1ObjectIdentifier c2tnb359v1 = cTwoCurve.branch("18");
- static final ASN1ObjectIdentifier c2pnb368w1 = cTwoCurve.branch("19");
- static final ASN1ObjectIdentifier c2tnb431r1 = cTwoCurve.branch("20");
-
- //
- // Prime
- //
- static final ASN1ObjectIdentifier primeCurve = ellipticCurve.branch("1");
-
- static final ASN1ObjectIdentifier prime192v1 = primeCurve.branch("1");
- static final ASN1ObjectIdentifier prime192v2 = primeCurve.branch("2");
- static final ASN1ObjectIdentifier prime192v3 = primeCurve.branch("3");
- static final ASN1ObjectIdentifier prime239v1 = primeCurve.branch("4");
- static final ASN1ObjectIdentifier prime239v2 = primeCurve.branch("5");
- static final ASN1ObjectIdentifier prime239v3 = primeCurve.branch("6");
- static final ASN1ObjectIdentifier prime256v1 = primeCurve.branch("7");
-
- //
- // DSA
- //
- // dsapublicnumber OBJECT IDENTIFIER ::= { iso(1) member-body(2)
- // us(840) ansi-x957(10040) number-type(4) 1 }
- static final ASN1ObjectIdentifier id_dsa = new ASN1ObjectIdentifier("1.2.840.10040.4.1");
-
- /**
- * id-dsa-with-sha1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) x9-57
- * (10040) x9cm(4) 3 }
- */
- public static final ASN1ObjectIdentifier id_dsa_with_sha1 = new ASN1ObjectIdentifier("1.2.840.10040.4.3");
-
- /**
- * X9.63
- */
- public static final ASN1ObjectIdentifier x9_63_scheme = new ASN1ObjectIdentifier("1.3.133.16.840.63.0");
- public static final ASN1ObjectIdentifier dhSinglePass_stdDH_sha1kdf_scheme = x9_63_scheme.branch("2");
- public static final ASN1ObjectIdentifier dhSinglePass_cofactorDH_sha1kdf_scheme = x9_63_scheme.branch("3");
- public static final ASN1ObjectIdentifier mqvSinglePass_sha1kdf_scheme = x9_63_scheme.branch("16");
-
- /**
- * X9.42
- */
-
- static final ASN1ObjectIdentifier ansi_X9_42 = new ASN1ObjectIdentifier("1.2.840.10046");
-
- //
- // Diffie-Hellman
- //
- // dhpublicnumber OBJECT IDENTIFIER ::= { iso(1) member-body(2)
- // us(840) ansi-x942(10046) number-type(2) 1 }
- //
- public static final ASN1ObjectIdentifier dhpublicnumber = ansi_X9_42.branch("2.1");
-
- public static final ASN1ObjectIdentifier x9_42_schemes = ansi_X9_42.branch("3");
- public static final ASN1ObjectIdentifier dhStatic = x9_42_schemes.branch("1");
- public static final ASN1ObjectIdentifier dhEphem = x9_42_schemes.branch("2");
- public static final ASN1ObjectIdentifier dhOneFlow = x9_42_schemes.branch("3");
- public static final ASN1ObjectIdentifier dhHybrid1 = x9_42_schemes.branch("4");
- public static final ASN1ObjectIdentifier dhHybrid2 = x9_42_schemes.branch("5");
- public static final ASN1ObjectIdentifier dhHybridOneFlow = x9_42_schemes.branch("6");
- public static final ASN1ObjectIdentifier mqv2 = x9_42_schemes.branch("7");
- public static final ASN1ObjectIdentifier mqv1 = x9_42_schemes.branch("8");
-}
+package org.bouncycastle.asn1.x9; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; + +public interface X9ObjectIdentifiers +{ + // + // X9.62 + // + // ansi-X9-62 OBJECT IDENTIFIER ::= { iso(1) member-body(2) + // us(840) ansi-x962(10045) } + // + static final ASN1ObjectIdentifier ansi_X9_62 = new ASN1ObjectIdentifier("1.2.840.10045"); + static final ASN1ObjectIdentifier id_fieldType = ansi_X9_62.branch("1"); + + static final ASN1ObjectIdentifier prime_field = id_fieldType.branch("1"); + + static final ASN1ObjectIdentifier characteristic_two_field = id_fieldType.branch("2"); + + static final ASN1ObjectIdentifier gnBasis = characteristic_two_field.branch("3.1"); + + static final ASN1ObjectIdentifier tpBasis = characteristic_two_field.branch("3.2"); + + static final ASN1ObjectIdentifier ppBasis = characteristic_two_field.branch("3.3"); + + static final ASN1ObjectIdentifier id_ecSigType = ansi_X9_62.branch("4"); + + static final ASN1ObjectIdentifier ecdsa_with_SHA1 = new ASN1ObjectIdentifier(id_ecSigType + ".1"); + + static final ASN1ObjectIdentifier id_publicKeyType = ansi_X9_62.branch("2"); + + static final ASN1ObjectIdentifier id_ecPublicKey = id_publicKeyType.branch("1"); + + static final ASN1ObjectIdentifier ecdsa_with_SHA2 = id_ecSigType.branch("3"); + + static final ASN1ObjectIdentifier ecdsa_with_SHA224 = ecdsa_with_SHA2.branch("1"); + + static final ASN1ObjectIdentifier ecdsa_with_SHA256 = ecdsa_with_SHA2.branch("2"); + + static final ASN1ObjectIdentifier ecdsa_with_SHA384 = ecdsa_with_SHA2.branch("3"); + + static final ASN1ObjectIdentifier ecdsa_with_SHA512 = ecdsa_with_SHA2.branch("4"); + + // + // named curves + // + static final ASN1ObjectIdentifier ellipticCurve = ansi_X9_62.branch("3"); + + // + // Two Curves + // + static final ASN1ObjectIdentifier cTwoCurve = ellipticCurve.branch("0"); + + static final ASN1ObjectIdentifier c2pnb163v1 = cTwoCurve.branch("1"); + static final ASN1ObjectIdentifier c2pnb163v2 = cTwoCurve.branch("2"); + static final ASN1ObjectIdentifier c2pnb163v3 = cTwoCurve.branch("3"); + static final ASN1ObjectIdentifier c2pnb176w1 = cTwoCurve.branch("4"); + static final ASN1ObjectIdentifier c2tnb191v1 = cTwoCurve.branch("5"); + static final ASN1ObjectIdentifier c2tnb191v2 = cTwoCurve.branch("6"); + static final ASN1ObjectIdentifier c2tnb191v3 = cTwoCurve.branch("7"); + static final ASN1ObjectIdentifier c2onb191v4 = cTwoCurve.branch("8"); + static final ASN1ObjectIdentifier c2onb191v5 = cTwoCurve.branch("9"); + static final ASN1ObjectIdentifier c2pnb208w1 = cTwoCurve.branch("10"); + static final ASN1ObjectIdentifier c2tnb239v1 = cTwoCurve.branch("11"); + static final ASN1ObjectIdentifier c2tnb239v2 = cTwoCurve.branch("12"); + static final ASN1ObjectIdentifier c2tnb239v3 = cTwoCurve.branch("13"); + static final ASN1ObjectIdentifier c2onb239v4 = cTwoCurve.branch("14"); + static final ASN1ObjectIdentifier c2onb239v5 = cTwoCurve.branch("15"); + static final ASN1ObjectIdentifier c2pnb272w1 = cTwoCurve.branch("16"); + static final ASN1ObjectIdentifier c2pnb304w1 = cTwoCurve.branch("17"); + static final ASN1ObjectIdentifier c2tnb359v1 = cTwoCurve.branch("18"); + static final ASN1ObjectIdentifier c2pnb368w1 = cTwoCurve.branch("19"); + static final ASN1ObjectIdentifier c2tnb431r1 = cTwoCurve.branch("20"); + + // + // Prime + // + static final ASN1ObjectIdentifier primeCurve = ellipticCurve.branch("1"); + + static final ASN1ObjectIdentifier prime192v1 = primeCurve.branch("1"); + static final ASN1ObjectIdentifier prime192v2 = primeCurve.branch("2"); + static final ASN1ObjectIdentifier prime192v3 = primeCurve.branch("3"); + static final ASN1ObjectIdentifier prime239v1 = primeCurve.branch("4"); + static final ASN1ObjectIdentifier prime239v2 = primeCurve.branch("5"); + static final ASN1ObjectIdentifier prime239v3 = primeCurve.branch("6"); + static final ASN1ObjectIdentifier prime256v1 = primeCurve.branch("7"); + + // + // DSA + // + // dsapublicnumber OBJECT IDENTIFIER ::= { iso(1) member-body(2) + // us(840) ansi-x957(10040) number-type(4) 1 } + static final ASN1ObjectIdentifier id_dsa = new ASN1ObjectIdentifier("1.2.840.10040.4.1"); + + /** + * id-dsa-with-sha1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) x9-57 + * (10040) x9cm(4) 3 } + */ + public static final ASN1ObjectIdentifier id_dsa_with_sha1 = new ASN1ObjectIdentifier("1.2.840.10040.4.3"); + + /** + * X9.63 + */ + public static final ASN1ObjectIdentifier x9_63_scheme = new ASN1ObjectIdentifier("1.3.133.16.840.63.0"); + public static final ASN1ObjectIdentifier dhSinglePass_stdDH_sha1kdf_scheme = x9_63_scheme.branch("2"); + public static final ASN1ObjectIdentifier dhSinglePass_cofactorDH_sha1kdf_scheme = x9_63_scheme.branch("3"); + public static final ASN1ObjectIdentifier mqvSinglePass_sha1kdf_scheme = x9_63_scheme.branch("16"); + + /** + * X9.42 + */ + + static final ASN1ObjectIdentifier ansi_X9_42 = new ASN1ObjectIdentifier("1.2.840.10046"); + + // + // Diffie-Hellman + // + // dhpublicnumber OBJECT IDENTIFIER ::= { iso(1) member-body(2) + // us(840) ansi-x942(10046) number-type(2) 1 } + // + public static final ASN1ObjectIdentifier dhpublicnumber = ansi_X9_42.branch("2.1"); + + public static final ASN1ObjectIdentifier x9_42_schemes = ansi_X9_42.branch("3"); + public static final ASN1ObjectIdentifier dhStatic = x9_42_schemes.branch("1"); + public static final ASN1ObjectIdentifier dhEphem = x9_42_schemes.branch("2"); + public static final ASN1ObjectIdentifier dhOneFlow = x9_42_schemes.branch("3"); + public static final ASN1ObjectIdentifier dhHybrid1 = x9_42_schemes.branch("4"); + public static final ASN1ObjectIdentifier dhHybrid2 = x9_42_schemes.branch("5"); + public static final ASN1ObjectIdentifier dhHybridOneFlow = x9_42_schemes.branch("6"); + public static final ASN1ObjectIdentifier mqv2 = x9_42_schemes.branch("7"); + public static final ASN1ObjectIdentifier mqv1 = x9_42_schemes.branch("8"); +} diff --git a/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java b/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java index 86873cec..1e2e0723 100644 --- a/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java +++ b/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java @@ -1,391 +1,391 @@ -package org.bouncycastle.bcpg;
-
-import org.bouncycastle.util.io.Streams;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * reader for PGP objects
- */
-public class BCPGInputStream
- extends InputStream implements PacketTags
-{
- InputStream in;
- boolean next = false;
- int nextB;
-
- public BCPGInputStream(
- InputStream in)
- {
- this.in = in;
- }
-
- public int available()
- throws IOException
- {
- return in.available();
- }
-
- public int read()
- throws IOException
- {
- if (next)
- {
- next = false;
-
- return nextB;
- }
- else
- {
- return in.read();
- }
- }
-
- public int read(
- byte[] buf,
- int off,
- int len)
- throws IOException
- {
- if (len == 0)
- {
- return 0;
- }
-
- if (!next)
- {
- return in.read(buf, off, len);
- }
-
- // We have next byte waiting, so return it
-
- if (nextB < 0)
- {
- return -1; // EOF
- }
-
- buf[off] = (byte)nextB; // May throw NullPointerException...
- next = false; // ...so only set this afterwards
-
- return 1;
- }
-
- public void readFully(
- byte[] buf,
- int off,
- int len)
- throws IOException
- {
- if (Streams.readFully(this, buf, off, len) < len)
- {
- throw new EOFException();
- }
- }
-
- public byte[] readAll()
- throws IOException
- {
- return Streams.readAll(this);
- }
-
- public void readFully(
- byte[] buf)
- throws IOException
- {
- readFully(buf, 0, buf.length);
- }
-
- /**
- * returns the next packet tag in the stream.
- *
- * @return the tag number.
- *
- * @throws IOException
- */
- public int nextPacketTag()
- throws IOException
- {
- if (!next)
- {
- try
- {
- nextB = in.read();
- }
- catch (EOFException e)
- {
- nextB = -1;
- }
- }
-
- next = true;
-
- if (nextB >= 0)
- {
- if ((nextB & 0x40) != 0) // new
- {
- return (nextB & 0x3f);
- }
- else // old
- {
- return ((nextB & 0x3f) >> 2);
- }
- }
-
- return nextB;
- }
-
- public Packet readPacket()
- throws IOException
- {
- int hdr = this.read();
-
- if (hdr < 0)
- {
- return null;
- }
-
- if ((hdr & 0x80) == 0)
- {
- throw new IOException("invalid header encountered");
- }
-
- boolean newPacket = (hdr & 0x40) != 0;
- int tag = 0;
- int bodyLen = 0;
- boolean partial = false;
-
- if (newPacket)
- {
- tag = hdr & 0x3f;
-
- int l = this.read();
-
- if (l < 192)
- {
- bodyLen = l;
- }
- else if (l <= 223)
- {
- int b = in.read();
-
- bodyLen = ((l - 192) << 8) + (b) + 192;
- }
- else if (l == 255)
- {
- bodyLen = (in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read();
- }
- else
- {
- partial = true;
- bodyLen = 1 << (l & 0x1f);
- }
- }
- else
- {
- int lengthType = hdr & 0x3;
-
- tag = (hdr & 0x3f) >> 2;
-
- switch (lengthType)
- {
- case 0:
- bodyLen = this.read();
- break;
- case 1:
- bodyLen = (this.read() << 8) | this.read();
- break;
- case 2:
- bodyLen = (this.read() << 24) | (this.read() << 16) | (this.read() << 8) | this.read();
- break;
- case 3:
- partial = true;
- break;
- default:
- throw new IOException("unknown length type encountered");
- }
- }
-
- BCPGInputStream objStream;
-
- if (bodyLen == 0 && partial)
- {
- objStream = this;
- }
- else
- {
- objStream = new BCPGInputStream(new PartialInputStream(this, partial, bodyLen));
- }
-
- switch (tag)
- {
- case RESERVED:
- return new InputStreamPacket(objStream);
- case PUBLIC_KEY_ENC_SESSION:
- return new PublicKeyEncSessionPacket(objStream);
- case SIGNATURE:
- return new SignaturePacket(objStream);
- case SYMMETRIC_KEY_ENC_SESSION:
- return new SymmetricKeyEncSessionPacket(objStream);
- case ONE_PASS_SIGNATURE:
- return new OnePassSignaturePacket(objStream);
- case SECRET_KEY:
- return new SecretKeyPacket(objStream);
- case PUBLIC_KEY:
- return new PublicKeyPacket(objStream);
- case SECRET_SUBKEY:
- return new SecretSubkeyPacket(objStream);
- case COMPRESSED_DATA:
- return new CompressedDataPacket(objStream);
- case SYMMETRIC_KEY_ENC:
- return new SymmetricEncDataPacket(objStream);
- case MARKER:
- return new MarkerPacket(objStream);
- case LITERAL_DATA:
- return new LiteralDataPacket(objStream);
- case TRUST:
- return new TrustPacket(objStream);
- case USER_ID:
- return new UserIDPacket(objStream);
- case USER_ATTRIBUTE:
- return new UserAttributePacket(objStream);
- case PUBLIC_SUBKEY:
- return new PublicSubkeyPacket(objStream);
- case SYM_ENC_INTEGRITY_PRO:
- return new SymmetricEncIntegrityPacket(objStream);
- case MOD_DETECTION_CODE:
- return new ModDetectionCodePacket(objStream);
- case EXPERIMENTAL_1:
- case EXPERIMENTAL_2:
- case EXPERIMENTAL_3:
- case EXPERIMENTAL_4:
- return new ExperimentalPacket(tag, objStream);
- default:
- throw new IOException("unknown packet type encountered: " + tag);
- }
- }
-
- public void close()
- throws IOException
- {
- in.close();
- }
-
- /**
- * a stream that overlays our input stream, allowing the user to only read a segment of it.
- *
- * NB: dataLength will be negative if the segment length is in the upper range above 2**31.
- */
- private static class PartialInputStream
- extends InputStream
- {
- private BCPGInputStream in;
- private boolean partial;
- private int dataLength;
-
- PartialInputStream(
- BCPGInputStream in,
- boolean partial,
- int dataLength)
- {
- this.in = in;
- this.partial = partial;
- this.dataLength = dataLength;
- }
-
- public int available()
- throws IOException
- {
- int avail = in.available();
-
- if (avail <= dataLength || dataLength < 0)
- {
- return avail;
- }
- else
- {
- if (partial && dataLength == 0)
- {
- return 1;
- }
- return dataLength;
- }
- }
-
- private int loadDataLength()
- throws IOException
- {
- int l = in.read();
-
- if (l < 0)
- {
- return -1;
- }
-
- partial = false;
- if (l < 192)
- {
- dataLength = l;
- }
- else if (l <= 223)
- {
- dataLength = ((l - 192) << 8) + (in.read()) + 192;
- }
- else if (l == 255)
- {
- dataLength = (in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read();
- }
- else
- {
- partial = true;
- dataLength = 1 << (l & 0x1f);
- }
-
- return dataLength;
- }
-
- public int read(byte[] buf, int offset, int len)
- throws IOException
- {
- do
- {
- if (dataLength != 0)
- {
- int readLen = (dataLength > len || dataLength < 0) ? len : dataLength;
- readLen = in.read(buf, offset, readLen);
- if (readLen < 0)
- {
- throw new EOFException("premature end of stream in PartialInputStream");
- }
- dataLength -= readLen;
- return readLen;
- }
- }
- while (partial && loadDataLength() >= 0);
-
- return -1;
- }
-
- public int read()
- throws IOException
- {
- do
- {
- if (dataLength != 0)
- {
- int ch = in.read();
- if (ch < 0)
- {
- throw new EOFException("premature end of stream in PartialInputStream");
- }
- dataLength--;
- return ch;
- }
- }
- while (partial && loadDataLength() >= 0);
-
- return -1;
- }
- }
-}
+package org.bouncycastle.bcpg; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +import org.bouncycastle.util.io.Streams; + +/** + * reader for PGP objects + */ +public class BCPGInputStream + extends InputStream implements PacketTags +{ + InputStream in; + boolean next = false; + int nextB; + + public BCPGInputStream( + InputStream in) + { + this.in = in; + } + + public int available() + throws IOException + { + return in.available(); + } + + public int read() + throws IOException + { + if (next) + { + next = false; + + return nextB; + } + else + { + return in.read(); + } + } + + public int read( + byte[] buf, + int off, + int len) + throws IOException + { + if (len == 0) + { + return 0; + } + + if (!next) + { + return in.read(buf, off, len); + } + + // We have next byte waiting, so return it + + if (nextB < 0) + { + return -1; // EOF + } + + buf[off] = (byte)nextB; // May throw NullPointerException... + next = false; // ...so only set this afterwards + + return 1; + } + + public void readFully( + byte[] buf, + int off, + int len) + throws IOException + { + if (Streams.readFully(this, buf, off, len) < len) + { + throw new EOFException(); + } + } + + public byte[] readAll() + throws IOException + { + return Streams.readAll(this); + } + + public void readFully( + byte[] buf) + throws IOException + { + readFully(buf, 0, buf.length); + } + + /** + * returns the next packet tag in the stream. + * + * @return the tag number. + * + * @throws IOException + */ + public int nextPacketTag() + throws IOException + { + if (!next) + { + try + { + nextB = in.read(); + } + catch (EOFException e) + { + nextB = -1; + } + } + + next = true; + + if (nextB >= 0) + { + if ((nextB & 0x40) != 0) // new + { + return (nextB & 0x3f); + } + else // old + { + return ((nextB & 0x3f) >> 2); + } + } + + return nextB; + } + + public Packet readPacket() + throws IOException + { + int hdr = this.read(); + + if (hdr < 0) + { + return null; + } + + if ((hdr & 0x80) == 0) + { + throw new IOException("invalid header encountered"); + } + + boolean newPacket = (hdr & 0x40) != 0; + int tag = 0; + int bodyLen = 0; + boolean partial = false; + + if (newPacket) + { + tag = hdr & 0x3f; + + int l = this.read(); + + if (l < 192) + { + bodyLen = l; + } + else if (l <= 223) + { + int b = in.read(); + + bodyLen = ((l - 192) << 8) + (b) + 192; + } + else if (l == 255) + { + bodyLen = (in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read(); + } + else + { + partial = true; + bodyLen = 1 << (l & 0x1f); + } + } + else + { + int lengthType = hdr & 0x3; + + tag = (hdr & 0x3f) >> 2; + + switch (lengthType) + { + case 0: + bodyLen = this.read(); + break; + case 1: + bodyLen = (this.read() << 8) | this.read(); + break; + case 2: + bodyLen = (this.read() << 24) | (this.read() << 16) | (this.read() << 8) | this.read(); + break; + case 3: + partial = true; + break; + default: + throw new IOException("unknown length type encountered"); + } + } + + BCPGInputStream objStream; + + if (bodyLen == 0 && partial) + { + objStream = this; + } + else + { + objStream = new BCPGInputStream(new PartialInputStream(this, partial, bodyLen)); + } + + switch (tag) + { + case RESERVED: + return new InputStreamPacket(objStream); + case PUBLIC_KEY_ENC_SESSION: + return new PublicKeyEncSessionPacket(objStream); + case SIGNATURE: + return new SignaturePacket(objStream); + case SYMMETRIC_KEY_ENC_SESSION: + return new SymmetricKeyEncSessionPacket(objStream); + case ONE_PASS_SIGNATURE: + return new OnePassSignaturePacket(objStream); + case SECRET_KEY: + return new SecretKeyPacket(objStream); + case PUBLIC_KEY: + return new PublicKeyPacket(objStream); + case SECRET_SUBKEY: + return new SecretSubkeyPacket(objStream); + case COMPRESSED_DATA: + return new CompressedDataPacket(objStream); + case SYMMETRIC_KEY_ENC: + return new SymmetricEncDataPacket(objStream); + case MARKER: + return new MarkerPacket(objStream); + case LITERAL_DATA: + return new LiteralDataPacket(objStream); + case TRUST: + return new TrustPacket(objStream); + case USER_ID: + return new UserIDPacket(objStream); + case USER_ATTRIBUTE: + return new UserAttributePacket(objStream); + case PUBLIC_SUBKEY: + return new PublicSubkeyPacket(objStream); + case SYM_ENC_INTEGRITY_PRO: + return new SymmetricEncIntegrityPacket(objStream); + case MOD_DETECTION_CODE: + return new ModDetectionCodePacket(objStream); + case EXPERIMENTAL_1: + case EXPERIMENTAL_2: + case EXPERIMENTAL_3: + case EXPERIMENTAL_4: + return new ExperimentalPacket(tag, objStream); + default: + throw new IOException("unknown packet type encountered: " + tag); + } + } + + public void close() + throws IOException + { + in.close(); + } + + /** + * a stream that overlays our input stream, allowing the user to only read a segment of it. + * + * NB: dataLength will be negative if the segment length is in the upper range above 2**31. + */ + private static class PartialInputStream + extends InputStream + { + private BCPGInputStream in; + private boolean partial; + private int dataLength; + + PartialInputStream( + BCPGInputStream in, + boolean partial, + int dataLength) + { + this.in = in; + this.partial = partial; + this.dataLength = dataLength; + } + + public int available() + throws IOException + { + int avail = in.available(); + + if (avail <= dataLength || dataLength < 0) + { + return avail; + } + else + { + if (partial && dataLength == 0) + { + return 1; + } + return dataLength; + } + } + + private int loadDataLength() + throws IOException + { + int l = in.read(); + + if (l < 0) + { + return -1; + } + + partial = false; + if (l < 192) + { + dataLength = l; + } + else if (l <= 223) + { + dataLength = ((l - 192) << 8) + (in.read()) + 192; + } + else if (l == 255) + { + dataLength = (in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read(); + } + else + { + partial = true; + dataLength = 1 << (l & 0x1f); + } + + return dataLength; + } + + public int read(byte[] buf, int offset, int len) + throws IOException + { + do + { + if (dataLength != 0) + { + int readLen = (dataLength > len || dataLength < 0) ? len : dataLength; + readLen = in.read(buf, offset, readLen); + if (readLen < 0) + { + throw new EOFException("premature end of stream in PartialInputStream"); + } + dataLength -= readLen; + return readLen; + } + } + while (partial && loadDataLength() >= 0); + + return -1; + } + + public int read() + throws IOException + { + do + { + if (dataLength != 0) + { + int ch = in.read(); + if (ch < 0) + { + throw new EOFException("premature end of stream in PartialInputStream"); + } + dataLength--; + return ch; + } + } + while (partial && loadDataLength() >= 0); + + return -1; + } + } +} diff --git a/src/main/java/org/bouncycastle/bcpg/ExperimentalPacket.java b/src/main/java/org/bouncycastle/bcpg/ExperimentalPacket.java index e3b0456a..8407052b 100644 --- a/src/main/java/org/bouncycastle/bcpg/ExperimentalPacket.java +++ b/src/main/java/org/bouncycastle/bcpg/ExperimentalPacket.java @@ -1,46 +1,46 @@ -package org.bouncycastle.bcpg;
-
-import java.io.IOException;
-
-import org.bouncycastle.util.Arrays;
-
-/**
- * basic packet for an experimental packet.
- */
-public class ExperimentalPacket
- extends ContainedPacket implements PublicKeyAlgorithmTags
-{
- private int tag;
- private byte[] contents;
-
- /**
- *
- * @param in
- * @throws IOException
- */
- ExperimentalPacket(
- int tag,
- BCPGInputStream in)
- throws IOException
- {
- this.tag = tag;
- this.contents = in.readAll();
- }
-
- public int getTag()
- {
- return tag;
- }
-
- public byte[] getContents()
- {
- return Arrays.clone(contents);
- }
-
- public void encode(
- BCPGOutputStream out)
- throws IOException
- {
- out.writePacket(tag, contents, true);
- }
-}
+package org.bouncycastle.bcpg; + +import java.io.IOException; + +import org.bouncycastle.util.Arrays; + +/** + * basic packet for an experimental packet. + */ +public class ExperimentalPacket + extends ContainedPacket implements PublicKeyAlgorithmTags +{ + private int tag; + private byte[] contents; + + /** + * + * @param in + * @throws IOException + */ + ExperimentalPacket( + int tag, + BCPGInputStream in) + throws IOException + { + this.tag = tag; + this.contents = in.readAll(); + } + + public int getTag() + { + return tag; + } + + public byte[] getContents() + { + return Arrays.clone(contents); + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + out.writePacket(tag, contents, true); + } +} diff --git a/src/main/java/org/bouncycastle/bcpg/SecretKeyPacket.java b/src/main/java/org/bouncycastle/bcpg/SecretKeyPacket.java index 6be9632a..d362bb93 100644 --- a/src/main/java/org/bouncycastle/bcpg/SecretKeyPacket.java +++ b/src/main/java/org/bouncycastle/bcpg/SecretKeyPacket.java @@ -1,185 +1,185 @@ -package org.bouncycastle.bcpg;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-/**
- * basic packet for a PGP secret key
- */
-public class SecretKeyPacket
- extends ContainedPacket implements PublicKeyAlgorithmTags
-{
- public static final int USAGE_NONE = 0x00;
- public static final int USAGE_CHECKSUM = 0xff;
- public static final int USAGE_SHA1 = 0xfe;
-
- private PublicKeyPacket pubKeyPacket;
- private byte[] secKeyData;
- private int s2kUsage;
- private int encAlgorithm;
- private S2K s2k;
- private byte[] iv;
-
- /**
- *
- * @param in
- * @throws IOException
- */
- SecretKeyPacket(
- BCPGInputStream in)
- throws IOException
- {
- if (this instanceof SecretSubkeyPacket)
- {
- pubKeyPacket = new PublicSubkeyPacket(in);
- }
- else
- {
- pubKeyPacket = new PublicKeyPacket(in);
- }
-
- s2kUsage = in.read();
-
- if (s2kUsage == USAGE_CHECKSUM || s2kUsage == USAGE_SHA1)
- {
- encAlgorithm = in.read();
- s2k = new S2K(in);
- }
- else
- {
- encAlgorithm = s2kUsage;
- }
-
- if (!(s2k != null && s2k.getType() == S2K.GNU_DUMMY_S2K && s2k.getProtectionMode() == 0x01))
- {
- if (s2kUsage != 0)
- {
- if (encAlgorithm < 7)
- {
- iv = new byte[8];
- }
- else
- {
- iv = new byte[16];
- }
- in.readFully(iv, 0, iv.length);
- }
- }
-
- this.secKeyData = in.readAll();
- }
-
- /**
- *
- * @param pubKeyPacket
- * @param encAlgorithm
- * @param s2k
- * @param iv
- * @param secKeyData
- */
- public SecretKeyPacket(
- PublicKeyPacket pubKeyPacket,
- int encAlgorithm,
- S2K s2k,
- byte[] iv,
- byte[] secKeyData)
- {
- this.pubKeyPacket = pubKeyPacket;
- this.encAlgorithm = encAlgorithm;
-
- if (encAlgorithm != SymmetricKeyAlgorithmTags.NULL)
- {
- this.s2kUsage = USAGE_CHECKSUM;
- }
- else
- {
- this.s2kUsage = USAGE_NONE;
- }
-
- this.s2k = s2k;
- this.iv = iv;
- this.secKeyData = secKeyData;
- }
-
- public SecretKeyPacket(
- PublicKeyPacket pubKeyPacket,
- int encAlgorithm,
- int s2kUsage,
- S2K s2k,
- byte[] iv,
- byte[] secKeyData)
- {
- this.pubKeyPacket = pubKeyPacket;
- this.encAlgorithm = encAlgorithm;
- this.s2kUsage = s2kUsage;
- this.s2k = s2k;
- this.iv = iv;
- this.secKeyData = secKeyData;
- }
-
- public int getEncAlgorithm()
- {
- return encAlgorithm;
- }
-
- public int getS2KUsage()
- {
- return s2kUsage;
- }
-
- public byte[] getIV()
- {
- return iv;
- }
-
- public S2K getS2K()
- {
- return s2k;
- }
-
- public PublicKeyPacket getPublicKeyPacket()
- {
- return pubKeyPacket;
- }
-
- public byte[] getSecretKeyData()
- {
- return secKeyData;
- }
-
- public byte[] getEncodedContents()
- throws IOException
- {
- ByteArrayOutputStream bOut = new ByteArrayOutputStream();
- BCPGOutputStream pOut = new BCPGOutputStream(bOut);
-
- pOut.write(pubKeyPacket.getEncodedContents());
-
- pOut.write(s2kUsage);
-
- if (s2kUsage == USAGE_CHECKSUM || s2kUsage == USAGE_SHA1)
- {
- pOut.write(encAlgorithm);
- pOut.writeObject(s2k);
- }
-
- if (iv != null)
- {
- pOut.write(iv);
- }
-
- if (secKeyData != null && secKeyData.length > 0)
- {
- pOut.write(secKeyData);
- }
-
- return bOut.toByteArray();
- }
-
- public void encode(
- BCPGOutputStream out)
- throws IOException
- {
- out.writePacket(SECRET_KEY, getEncodedContents(), true);
- }
-}
+package org.bouncycastle.bcpg; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * basic packet for a PGP secret key + */ +public class SecretKeyPacket + extends ContainedPacket implements PublicKeyAlgorithmTags +{ + public static final int USAGE_NONE = 0x00; + public static final int USAGE_CHECKSUM = 0xff; + public static final int USAGE_SHA1 = 0xfe; + + private PublicKeyPacket pubKeyPacket; + private byte[] secKeyData; + private int s2kUsage; + private int encAlgorithm; + private S2K s2k; + private byte[] iv; + + /** + * + * @param in + * @throws IOException + */ + SecretKeyPacket( + BCPGInputStream in) + throws IOException + { + if (this instanceof SecretSubkeyPacket) + { + pubKeyPacket = new PublicSubkeyPacket(in); + } + else + { + pubKeyPacket = new PublicKeyPacket(in); + } + + s2kUsage = in.read(); + + if (s2kUsage == USAGE_CHECKSUM || s2kUsage == USAGE_SHA1) + { + encAlgorithm = in.read(); + s2k = new S2K(in); + } + else + { + encAlgorithm = s2kUsage; + } + + if (!(s2k != null && s2k.getType() == S2K.GNU_DUMMY_S2K && s2k.getProtectionMode() == 0x01)) + { + if (s2kUsage != 0) + { + if (encAlgorithm < 7) + { + iv = new byte[8]; + } + else + { + iv = new byte[16]; + } + in.readFully(iv, 0, iv.length); + } + } + + this.secKeyData = in.readAll(); + } + + /** + * + * @param pubKeyPacket + * @param encAlgorithm + * @param s2k + * @param iv + * @param secKeyData + */ + public SecretKeyPacket( + PublicKeyPacket pubKeyPacket, + int encAlgorithm, + S2K s2k, + byte[] iv, + byte[] secKeyData) + { + this.pubKeyPacket = pubKeyPacket; + this.encAlgorithm = encAlgorithm; + + if (encAlgorithm != SymmetricKeyAlgorithmTags.NULL) + { + this.s2kUsage = USAGE_CHECKSUM; + } + else + { + this.s2kUsage = USAGE_NONE; + } + + this.s2k = s2k; + this.iv = iv; + this.secKeyData = secKeyData; + } + + public SecretKeyPacket( + PublicKeyPacket pubKeyPacket, + int encAlgorithm, + int s2kUsage, + S2K s2k, + byte[] iv, + byte[] secKeyData) + { + this.pubKeyPacket = pubKeyPacket; + this.encAlgorithm = encAlgorithm; + this.s2kUsage = s2kUsage; + this.s2k = s2k; + this.iv = iv; + this.secKeyData = secKeyData; + } + + public int getEncAlgorithm() + { + return encAlgorithm; + } + + public int getS2KUsage() + { + return s2kUsage; + } + + public byte[] getIV() + { + return iv; + } + + public S2K getS2K() + { + return s2k; + } + + public PublicKeyPacket getPublicKeyPacket() + { + return pubKeyPacket; + } + + public byte[] getSecretKeyData() + { + return secKeyData; + } + + public byte[] getEncodedContents() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pOut = new BCPGOutputStream(bOut); + + pOut.write(pubKeyPacket.getEncodedContents()); + + pOut.write(s2kUsage); + + if (s2kUsage == USAGE_CHECKSUM || s2kUsage == USAGE_SHA1) + { + pOut.write(encAlgorithm); + pOut.writeObject(s2k); + } + + if (iv != null) + { + pOut.write(iv); + } + + if (secKeyData != null && secKeyData.length > 0) + { + pOut.write(secKeyData); + } + + return bOut.toByteArray(); + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + out.writePacket(SECRET_KEY, getEncodedContents(), true); + } +} diff --git a/src/main/java/org/bouncycastle/bcpg/SymmetricKeyEncSessionPacket.java b/src/main/java/org/bouncycastle/bcpg/SymmetricKeyEncSessionPacket.java index 3e4218fe..37769fef 100644 --- a/src/main/java/org/bouncycastle/bcpg/SymmetricKeyEncSessionPacket.java +++ b/src/main/java/org/bouncycastle/bcpg/SymmetricKeyEncSessionPacket.java @@ -1,90 +1,90 @@ -package org.bouncycastle.bcpg;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-/**
- * Basic type for a symmetric encrypted session key packet
- */
-public class SymmetricKeyEncSessionPacket
- extends ContainedPacket
-{
- private int version;
- private int encAlgorithm;
- private S2K s2k;
- private byte[] secKeyData;
-
- public SymmetricKeyEncSessionPacket(
- BCPGInputStream in)
- throws IOException
- {
- version = in.read();
- encAlgorithm = in.read();
-
- s2k = new S2K(in);
-
- this.secKeyData = in.readAll();
- }
-
- public SymmetricKeyEncSessionPacket(
- int encAlgorithm,
- S2K s2k,
- byte[] secKeyData)
- {
- this.version = 4;
- this.encAlgorithm = encAlgorithm;
- this.s2k = s2k;
- this.secKeyData = secKeyData;
- }
-
- /**
- * @return int
- */
- public int getEncAlgorithm()
- {
- return encAlgorithm;
- }
-
- /**
- * @return S2K
- */
- public S2K getS2K()
- {
- return s2k;
- }
-
- /**
- * @return byte[]
- */
- public byte[] getSecKeyData()
- {
- return secKeyData;
- }
-
- /**
- * @return int
- */
- public int getVersion()
- {
- return version;
- }
-
- public void encode(
- BCPGOutputStream out)
- throws IOException
- {
- ByteArrayOutputStream bOut = new ByteArrayOutputStream();
- BCPGOutputStream pOut = new BCPGOutputStream(bOut);
-
- pOut.write(version);
- pOut.write(encAlgorithm);
- pOut.writeObject(s2k);
-
- if (secKeyData != null && secKeyData.length > 0)
- {
- pOut.write(secKeyData);
- }
-
- out.writePacket(SYMMETRIC_KEY_ENC_SESSION, bOut.toByteArray(), true);
- }
-}
+package org.bouncycastle.bcpg; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * Basic type for a symmetric encrypted session key packet + */ +public class SymmetricKeyEncSessionPacket + extends ContainedPacket +{ + private int version; + private int encAlgorithm; + private S2K s2k; + private byte[] secKeyData; + + public SymmetricKeyEncSessionPacket( + BCPGInputStream in) + throws IOException + { + version = in.read(); + encAlgorithm = in.read(); + + s2k = new S2K(in); + + this.secKeyData = in.readAll(); + } + + public SymmetricKeyEncSessionPacket( + int encAlgorithm, + S2K s2k, + byte[] secKeyData) + { + this.version = 4; + this.encAlgorithm = encAlgorithm; + this.s2k = s2k; + this.secKeyData = secKeyData; + } + + /** + * @return int + */ + public int getEncAlgorithm() + { + return encAlgorithm; + } + + /** + * @return S2K + */ + public S2K getS2K() + { + return s2k; + } + + /** + * @return byte[] + */ + public byte[] getSecKeyData() + { + return secKeyData; + } + + /** + * @return int + */ + public int getVersion() + { + return version; + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pOut = new BCPGOutputStream(bOut); + + pOut.write(version); + pOut.write(encAlgorithm); + pOut.writeObject(s2k); + + if (secKeyData != null && secKeyData.length > 0) + { + pOut.write(secKeyData); + } + + out.writePacket(SYMMETRIC_KEY_ENC_SESSION, bOut.toByteArray(), true); + } +} diff --git a/src/main/java/org/bouncycastle/bcpg/UserIDPacket.java b/src/main/java/org/bouncycastle/bcpg/UserIDPacket.java index dff7e5ab..15ae71b1 100644 --- a/src/main/java/org/bouncycastle/bcpg/UserIDPacket.java +++ b/src/main/java/org/bouncycastle/bcpg/UserIDPacket.java @@ -1,39 +1,39 @@ -package org.bouncycastle.bcpg;
-
-import java.io.IOException;
-
-import org.bouncycastle.util.Strings;
-
-/**
- * Basic type for a user ID packet.
- */
-public class UserIDPacket
- extends ContainedPacket
-{
- private byte[] idData;
-
- public UserIDPacket(
- BCPGInputStream in)
- throws IOException
- {
- this.idData = in.readAll();
- }
-
- public UserIDPacket(
- String id)
- {
- this.idData = Strings.toUTF8ByteArray(id);
- }
-
- public String getID()
- {
- return Strings.fromUTF8ByteArray(idData);
- }
-
- public void encode(
- BCPGOutputStream out)
- throws IOException
- {
- out.writePacket(USER_ID, idData, true);
- }
-}
+package org.bouncycastle.bcpg; + +import java.io.IOException; + +import org.bouncycastle.util.Strings; + +/** + * Basic type for a user ID packet. + */ +public class UserIDPacket + extends ContainedPacket +{ + private byte[] idData; + + public UserIDPacket( + BCPGInputStream in) + throws IOException + { + this.idData = in.readAll(); + } + + public UserIDPacket( + String id) + { + this.idData = Strings.toUTF8ByteArray(id); + } + + public String getID() + { + return Strings.fromUTF8ByteArray(idData); + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + out.writePacket(USER_ID, idData, true); + } +} diff --git a/src/main/java/org/bouncycastle/cert/CertRuntimeException.java b/src/main/java/org/bouncycastle/cert/CertRuntimeException.java new file mode 100644 index 00000000..5384148a --- /dev/null +++ b/src/main/java/org/bouncycastle/cert/CertRuntimeException.java @@ -0,0 +1,19 @@ +package org.bouncycastle.cert; + +public class CertRuntimeException + extends RuntimeException +{ + private Throwable cause; + + public CertRuntimeException(String msg, Throwable cause) + { + super(msg); + + this.cause = cause; + } + + public Throwable getCause() + { + return cause; + } +}
\ No newline at end of file diff --git a/src/main/java/org/bouncycastle/cert/X509ExtensionUtils.java b/src/main/java/org/bouncycastle/cert/X509ExtensionUtils.java index 43c5948d..9afaf040 100644 --- a/src/main/java/org/bouncycastle/cert/X509ExtensionUtils.java +++ b/src/main/java/org/bouncycastle/cert/X509ExtensionUtils.java @@ -118,7 +118,7 @@ public class X509ExtensionUtils } catch (IOException e) { // it's hard to imagine this happening, but yes it does! - throw new IllegalStateException("unable to calculate identifier: " + e.getMessage(), e); + throw new CertRuntimeException("unable to calculate identifier: " + e.getMessage(), e); } return calculator.getDigest(); diff --git a/src/main/java/org/bouncycastle/cert/ocsp/OCSPReq.java b/src/main/java/org/bouncycastle/cert/ocsp/OCSPReq.java index 8a376952..2706c401 100644 --- a/src/main/java/org/bouncycastle/cert/ocsp/OCSPReq.java +++ b/src/main/java/org/bouncycastle/cert/ocsp/OCSPReq.java @@ -81,6 +81,10 @@ public class OCSPReq try { this.req = OCSPRequest.getInstance(aIn.readObject()); + if (req == null) + { + throw new CertIOException("malformed request: no request data found"); + } this.extensions = req.getTbsRequest().getRequestExtensions(); } catch (IllegalArgumentException e) diff --git a/src/main/java/org/bouncycastle/cert/ocsp/OCSPResp.java b/src/main/java/org/bouncycastle/cert/ocsp/OCSPResp.java index 43c240fc..e7e07705 100644 --- a/src/main/java/org/bouncycastle/cert/ocsp/OCSPResp.java +++ b/src/main/java/org/bouncycastle/cert/ocsp/OCSPResp.java @@ -65,6 +65,11 @@ public class OCSPResp { throw new CertIOException("malformed response: " + e.getMessage(), e); } + + if (resp == null) + { + throw new CertIOException("malformed response: no response data found"); + } } public int getStatus() diff --git a/src/main/java/org/bouncycastle/cms/RecipientInformationStore.java b/src/main/java/org/bouncycastle/cms/RecipientInformationStore.java index 7b8f57a8..af1ffe06 100644 --- a/src/main/java/org/bouncycastle/cms/RecipientInformationStore.java +++ b/src/main/java/org/bouncycastle/cms/RecipientInformationStore.java @@ -1,121 +1,115 @@ -package org.bouncycastle.cms; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -public class RecipientInformationStore -{ - private final List all; //ArrayList[RecipientInformation] - private final Map table = new HashMap(); // HashMap[RecipientID, ArrayList[RecipientInformation]] - - public RecipientInformationStore( - Collection recipientInfos) - { - Iterator it = recipientInfos.iterator(); - - while (it.hasNext()) - { - RecipientInformation recipientInformation = (RecipientInformation)it.next(); - RecipientId rid = recipientInformation.getRID(); - - List list = (ArrayList)table.get(rid); - if (list == null) - { - list = new ArrayList(1); - table.put(rid, list); - } - - list.add(recipientInformation); - } - - this.all = new ArrayList(recipientInfos); - } - - /** - * Return the first RecipientInformation object that matches the - * passed in selector. Null if there are no matches. - * - * @param selector to identify a recipient - * @return a single RecipientInformation object. Null if none matches. - */ - public RecipientInformation get( - RecipientId selector) - { - Collection list = getRecipients(selector); - - return list.size() == 0 ? null : (RecipientInformation)list.iterator().next(); - } - - /** - * Return the number of recipients in the collection. - * - * @return number of recipients identified. - */ - public int size() - { - return all.size(); - } - - /** - * Return all recipients in the collection - * - * @return a collection of recipients. - */ - public Collection getRecipients() - { - return new ArrayList(all); - } - - /** - * Return possible empty collection with recipients matching the passed in RecipientId - * - * @param selector a recipient id to select against. - * @return a collection of RecipientInformation objects. - */ - public Collection getRecipients( - RecipientId selector) - { - if (selector instanceof KeyTransRecipientId) - { - KeyTransRecipientId keyTrans = (KeyTransRecipientId)selector; - byte[] subjectKeyId = keyTrans.getSubjectKeyIdentifier(); - - if (keyTrans.getIssuer() != null && subjectKeyId != null) - { - List results = new ArrayList(); - - Collection match1 = getRecipients(new KeyTransRecipientId(keyTrans.getIssuer(), keyTrans.getSerialNumber())); - - if (match1 != null) - { - results.addAll(match1); - } - - Collection match2 = getRecipients(new KeyTransRecipientId(subjectKeyId)); - - if (match2 != null) - { - results.addAll(match2); - } - - return results; - } - else - { - List list = (ArrayList)table.get(selector); - - return list == null ? new ArrayList() : new ArrayList(list); - } - } - else - { - List list = (ArrayList)table.get(selector); - - return list == null ? new ArrayList() : new ArrayList(list); - } - } -} +package org.bouncycastle.cms;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.bouncycastle.asn1.x500.X500Name;
+
+public class RecipientInformationStore
+{
+ private final List all; //ArrayList[RecipientInformation]
+ private final Map table = new HashMap(); // HashMap[RecipientID, ArrayList[RecipientInformation]]
+
+ public RecipientInformationStore(
+ Collection recipientInfos)
+ {
+ Iterator it = recipientInfos.iterator();
+
+ while (it.hasNext())
+ {
+ RecipientInformation recipientInformation = (RecipientInformation)it.next();
+ RecipientId rid = recipientInformation.getRID();
+
+ List list = (ArrayList)table.get(rid);
+ if (list == null)
+ {
+ list = new ArrayList(1);
+ table.put(rid, list);
+ }
+
+ list.add(recipientInformation);
+ }
+
+ this.all = new ArrayList(recipientInfos);
+ }
+
+ /**
+ * Return the first RecipientInformation object that matches the
+ * passed in selector. Null if there are no matches.
+ *
+ * @param selector to identify a recipient
+ * @return a single RecipientInformation object. Null if none matches.
+ */
+ public RecipientInformation get(
+ RecipientId selector)
+ {
+ Collection list = getRecipients(selector);
+
+ return list.size() == 0 ? null : (RecipientInformation)list.iterator().next();
+ }
+
+ /**
+ * Return the number of recipients in the collection.
+ *
+ * @return number of recipients identified.
+ */
+ public int size()
+ {
+ return all.size();
+ }
+
+ /**
+ * Return all recipients in the collection
+ *
+ * @return a collection of recipients.
+ */
+ public Collection getRecipients()
+ {
+ return new ArrayList(all);
+ }
+
+ /**
+ * Return possible empty collection with recipients matching the passed in RecipientId
+ *
+ * @param selector a recipient id to select against.
+ * @return a collection of RecipientInformation objects.
+ */
+ public Collection getRecipients(
+ RecipientId selector)
+ {
+ if (selector instanceof KeyTransRecipientId)
+ {
+ KeyTransRecipientId keyTrans = (KeyTransRecipientId)selector;
+
+ X500Name issuer = keyTrans.getIssuer();
+ byte[] subjectKeyId = keyTrans.getSubjectKeyIdentifier();
+
+ if (issuer != null && subjectKeyId != null)
+ {
+ List results = new ArrayList();
+
+ Collection match1 = getRecipients(new KeyTransRecipientId(issuer, keyTrans.getSerialNumber()));
+ if (match1 != null)
+ {
+ results.addAll(match1);
+ }
+
+ Collection match2 = getRecipients(new KeyTransRecipientId(subjectKeyId));
+ if (match2 != null)
+ {
+ results.addAll(match2);
+ }
+
+ return results;
+ }
+ }
+
+ List list = (ArrayList)table.get(selector);
+
+ return list == null ? new ArrayList() : new ArrayList(list);
+ }
+}
diff --git a/src/main/java/org/bouncycastle/crypto/KeyEncapsulation.java b/src/main/java/org/bouncycastle/crypto/KeyEncapsulation.java new file mode 100755 index 00000000..16744573 --- /dev/null +++ b/src/main/java/org/bouncycastle/crypto/KeyEncapsulation.java @@ -0,0 +1,22 @@ +package org.bouncycastle.crypto; + +/** + * The basic interface for key encapsulation mechanisms. + */ +public interface KeyEncapsulation +{ + /** + * Initialise the key encapsulation mechanism. + */ + public void init(CipherParameters param); + + /** + * Encapsulate a randomly generated session key. + */ + public CipherParameters encrypt(byte[] out, int outOff, int keyLen); + + /** + * Decapsulate an encapsulated session key. + */ + public CipherParameters decrypt(byte[] in, int inOff, int inLen, int keyLen); +} diff --git a/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKEKGenerator.java b/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKEKGenerator.java index 356f8b6e..947bc5c4 100644 --- a/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKEKGenerator.java +++ b/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKEKGenerator.java @@ -1,131 +1,131 @@ -package org.bouncycastle.crypto.agreement.kdf;
-
-import java.io.IOException;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Encoding;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERTaggedObject;
-import org.bouncycastle.crypto.DataLengthException;
-import org.bouncycastle.crypto.DerivationFunction;
-import org.bouncycastle.crypto.DerivationParameters;
-import org.bouncycastle.crypto.Digest;
-import org.bouncycastle.crypto.util.Pack;
-
-/**
- * RFC 2631 Diffie-hellman KEK derivation function.
- */
-public class DHKEKGenerator
- implements DerivationFunction
-{
- private final Digest digest;
-
- private DERObjectIdentifier algorithm;
- private int keySize;
- private byte[] z;
- private byte[] partyAInfo;
-
- public DHKEKGenerator(
- Digest digest)
- {
- this.digest = digest;
- }
-
- public void init(DerivationParameters param)
- {
- DHKDFParameters params = (DHKDFParameters)param;
-
- this.algorithm = params.getAlgorithm();
- this.keySize = params.getKeySize();
- this.z = params.getZ();
- this.partyAInfo = params.getExtraInfo();
- }
-
- public Digest getDigest()
- {
- return digest;
- }
-
- public int generateBytes(byte[] out, int outOff, int len)
- throws DataLengthException, IllegalArgumentException
- {
- if ((out.length - len) < outOff)
- {
- throw new DataLengthException("output buffer too small");
- }
-
- long oBytes = len;
- int outLen = digest.getDigestSize();
-
- //
- // this is at odds with the standard implementation, the
- // maximum value should be hBits * (2^32 - 1) where hBits
- // is the digest output size in bits. We can't have an
- // array with a long index at the moment...
- //
- if (oBytes > ((2L << 32) - 1))
- {
- throw new IllegalArgumentException("Output length too large");
- }
-
- int cThreshold = (int)((oBytes + outLen - 1) / outLen);
-
- byte[] dig = new byte[digest.getDigestSize()];
-
- int counter = 1;
-
- for (int i = 0; i < cThreshold; i++)
- {
- digest.update(z, 0, z.length);
-
- // OtherInfo
- ASN1EncodableVector v1 = new ASN1EncodableVector();
- // KeySpecificInfo
- ASN1EncodableVector v2 = new ASN1EncodableVector();
-
- v2.add(algorithm);
- v2.add(new DEROctetString(Pack.intToBigEndian(counter)));
-
- v1.add(new DERSequence(v2));
-
- if (partyAInfo != null)
- {
- v1.add(new DERTaggedObject(true, 0, new DEROctetString(partyAInfo)));
- }
-
- v1.add(new DERTaggedObject(true, 2, new DEROctetString(Pack.intToBigEndian(keySize))));
-
- try
- {
- byte[] other = new DERSequence(v1).getEncoded(ASN1Encoding.DER);
-
- digest.update(other, 0, other.length);
- }
- catch (IOException e)
- {
- throw new IllegalArgumentException("unable to encode parameter info: " + e.getMessage());
- }
-
- digest.doFinal(dig, 0);
-
- if (len > outLen)
- {
- System.arraycopy(dig, 0, out, outOff, outLen);
- outOff += outLen;
- len -= outLen;
- }
- else
- {
- System.arraycopy(dig, 0, out, outOff, len);
- }
-
- counter++;
- }
-
- digest.reset();
-
- return (int)oBytes;
- }
-}
+package org.bouncycastle.crypto.agreement.kdf; + +import java.io.IOException; + +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.DERObjectIdentifier; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.DERTaggedObject; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.DerivationFunction; +import org.bouncycastle.crypto.DerivationParameters; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.util.Pack; + +/** + * RFC 2631 Diffie-hellman KEK derivation function. + */ +public class DHKEKGenerator + implements DerivationFunction +{ + private final Digest digest; + + private DERObjectIdentifier algorithm; + private int keySize; + private byte[] z; + private byte[] partyAInfo; + + public DHKEKGenerator( + Digest digest) + { + this.digest = digest; + } + + public void init(DerivationParameters param) + { + DHKDFParameters params = (DHKDFParameters)param; + + this.algorithm = params.getAlgorithm(); + this.keySize = params.getKeySize(); + this.z = params.getZ(); + this.partyAInfo = params.getExtraInfo(); + } + + public Digest getDigest() + { + return digest; + } + + public int generateBytes(byte[] out, int outOff, int len) + throws DataLengthException, IllegalArgumentException + { + if ((out.length - len) < outOff) + { + throw new DataLengthException("output buffer too small"); + } + + long oBytes = len; + int outLen = digest.getDigestSize(); + + // + // this is at odds with the standard implementation, the + // maximum value should be hBits * (2^32 - 1) where hBits + // is the digest output size in bits. We can't have an + // array with a long index at the moment... + // + if (oBytes > ((2L << 32) - 1)) + { + throw new IllegalArgumentException("Output length too large"); + } + + int cThreshold = (int)((oBytes + outLen - 1) / outLen); + + byte[] dig = new byte[digest.getDigestSize()]; + + int counter = 1; + + for (int i = 0; i < cThreshold; i++) + { + digest.update(z, 0, z.length); + + // OtherInfo + ASN1EncodableVector v1 = new ASN1EncodableVector(); + // KeySpecificInfo + ASN1EncodableVector v2 = new ASN1EncodableVector(); + + v2.add(algorithm); + v2.add(new DEROctetString(Pack.intToBigEndian(counter))); + + v1.add(new DERSequence(v2)); + + if (partyAInfo != null) + { + v1.add(new DERTaggedObject(true, 0, new DEROctetString(partyAInfo))); + } + + v1.add(new DERTaggedObject(true, 2, new DEROctetString(Pack.intToBigEndian(keySize)))); + + try + { + byte[] other = new DERSequence(v1).getEncoded(ASN1Encoding.DER); + + digest.update(other, 0, other.length); + } + catch (IOException e) + { + throw new IllegalArgumentException("unable to encode parameter info: " + e.getMessage()); + } + + digest.doFinal(dig, 0); + + if (len > outLen) + { + System.arraycopy(dig, 0, out, outOff, outLen); + outOff += outLen; + len -= outLen; + } + else + { + System.arraycopy(dig, 0, out, outOff, len); + } + + counter++; + } + + digest.reset(); + + return (int)oBytes; + } +} diff --git a/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java b/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java index 03ca321f..68039533 100644 --- a/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java +++ b/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java @@ -1,74 +1,74 @@ -package org.bouncycastle.crypto.agreement.kdf;
-
-import java.io.IOException;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Encoding;
-import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.DERNull;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERTaggedObject;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.crypto.DataLengthException;
-import org.bouncycastle.crypto.DerivationFunction;
-import org.bouncycastle.crypto.DerivationParameters;
-import org.bouncycastle.crypto.Digest;
-import org.bouncycastle.crypto.generators.KDF2BytesGenerator;
-import org.bouncycastle.crypto.params.KDFParameters;
-import org.bouncycastle.crypto.util.Pack;
-
-/**
- * X9.63 based key derivation function for ECDH CMS.
- */
-public class ECDHKEKGenerator
- implements DerivationFunction
-{
- private DerivationFunction kdf;
-
- private ASN1ObjectIdentifier algorithm;
- private int keySize;
- private byte[] z;
-
- public ECDHKEKGenerator(
- Digest digest)
- {
- this.kdf = new KDF2BytesGenerator(digest);
- }
-
- public void init(DerivationParameters param)
- {
- DHKDFParameters params = (DHKDFParameters)param;
-
- this.algorithm = params.getAlgorithm();
- this.keySize = params.getKeySize();
- this.z = params.getZ();
- }
-
- public Digest getDigest()
- {
- return kdf.getDigest();
- }
-
- public int generateBytes(byte[] out, int outOff, int len)
- throws DataLengthException, IllegalArgumentException
- {
- // TODO Create an ASN.1 class for this (RFC3278)
- // ECC-CMS-SharedInfo
- ASN1EncodableVector v = new ASN1EncodableVector();
-
- v.add(new AlgorithmIdentifier(algorithm, DERNull.INSTANCE));
- v.add(new DERTaggedObject(true, 2, new DEROctetString(Pack.intToBigEndian(keySize))));
-
- try
- {
- kdf.init(new KDFParameters(z, new DERSequence(v).getEncoded(ASN1Encoding.DER)));
- }
- catch (IOException e)
- {
- throw new IllegalArgumentException("unable to initialise kdf: " + e.getMessage());
- }
-
- return kdf.generateBytes(out, outOff, len);
- }
-}
+package org.bouncycastle.crypto.agreement.kdf; + +import java.io.IOException; + +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.DERNull; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.DERTaggedObject; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.DerivationFunction; +import org.bouncycastle.crypto.DerivationParameters; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.generators.KDF2BytesGenerator; +import org.bouncycastle.crypto.params.KDFParameters; +import org.bouncycastle.crypto.util.Pack; + +/** + * X9.63 based key derivation function for ECDH CMS. + */ +public class ECDHKEKGenerator + implements DerivationFunction +{ + private DerivationFunction kdf; + + private ASN1ObjectIdentifier algorithm; + private int keySize; + private byte[] z; + + public ECDHKEKGenerator( + Digest digest) + { + this.kdf = new KDF2BytesGenerator(digest); + } + + public void init(DerivationParameters param) + { + DHKDFParameters params = (DHKDFParameters)param; + + this.algorithm = params.getAlgorithm(); + this.keySize = params.getKeySize(); + this.z = params.getZ(); + } + + public Digest getDigest() + { + return kdf.getDigest(); + } + + public int generateBytes(byte[] out, int outOff, int len) + throws DataLengthException, IllegalArgumentException + { + // TODO Create an ASN.1 class for this (RFC3278) + // ECC-CMS-SharedInfo + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new AlgorithmIdentifier(algorithm, DERNull.INSTANCE)); + v.add(new DERTaggedObject(true, 2, new DEROctetString(Pack.intToBigEndian(keySize)))); + + try + { + kdf.init(new KDFParameters(z, new DERSequence(v).getEncoded(ASN1Encoding.DER))); + } + catch (IOException e) + { + throw new IllegalArgumentException("unable to initialise kdf: " + e.getMessage()); + } + + return kdf.generateBytes(out, outOff, len); + } +} diff --git a/src/main/java/org/bouncycastle/crypto/digests/GOST3411Digest.java b/src/main/java/org/bouncycastle/crypto/digests/GOST3411Digest.java index 956fada0..38a52aab 100644 --- a/src/main/java/org/bouncycastle/crypto/digests/GOST3411Digest.java +++ b/src/main/java/org/bouncycastle/crypto/digests/GOST3411Digest.java @@ -7,12 +7,13 @@ import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithSBox; import org.bouncycastle.crypto.util.Pack; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Memoable; /** * implementation of GOST R 34.11-94 */ public class GOST3411Digest - implements ExtendedDigest + implements ExtendedDigest, Memoable { private static final int DIGEST_LENGTH = 32; @@ -56,22 +57,7 @@ public class GOST3411Digest */ public GOST3411Digest(GOST3411Digest t) { - this.sBox = t.sBox; - cipher.init(true, new ParametersWithSBox(null, sBox)); - - reset(); - - System.arraycopy(t.H, 0, this.H, 0, t.H.length); - System.arraycopy(t.L, 0, this.L, 0, t.L.length); - System.arraycopy(t.M, 0, this.M, 0, t.M.length); - System.arraycopy(t.Sum, 0, this.Sum, 0, t.Sum.length); - System.arraycopy(t.C[1], 0, this.C[1], 0, t.C[1].length); - System.arraycopy(t.C[2], 0, this.C[2], 0, t.C[2].length); - System.arraycopy(t.C[3], 0, this.C[3], 0, t.C[3].length); - System.arraycopy(t.xBuf, 0, this.xBuf, 0, t.xBuf.length); - - this.xBufOff = t.xBufOff; - this.byteCount = t.byteCount; + reset(t); } public String getAlgorithmName() @@ -344,6 +330,33 @@ public class GOST3411Digest { return 32; } + + public Memoable copy() + { + return new GOST3411Digest(this); + } + + public void reset(Memoable other) + { + GOST3411Digest t = (GOST3411Digest)other; + + this.sBox = t.sBox; + cipher.init(true, new ParametersWithSBox(null, sBox)); + + reset(); + + System.arraycopy(t.H, 0, this.H, 0, t.H.length); + System.arraycopy(t.L, 0, this.L, 0, t.L.length); + System.arraycopy(t.M, 0, this.M, 0, t.M.length); + System.arraycopy(t.Sum, 0, this.Sum, 0, t.Sum.length); + System.arraycopy(t.C[1], 0, this.C[1], 0, t.C[1].length); + System.arraycopy(t.C[2], 0, this.C[2], 0, t.C[2].length); + System.arraycopy(t.C[3], 0, this.C[3], 0, t.C[3].length); + System.arraycopy(t.xBuf, 0, this.xBuf, 0, t.xBuf.length); + + this.xBufOff = t.xBufOff; + this.byteCount = t.byteCount; + } } diff --git a/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java b/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java index f2c99674..15f3ebbd 100644 --- a/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java +++ b/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java @@ -1,13 +1,14 @@ package org.bouncycastle.crypto.digests; import org.bouncycastle.crypto.ExtendedDigest; +import org.bouncycastle.util.Memoable; /** * base implementation of MD4 family style digest as outlined in * "Handbook of Applied Cryptography", pages 344 - 347. */ public abstract class GeneralDigest - implements ExtendedDigest + implements ExtendedDigest, Memoable { private static final int BYTE_LENGTH = 64; private byte[] xBuf; @@ -32,6 +33,12 @@ public abstract class GeneralDigest protected GeneralDigest(GeneralDigest t) { xBuf = new byte[t.xBuf.length]; + + copyIn(t); + } + + protected void copyIn(GeneralDigest t) + { System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length); xBufOff = t.xBufOff; diff --git a/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java b/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java index 22d457bf..5c79e4ee 100644 --- a/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java +++ b/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java @@ -2,12 +2,13 @@ package org.bouncycastle.crypto.digests; import org.bouncycastle.crypto.ExtendedDigest; import org.bouncycastle.crypto.util.Pack; +import org.bouncycastle.util.Memoable; /** * Base class for SHA-384 and SHA-512. */ public abstract class LongDigest - implements ExtendedDigest + implements ExtendedDigest, Memoable { private static final int BYTE_LENGTH = 128; @@ -41,6 +42,12 @@ public abstract class LongDigest protected LongDigest(LongDigest t) { xBuf = new byte[t.xBuf.length]; + + copyIn(t); + } + + protected void copyIn(LongDigest t) + { System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length); xBufOff = t.xBufOff; diff --git a/src/main/java/org/bouncycastle/crypto/digests/MD2Digest.java b/src/main/java/org/bouncycastle/crypto/digests/MD2Digest.java index bd942a1c..f96b4a15 100644 --- a/src/main/java/org/bouncycastle/crypto/digests/MD2Digest.java +++ b/src/main/java/org/bouncycastle/crypto/digests/MD2Digest.java @@ -1,12 +1,14 @@ package org.bouncycastle.crypto.digests; import org.bouncycastle.crypto.*; +import org.bouncycastle.util.Memoable; + /** * implementation of MD2 * as outlined in RFC1319 by B.Kaliski from RSA Laboratories April 1992 */ public class MD2Digest - implements ExtendedDigest + implements ExtendedDigest, Memoable { private static final int DIGEST_LENGTH = 16; @@ -24,8 +26,14 @@ public class MD2Digest { reset(); } + public MD2Digest(MD2Digest t) { + copyIn(t); + } + + private void copyIn(MD2Digest t) + { System.arraycopy(t.X, 0, X, 0, t.X.length); xOff = t.xOff; System.arraycopy(t.M, 0, M, 0, t.M.length); @@ -33,6 +41,7 @@ public class MD2Digest System.arraycopy(t.C, 0, C, 0, t.C.length); COff = t.COff; } + /** * return the algorithm name * @@ -232,6 +241,18 @@ public class MD2Digest { return 16; } + + public Memoable copy() + { + return new MD2Digest(this); + } + + public void reset(Memoable other) + { + MD2Digest d = (MD2Digest)other; + + copyIn(d); + } } diff --git a/src/main/java/org/bouncycastle/crypto/digests/MD4Digest.java b/src/main/java/org/bouncycastle/crypto/digests/MD4Digest.java index 2a8084f9..68532bd2 100644 --- a/src/main/java/org/bouncycastle/crypto/digests/MD4Digest.java +++ b/src/main/java/org/bouncycastle/crypto/digests/MD4Digest.java @@ -1,6 +1,8 @@ package org.bouncycastle.crypto.digests; +import org.bouncycastle.util.Memoable; + /** * implementation of MD4 as RFC 1320 by R. Rivest, MIT Laboratory for * Computer Science and RSA Data Security, Inc. @@ -34,6 +36,13 @@ public class MD4Digest { super(t); + copyIn(t); + } + + private void copyIn(MD4Digest t) + { + super.copyIn(t); + H1 = t.H1; H2 = t.H2; H3 = t.H3; @@ -267,4 +276,16 @@ public class MD4Digest X[i] = 0; } } + + public Memoable copy() + { + return new MD4Digest(this); + } + + public void reset(Memoable other) + { + MD4Digest d = (MD4Digest)other; + + copyIn(d); + } } diff --git a/src/main/java/org/bouncycastle/crypto/digests/MD5Digest.java b/src/main/java/org/bouncycastle/crypto/digests/MD5Digest.java index 05ed27a6..ff9cedf0 100644 --- a/src/main/java/org/bouncycastle/crypto/digests/MD5Digest.java +++ b/src/main/java/org/bouncycastle/crypto/digests/MD5Digest.java @@ -1,6 +1,8 @@ package org.bouncycastle.crypto.digests; +import org.bouncycastle.util.Memoable; + /** * implementation of MD5 as outlined in "Handbook of Applied Cryptography", pages 346 - 347. */ @@ -30,6 +32,13 @@ public class MD5Digest { super(t); + copyIn(t); + } + + private void copyIn(MD5Digest t) + { + super.copyIn(t); + H1 = t.H1; H2 = t.H2; H3 = t.H3; @@ -299,4 +308,16 @@ public class MD5Digest X[i] = 0; } } + + public Memoable copy() + { + return new MD5Digest(this); + } + + public void reset(Memoable other) + { + MD5Digest d = (MD5Digest)other; + + copyIn(d); + } } diff --git a/src/main/java/org/bouncycastle/crypto/digests/NonMemoableDigest.java b/src/main/java/org/bouncycastle/crypto/digests/NonMemoableDigest.java new file mode 100644 index 00000000..87a4d249 --- /dev/null +++ b/src/main/java/org/bouncycastle/crypto/digests/NonMemoableDigest.java @@ -0,0 +1,64 @@ +package org.bouncycastle.crypto.digests; + +import org.bouncycastle.crypto.ExtendedDigest; + +/** + * Wrapper removes exposure to the Memoable interface on an ExtendedDigest implementation. + */ +public class NonMemoableDigest + implements ExtendedDigest +{ + private ExtendedDigest baseDigest; + + /** + * Base constructor. + * + * @param baseDigest underlying digest to use. + * @exception IllegalArgumentException if baseDigest is null + */ + public NonMemoableDigest( + ExtendedDigest baseDigest) + { + if (baseDigest == null) + { + throw new IllegalArgumentException("baseDigest must not be null"); + } + + this.baseDigest = baseDigest; + } + + public String getAlgorithmName() + { + return baseDigest.getAlgorithmName(); + } + + public int getDigestSize() + { + return baseDigest.getDigestSize(); + } + + public void update(byte in) + { + baseDigest.update(in); + } + + public void update(byte[] in, int inOff, int len) + { + baseDigest.update(in, inOff, len); + } + + public int doFinal(byte[] out, int outOff) + { + return baseDigest.doFinal(out, outOff); + } + + public void reset() + { + baseDigest.reset(); + } + + public int getByteLength() + { + return baseDigest.getByteLength(); + } +} diff --git a/src/main/java/org/bouncycastle/crypto/digests/RIPEMD128Digest.java b/src/main/java/org/bouncycastle/crypto/digests/RIPEMD128Digest.java index 46fd8b39..ec7fa859 100644 --- a/src/main/java/org/bouncycastle/crypto/digests/RIPEMD128Digest.java +++ b/src/main/java/org/bouncycastle/crypto/digests/RIPEMD128Digest.java @@ -1,6 +1,8 @@ package org.bouncycastle.crypto.digests; +import org.bouncycastle.util.Memoable; + /** * implementation of RIPEMD128 */ @@ -30,6 +32,13 @@ public class RIPEMD128Digest { super(t); + copyIn(t); + } + + private void copyIn(RIPEMD128Digest t) + { + super.copyIn(t); + H0 = t.H0; H1 = t.H1; H2 = t.H2; @@ -458,4 +467,16 @@ public class RIPEMD128Digest X[i] = 0; } } + + public Memoable copy() + { + return new RIPEMD128Digest(this); + } + + public void reset(Memoable other) + { + RIPEMD128Digest d = (RIPEMD128Digest)other; + + copyIn(d); + } } diff --git a/src/main/java/org/bouncycastle/crypto/digests/RIPEMD160Digest.java b/src/main/java/org/bouncycastle/crypto/digests/RIPEMD160Digest.java index 2351357c..20c81e68 100644 --- a/src/main/java/org/bouncycastle/crypto/digests/RIPEMD160Digest.java +++ b/src/main/java/org/bouncycastle/crypto/digests/RIPEMD160Digest.java @@ -1,6 +1,8 @@ package org.bouncycastle.crypto.digests; +import org.bouncycastle.util.Memoable; + /** * implementation of RIPEMD see, * http://www.esat.kuleuven.ac.be/~bosselae/ripemd160.html @@ -31,6 +33,13 @@ public class RIPEMD160Digest { super(t); + copyIn(t); + } + + private void copyIn(RIPEMD160Digest t) + { + super.copyIn(t); + H0 = t.H0; H1 = t.H1; H2 = t.H2; @@ -419,4 +428,16 @@ public class RIPEMD160Digest X[i] = 0; } } + + public Memoable copy() + { + return new RIPEMD160Digest(this); + } + + public void reset(Memoable other) + { + RIPEMD160Digest d = (RIPEMD160Digest)other; + + copyIn(d); + } } diff --git a/src/main/java/org/bouncycastle/crypto/digests/RIPEMD256Digest.java b/src/main/java/org/bouncycastle/crypto/digests/RIPEMD256Digest.java index 3d92e4a3..86746b45 100644 --- a/src/main/java/org/bouncycastle/crypto/digests/RIPEMD256Digest.java +++ b/src/main/java/org/bouncycastle/crypto/digests/RIPEMD256Digest.java @@ -1,6 +1,8 @@ package org.bouncycastle.crypto.digests; +import org.bouncycastle.util.Memoable; + /** * implementation of RIPEMD256. * <p> @@ -32,6 +34,13 @@ public class RIPEMD256Digest { super(t); + copyIn(t); + } + + private void copyIn(RIPEMD256Digest t) + { + super.copyIn(t); + H0 = t.H0; H1 = t.H1; H2 = t.H2; @@ -473,4 +482,16 @@ public class RIPEMD256Digest X[i] = 0; } } + + public Memoable copy() + { + return new RIPEMD256Digest(this); + } + + public void reset(Memoable other) + { + RIPEMD256Digest d = (RIPEMD256Digest)other; + + copyIn(d); + } } diff --git a/src/main/java/org/bouncycastle/crypto/digests/RIPEMD320Digest.java b/src/main/java/org/bouncycastle/crypto/digests/RIPEMD320Digest.java index e2cda588..32775e77 100644 --- a/src/main/java/org/bouncycastle/crypto/digests/RIPEMD320Digest.java +++ b/src/main/java/org/bouncycastle/crypto/digests/RIPEMD320Digest.java @@ -1,6 +1,8 @@ package org.bouncycastle.crypto.digests; +import org.bouncycastle.util.Memoable; + /** * implementation of RIPEMD 320. * <p> @@ -33,6 +35,12 @@ public class RIPEMD320Digest { super(t); + doCopy(t); + } + + private void doCopy(RIPEMD320Digest t) + { + super.copyIn(t); H0 = t.H0; H1 = t.H1; H2 = t.H2; @@ -458,4 +466,16 @@ public class RIPEMD320Digest X[i] = 0; } } + + public Memoable copy() + { + return new RIPEMD320Digest(this); + } + + public void reset(Memoable other) + { + RIPEMD320Digest d = (RIPEMD320Digest)other; + + doCopy(d); + } } diff --git a/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java b/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java index 1b47fd3c..21b1024e 100644 --- a/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java +++ b/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java @@ -1,6 +1,7 @@ package org.bouncycastle.crypto.digests; import org.bouncycastle.crypto.util.Pack; +import org.bouncycastle.util.Memoable; /** * implementation of SHA-1 as outlined in "Handbook of Applied Cryptography", pages 346 - 349. @@ -34,6 +35,11 @@ public class SHA1Digest { super(t); + copyIn(t); + } + + private void copyIn(SHA1Digest t) + { H1 = t.H1; H2 = t.H2; H3 = t.H3; @@ -283,6 +289,19 @@ public class SHA1Digest X[i] = 0; } } + + public Memoable copy() + { + return new SHA1Digest(this); + } + + public void reset(Memoable other) + { + SHA1Digest d = (SHA1Digest)other; + + super.copyIn(d); + copyIn(d); + } } diff --git a/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java b/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java index d144c54b..d430321b 100644 --- a/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java +++ b/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java @@ -1,8 +1,8 @@ package org.bouncycastle.crypto.digests; -import org.bouncycastle.crypto.digests.GeneralDigest; import org.bouncycastle.crypto.util.Pack; +import org.bouncycastle.util.Memoable; /** @@ -42,6 +42,13 @@ public class SHA224Digest { super(t); + doCopy(t); + } + + private void doCopy(SHA224Digest t) + { + super.copyIn(t); + H1 = t.H1; H2 = t.H2; H3 = t.H3; @@ -288,5 +295,17 @@ public class SHA224Digest 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; + + public Memoable copy() + { + return new SHA224Digest(this); + } + + public void reset(Memoable other) + { + SHA224Digest d = (SHA224Digest)other; + + doCopy(d); + } } diff --git a/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java b/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java index abd9c1bb..a2ceda3d 100644 --- a/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java +++ b/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java @@ -1,8 +1,8 @@ package org.bouncycastle.crypto.digests; -import org.bouncycastle.crypto.digests.GeneralDigest; import org.bouncycastle.crypto.util.Pack; +import org.bouncycastle.util.Memoable; /** @@ -42,6 +42,13 @@ public class SHA256Digest { super(t); + copyIn(t); + } + + private void copyIn(SHA256Digest t) + { + super.copyIn(t); + H1 = t.H1; H2 = t.H2; H3 = t.H3; @@ -291,5 +298,17 @@ public class SHA256Digest 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; + + public Memoable copy() + { + return new SHA256Digest(this); + } + + public void reset(Memoable other) + { + SHA256Digest d = (SHA256Digest)other; + + copyIn(d); + } } diff --git a/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java b/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java index cdd979a9..75d195d4 100644 --- a/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java +++ b/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java @@ -1,6 +1,7 @@ package org.bouncycastle.crypto.digests; import org.bouncycastle.crypto.util.Pack; +import org.bouncycastle.util.Memoable; /** @@ -17,7 +18,6 @@ import org.bouncycastle.crypto.util.Pack; public class SHA384Digest extends LongDigest { - private static final int DIGEST_LENGTH = 48; /** @@ -84,4 +84,16 @@ public class SHA384Digest H7 = 0xdb0c2e0d64f98fa7l; H8 = 0x47b5481dbefa4fa4l; } + + public Memoable copy() + { + return new SHA384Digest(this); + } + + public void reset(Memoable other) + { + SHA384Digest d = (SHA384Digest)other; + + super.copyIn(d); + } } diff --git a/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java b/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java index 34a8e4e4..7db63ad2 100644 --- a/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java +++ b/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java @@ -1,6 +1,7 @@ package org.bouncycastle.crypto.digests; import org.bouncycastle.crypto.util.Pack; +import org.bouncycastle.util.Memoable; /** @@ -85,5 +86,17 @@ public class SHA512Digest H7 = 0x1f83d9abfb41bd6bL; H8 = 0x5be0cd19137e2179L; } + + public Memoable copy() + { + return new SHA512Digest(this); + } + + public void reset(Memoable other) + { + SHA512Digest d = (SHA512Digest)other; + + copyIn(d); + } } diff --git a/src/main/java/org/bouncycastle/crypto/digests/SHA512tDigest.java b/src/main/java/org/bouncycastle/crypto/digests/SHA512tDigest.java index 459ade16..46154618 100644 --- a/src/main/java/org/bouncycastle/crypto/digests/SHA512tDigest.java +++ b/src/main/java/org/bouncycastle/crypto/digests/SHA512tDigest.java @@ -1,5 +1,8 @@ package org.bouncycastle.crypto.digests; +import org.bouncycastle.util.Memoable; +import org.bouncycastle.util.MemoableResetException; + /** * FIPS 180-4 implementation of SHA-512/t */ @@ -47,14 +50,7 @@ public class SHA512tDigest this.digestLength = t.digestLength; - this.H1t = t.H1t; - this.H2t = t.H2t; - this.H3t = t.H3t; - this.H4t = t.H4t; - this.H5t = t.H5t; - this.H6t = t.H6t; - this.H7t = t.H7t; - this.H8t = t.H8t; + reset(t); } public String getAlgorithmName() @@ -180,4 +176,30 @@ public class SHA512tDigest bs[off + num] = (byte)(n >>> shift); } } + + public Memoable copy() + { + return new SHA512tDigest(this); + } + + public void reset(Memoable other) + { + SHA512tDigest t = (SHA512tDigest)other; + + if (this.digestLength != t.digestLength) + { + throw new MemoableResetException("digestLength inappropriate in other"); + } + + super.copyIn(t); + + this.H1t = t.H1t; + this.H2t = t.H2t; + this.H3t = t.H3t; + this.H4t = t.H4t; + this.H5t = t.H5t; + this.H6t = t.H6t; + this.H7t = t.H7t; + this.H8t = t.H8t; + } } diff --git a/src/main/java/org/bouncycastle/crypto/digests/TigerDigest.java b/src/main/java/org/bouncycastle/crypto/digests/TigerDigest.java index 0062ea92..2899e305 100644 --- a/src/main/java/org/bouncycastle/crypto/digests/TigerDigest.java +++ b/src/main/java/org/bouncycastle/crypto/digests/TigerDigest.java @@ -1,6 +1,7 @@ package org.bouncycastle.crypto.digests; import org.bouncycastle.crypto.ExtendedDigest; +import org.bouncycastle.util.Memoable; /** * implementation of Tiger based on: @@ -8,7 +9,7 @@ import org.bouncycastle.crypto.ExtendedDigest; * http://www.cs.technion.ac.il/~biham/Reports/Tiger</a> */ public class TigerDigest - implements ExtendedDigest + implements ExtendedDigest, Memoable { private static final int BYTE_LENGTH = 64; @@ -570,17 +571,7 @@ public class TigerDigest */ public TigerDigest(TigerDigest t) { - a = t.a; - b = t.b; - c = t.c; - - System.arraycopy(t.x, 0, x, 0, t.x.length); - xOff = t.xOff; - - System.arraycopy(t.buf, 0, buf, 0, t.buf.length); - bOff = t.bOff; - - byteCount = t.byteCount; + this.reset(t); } public String getAlgorithmName() @@ -863,4 +854,26 @@ public class TigerDigest { return BYTE_LENGTH; } + + public Memoable copy() + { + return new TigerDigest(this); + } + + public void reset(Memoable other) + { + TigerDigest t = (TigerDigest)other; + + a = t.a; + b = t.b; + c = t.c; + + System.arraycopy(t.x, 0, x, 0, t.x.length); + xOff = t.xOff; + + System.arraycopy(t.buf, 0, buf, 0, t.buf.length); + bOff = t.bOff; + + byteCount = t.byteCount; + } } diff --git a/src/main/java/org/bouncycastle/crypto/digests/WhirlpoolDigest.java b/src/main/java/org/bouncycastle/crypto/digests/WhirlpoolDigest.java index 6d350470..11e884cd 100644 --- a/src/main/java/org/bouncycastle/crypto/digests/WhirlpoolDigest.java +++ b/src/main/java/org/bouncycastle/crypto/digests/WhirlpoolDigest.java @@ -2,6 +2,7 @@ package org.bouncycastle.crypto.digests; import org.bouncycastle.crypto.ExtendedDigest; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Memoable; /** @@ -10,7 +11,7 @@ import org.bouncycastle.util.Arrays; * */ public final class WhirlpoolDigest - implements ExtendedDigest + implements ExtendedDigest, Memoable { private static final int BYTE_LENGTH = 64; @@ -136,19 +137,7 @@ public final class WhirlpoolDigest */ public WhirlpoolDigest(WhirlpoolDigest originalDigest) { - System.arraycopy(originalDigest._rc, 0, _rc, 0, _rc.length); - - System.arraycopy(originalDigest._buffer, 0, _buffer, 0, _buffer.length); - - this._bufferPos = originalDigest._bufferPos; - System.arraycopy(originalDigest._bitCount, 0, _bitCount, 0, _bitCount.length); - - // -- internal hash state -- - System.arraycopy(originalDigest._hash, 0, _hash, 0, _hash.length); - System.arraycopy(originalDigest._K, 0, _K, 0, _K.length); - System.arraycopy(originalDigest._L, 0, _L, 0, _L.length); - System.arraycopy(originalDigest._block, 0, _block, 0, _block.length); - System.arraycopy(originalDigest._state, 0, _state, 0, _state.length); + reset(originalDigest); } public String getAlgorithmName() @@ -393,4 +382,28 @@ public final class WhirlpoolDigest { return BYTE_LENGTH; } + + public Memoable copy() + { + return new WhirlpoolDigest(this); + } + + public void reset(Memoable other) + { + WhirlpoolDigest originalDigest = (WhirlpoolDigest)other; + + System.arraycopy(originalDigest._rc, 0, _rc, 0, _rc.length); + + System.arraycopy(originalDigest._buffer, 0, _buffer, 0, _buffer.length); + + this._bufferPos = originalDigest._bufferPos; + System.arraycopy(originalDigest._bitCount, 0, _bitCount, 0, _bitCount.length); + + // -- internal hash state -- + System.arraycopy(originalDigest._hash, 0, _hash, 0, _hash.length); + System.arraycopy(originalDigest._K, 0, _K, 0, _K.length); + System.arraycopy(originalDigest._L, 0, _L, 0, _L.length); + System.arraycopy(originalDigest._block, 0, _block, 0, _block.length); + System.arraycopy(originalDigest._state, 0, _state, 0, _state.length); + } } diff --git a/src/main/java/org/bouncycastle/crypto/ec/ECPair.java b/src/main/java/org/bouncycastle/crypto/ec/ECPair.java index 8f868dec..d910f3c7 100644 --- a/src/main/java/org/bouncycastle/crypto/ec/ECPair.java +++ b/src/main/java/org/bouncycastle/crypto/ec/ECPair.java @@ -22,4 +22,17 @@ public class ECPair { return y; } + + public byte[] getEncoded() + { + byte[] xEnc = x.getEncoded(); + byte[] yEnc = y.getEncoded(); + + byte[] full = new byte[xEnc.length + yEnc.length]; + + System.arraycopy(xEnc, 0, full, 0, xEnc.length); + System.arraycopy(yEnc, 0, full, xEnc.length, yEnc.length); + + return full; + } } diff --git a/src/main/java/org/bouncycastle/crypto/engines/DESedeEngine.java b/src/main/java/org/bouncycastle/crypto/engines/DESedeEngine.java index f6941e3c..513eccdf 100644 --- a/src/main/java/org/bouncycastle/crypto/engines/DESedeEngine.java +++ b/src/main/java/org/bouncycastle/crypto/engines/DESedeEngine.java @@ -47,21 +47,7 @@ public class DESedeEngine if (keyMaster.length != 24 && keyMaster.length != 16) { - // new style 3TDEA key without parity bits. - if (keyMaster.length == 21) - { - byte[] tmp = new byte[24]; - - padKey(keyMaster, 0, tmp, 0); - padKey(keyMaster, 7, tmp, 8); - padKey(keyMaster, 14, tmp, 16); - - keyMaster = tmp; - } - else - { - throw new IllegalArgumentException("key size must be 16, 21, or 24 bytes."); - } + throw new IllegalArgumentException("key size must be 16 or 24 bytes."); } this.forEncryption = encrypting; @@ -86,18 +72,6 @@ public class DESedeEngine } } - private void padKey(byte[] keyMaster, int keyOff, byte[] tmp, int tmpOff) - { - tmp[tmpOff + 0] = (byte)(keyMaster[keyOff + 0] & 0xfe); - tmp[tmpOff + 1] = (byte)((keyMaster[keyOff + 0] << 7) | ((keyMaster[keyOff + 1] & 0xfc) >>> 1)); - tmp[tmpOff + 2] = (byte)((keyMaster[keyOff + 1] << 6) | ((keyMaster[keyOff + 2] & 0xf8) >>> 2)); - tmp[tmpOff + 3] = (byte)((keyMaster[keyOff + 2] << 5) | ((keyMaster[keyOff + 3] & 0xf0) >>> 3)); - tmp[tmpOff + 4] = (byte)((keyMaster[keyOff + 3] << 4) | ((keyMaster[keyOff + 4] & 0xe0) >>> 4)); - tmp[tmpOff + 5] = (byte)((keyMaster[keyOff + 4] << 3) | ((keyMaster[keyOff + 5] & 0xc0) >>> 5)); - tmp[tmpOff + 6] = (byte)((keyMaster[keyOff + 5] << 2) | ((keyMaster[keyOff + 6] & 0x80) >>> 6)); - tmp[tmpOff + 7] = (byte)(keyMaster[keyOff + 6] << 1); - } - public String getAlgorithmName() { return "DESede"; diff --git a/src/main/java/org/bouncycastle/crypto/engines/IESEngine.java b/src/main/java/org/bouncycastle/crypto/engines/IESEngine.java index f77d5f56..ea8556de 100755 --- a/src/main/java/org/bouncycastle/crypto/engines/IESEngine.java +++ b/src/main/java/org/bouncycastle/crypto/engines/IESEngine.java @@ -1,398 +1,398 @@ -package org.bouncycastle.crypto.engines;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.math.BigInteger;
-
-import org.bouncycastle.crypto.BasicAgreement;
-import org.bouncycastle.crypto.BufferedBlockCipher;
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DerivationFunction;
-import org.bouncycastle.crypto.EphemeralKeyPair;
-import org.bouncycastle.crypto.InvalidCipherTextException;
-import org.bouncycastle.crypto.KeyParser;
-import org.bouncycastle.crypto.Mac;
-import org.bouncycastle.crypto.generators.EphemeralKeyPairGenerator;
-import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
-import org.bouncycastle.crypto.params.IESParameters;
-import org.bouncycastle.crypto.params.IESWithCipherParameters;
-import org.bouncycastle.crypto.params.KDFParameters;
-import org.bouncycastle.crypto.params.KeyParameter;
-import org.bouncycastle.crypto.util.Pack;
-import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.BigIntegers;
-
-/**
- * Support class for constructing integrated encryption ciphers
- * for doing basic message exchanges on top of key agreement ciphers.
- * Follows the description given in IEEE Std 1363a.
- */
-public class IESEngine
-{
- BasicAgreement agree;
- DerivationFunction kdf;
- Mac mac;
- BufferedBlockCipher cipher;
- byte[] macBuf;
-
- boolean forEncryption;
- CipherParameters privParam, pubParam;
- IESParameters param;
-
- byte[] V;
- private EphemeralKeyPairGenerator keyPairGenerator;
- private KeyParser keyParser;
-
-
- /**
- * set up for use with stream mode, where the key derivation function
- * is used to provide a stream of bytes to xor with the message.
- *
- * @param agree the key agreement used as the basis for the encryption
- * @param kdf the key derivation function used for byte generation
- * @param mac the message authentication code generator for the message
- */
- public IESEngine(
- BasicAgreement agree,
- DerivationFunction kdf,
- Mac mac)
- {
- this.agree = agree;
- this.kdf = kdf;
- this.mac = mac;
- this.macBuf = new byte[mac.getMacSize()];
- this.cipher = null;
- }
-
-
- /**
- * set up for use in conjunction with a block cipher to handle the
- * message.
- *
- * @param agree the key agreement used as the basis for the encryption
- * @param kdf the key derivation function used for byte generation
- * @param mac the message authentication code generator for the message
- * @param cipher the cipher to used for encrypting the message
- */
- public IESEngine(
- BasicAgreement agree,
- DerivationFunction kdf,
- Mac mac,
- BufferedBlockCipher cipher)
- {
- this.agree = agree;
- this.kdf = kdf;
- this.mac = mac;
- this.macBuf = new byte[mac.getMacSize()];
- this.cipher = cipher;
- }
-
-
- /**
- * Initialise the encryptor.
- *
- * @param forEncryption whether or not this is encryption/decryption.
- * @param privParam our private key parameters
- * @param pubParam the recipient's/sender's public key parameters
- * @param param encoding and derivation parameters.
- */
- public void init(
- boolean forEncryption,
- CipherParameters privParam,
- CipherParameters pubParam,
- CipherParameters param)
- {
- this.forEncryption = forEncryption;
- this.privParam = privParam;
- this.pubParam = pubParam;
- this.param = (IESParameters)param;
- this.V = new byte[0];
- }
-
-
- /**
- * Initialise the encryptor.
- *
- * @param publicKey the recipient's/sender's public key parameters
- * @param params encoding and derivation parameters.
- * @param ephemeralKeyPairGenerator the ephemeral key pair generator to use.
- */
- public void init(AsymmetricKeyParameter publicKey, CipherParameters params, EphemeralKeyPairGenerator ephemeralKeyPairGenerator)
- {
- this.forEncryption = true;
- this.pubParam = publicKey;
- this.param = (IESParameters)params;
- this.keyPairGenerator = ephemeralKeyPairGenerator;
- }
-
- /**
- * Initialise the encryptor.
- *
- * @param privateKey the recipient's private key.
- * @param params encoding and derivation parameters.
- * @param publicKeyParser the parser for reading the ephemeral public key.
- */
- public void init(AsymmetricKeyParameter privateKey, CipherParameters params, KeyParser publicKeyParser)
- {
- this.forEncryption = false;
- this.privParam = privateKey;
- this.param = (IESParameters)params;
- this.keyParser = publicKeyParser;
- }
-
- public BufferedBlockCipher getCipher()
- {
- return cipher;
- }
-
- public Mac getMac()
- {
- return mac;
- }
-
- private byte[] encryptBlock(
- byte[] in,
- int inOff,
- int inLen)
- throws InvalidCipherTextException
- {
- byte[] C = null, K = null, K1 = null, K2 = null;
- int len;
-
- if (cipher == null)
- {
- // Streaming mode.
- K1 = new byte[inLen];
- K2 = new byte[param.getMacKeySize() / 8];
- K = new byte[K1.length + K2.length];
-
- kdf.generateBytes(K, 0, K.length);
-
- if (V.length != 0)
- {
- System.arraycopy(K, 0, K2, 0, K2.length);
- System.arraycopy(K, K2.length, K1, 0, K1.length);
- }
- else
- {
- System.arraycopy(K, 0, K1, 0, K1.length);
- System.arraycopy(K, inLen, K2, 0, K2.length);
- }
-
- C = new byte[inLen];
-
- for (int i = 0; i != inLen; i++)
- {
- C[i] = (byte)(in[inOff + i] ^ K1[i]);
- }
- len = inLen;
- }
- else
- {
- // Block cipher mode.
- K1 = new byte[((IESWithCipherParameters)param).getCipherKeySize() / 8];
- K2 = new byte[param.getMacKeySize() / 8];
- K = new byte[K1.length + K2.length];
-
- kdf.generateBytes(K, 0, K.length);
- System.arraycopy(K, 0, K1, 0, K1.length);
- System.arraycopy(K, K1.length, K2, 0, K2.length);
-
- cipher.init(true, new KeyParameter(K1));
- C = new byte[cipher.getOutputSize(inLen)];
- len = cipher.processBytes(in, inOff, inLen, C, 0);
- len += cipher.doFinal(C, len);
- }
-
-
- // Convert the length of the encoding vector into a byte array.
- byte[] P2 = param.getEncodingV();
- byte[] L2 = new byte[4];
- if (V.length != 0 && P2 != null)
- {
- Pack.intToBigEndian(P2.length * 8, L2, 0);
- }
-
-
- // Apply the MAC.
- byte[] T = new byte[mac.getMacSize()];
-
- mac.init(new KeyParameter(K2));
- mac.update(C, 0, C.length);
- if (P2 != null)
- {
- mac.update(P2, 0, P2.length);
- }
- if (V.length != 0)
- {
- mac.update(L2, 0, L2.length);
- }
- mac.doFinal(T, 0);
-
-
- // Output the triple (V,C,T).
- byte[] Output = new byte[V.length + len + T.length];
- System.arraycopy(V, 0, Output, 0, V.length);
- System.arraycopy(C, 0, Output, V.length, len);
- System.arraycopy(T, 0, Output, V.length + len, T.length);
- return Output;
- }
-
- private byte[] decryptBlock(
- byte[] in_enc,
- int inOff,
- int inLen)
- throws InvalidCipherTextException
- {
- byte[] M = null, K = null, K1 = null, K2 = null;
- int len;
-
- if (cipher == null)
- {
- // Streaming mode.
- K1 = new byte[inLen - V.length - mac.getMacSize()];
- K2 = new byte[param.getMacKeySize() / 8];
- K = new byte[K1.length + K2.length];
-
- kdf.generateBytes(K, 0, K.length);
-
- if (V.length != 0)
- {
- System.arraycopy(K, 0, K2, 0, K2.length);
- System.arraycopy(K, K2.length, K1, 0, K1.length);
- }
- else
- {
- System.arraycopy(K, 0, K1, 0, K1.length);
- System.arraycopy(K, K1.length, K2, 0, K2.length);
- }
-
- M = new byte[K1.length];
-
- for (int i = 0; i != K1.length; i++)
- {
- M[i] = (byte)(in_enc[inOff + V.length + i] ^ K1[i]);
- }
-
- len = K1.length;
- }
- else
- {
- // Block cipher mode.
- K1 = new byte[((IESWithCipherParameters)param).getCipherKeySize() / 8];
- K2 = new byte[param.getMacKeySize() / 8];
- K = new byte[K1.length + K2.length];
-
- kdf.generateBytes(K, 0, K.length);
- System.arraycopy(K, 0, K1, 0, K1.length);
- System.arraycopy(K, K1.length, K2, 0, K2.length);
-
- cipher.init(false, new KeyParameter(K1));
-
- M = new byte[cipher.getOutputSize(inLen - V.length - mac.getMacSize())];
- len = cipher.processBytes(in_enc, inOff + V.length, inLen - V.length - mac.getMacSize(), M, 0);
- len += cipher.doFinal(M, len);
- }
-
-
- // Convert the length of the encoding vector into a byte array.
- byte[] P2 = param.getEncodingV();
- byte[] L2 = new byte[4];
- if (V.length != 0 && P2 != null)
- {
- Pack.intToBigEndian(P2.length * 8, L2, 0);
- }
-
-
- // Verify the MAC.
- int end = inOff + inLen;
- byte[] T1 = Arrays.copyOfRange(in_enc, end - mac.getMacSize(), end);
-
- byte[] T2 = new byte[T1.length];
- mac.init(new KeyParameter(K2));
- mac.update(in_enc, inOff + V.length, inLen - V.length - T2.length);
-
- if (P2 != null)
- {
- mac.update(P2, 0, P2.length);
- }
- if (V.length != 0)
- {
- mac.update(L2, 0, L2.length);
- }
- mac.doFinal(T2, 0);
-
- if (!Arrays.constantTimeAreEqual(T1, T2))
- {
- throw new InvalidCipherTextException("Invalid MAC.");
- }
-
-
- // Output the message.
- return Arrays.copyOfRange(M, 0, len);
- }
-
-
- public byte[] processBlock(
- byte[] in,
- int inOff,
- int inLen)
- throws InvalidCipherTextException
- {
- if (forEncryption)
- {
- if (keyPairGenerator != null)
- {
- EphemeralKeyPair ephKeyPair = keyPairGenerator.generate();
-
- this.privParam = ephKeyPair.getKeyPair().getPrivate();
- this.V = ephKeyPair.getEncodedPublicKey();
- }
- }
- else
- {
- if (keyParser != null)
- {
- ByteArrayInputStream bIn = new ByteArrayInputStream(in, inOff, inLen);
-
- try
- {
- this.pubParam = keyParser.readKey(bIn);
- }
- catch (IOException e)
- {
- throw new InvalidCipherTextException("unable to recover ephemeral public key: " + e.getMessage(), e);
- }
-
- int encLength = (inLen - bIn.available());
- this.V = Arrays.copyOfRange(in, inOff, inOff + encLength);
- }
- }
-
- // Compute the common value and convert to byte array.
- agree.init(privParam);
- BigInteger z = agree.calculateAgreement(pubParam);
- byte[] Z = BigIntegers.asUnsignedByteArray(agree.getFieldSize(), z);
-
- // Create input to KDF.
- byte[] VZ;
- if (V.length != 0)
- {
- VZ = new byte[V.length + Z.length];
- System.arraycopy(V, 0, VZ, 0, V.length);
- System.arraycopy(Z, 0, VZ, V.length, Z.length);
- }
- else
- {
- VZ = Z;
- }
-
- // Initialise the KDF.
- KDFParameters kdfParam = new KDFParameters(VZ, param.getDerivationV());
- kdf.init(kdfParam);
-
- return forEncryption
- ? encryptBlock(in, inOff, inLen)
- : decryptBlock(in, inOff, inLen);
- }
-}
+package org.bouncycastle.crypto.engines; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.math.BigInteger; + +import org.bouncycastle.crypto.BasicAgreement; +import org.bouncycastle.crypto.BufferedBlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DerivationFunction; +import org.bouncycastle.crypto.EphemeralKeyPair; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.KeyParser; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.generators.EphemeralKeyPairGenerator; +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.crypto.params.IESParameters; +import org.bouncycastle.crypto.params.IESWithCipherParameters; +import org.bouncycastle.crypto.params.KDFParameters; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.util.Pack; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.BigIntegers; + +/** + * Support class for constructing integrated encryption ciphers + * for doing basic message exchanges on top of key agreement ciphers. + * Follows the description given in IEEE Std 1363a. + */ +public class IESEngine +{ + BasicAgreement agree; + DerivationFunction kdf; + Mac mac; + BufferedBlockCipher cipher; + byte[] macBuf; + + boolean forEncryption; + CipherParameters privParam, pubParam; + IESParameters param; + + byte[] V; + private EphemeralKeyPairGenerator keyPairGenerator; + private KeyParser keyParser; + + + /** + * set up for use with stream mode, where the key derivation function + * is used to provide a stream of bytes to xor with the message. + * + * @param agree the key agreement used as the basis for the encryption + * @param kdf the key derivation function used for byte generation + * @param mac the message authentication code generator for the message + */ + public IESEngine( + BasicAgreement agree, + DerivationFunction kdf, + Mac mac) + { + this.agree = agree; + this.kdf = kdf; + this.mac = mac; + this.macBuf = new byte[mac.getMacSize()]; + this.cipher = null; + } + + + /** + * set up for use in conjunction with a block cipher to handle the + * message. + * + * @param agree the key agreement used as the basis for the encryption + * @param kdf the key derivation function used for byte generation + * @param mac the message authentication code generator for the message + * @param cipher the cipher to used for encrypting the message + */ + public IESEngine( + BasicAgreement agree, + DerivationFunction kdf, + Mac mac, + BufferedBlockCipher cipher) + { + this.agree = agree; + this.kdf = kdf; + this.mac = mac; + this.macBuf = new byte[mac.getMacSize()]; + this.cipher = cipher; + } + + + /** + * Initialise the encryptor. + * + * @param forEncryption whether or not this is encryption/decryption. + * @param privParam our private key parameters + * @param pubParam the recipient's/sender's public key parameters + * @param param encoding and derivation parameters. + */ + public void init( + boolean forEncryption, + CipherParameters privParam, + CipherParameters pubParam, + CipherParameters param) + { + this.forEncryption = forEncryption; + this.privParam = privParam; + this.pubParam = pubParam; + this.param = (IESParameters)param; + this.V = new byte[0]; + } + + + /** + * Initialise the encryptor. + * + * @param publicKey the recipient's/sender's public key parameters + * @param params encoding and derivation parameters. + * @param ephemeralKeyPairGenerator the ephemeral key pair generator to use. + */ + public void init(AsymmetricKeyParameter publicKey, CipherParameters params, EphemeralKeyPairGenerator ephemeralKeyPairGenerator) + { + this.forEncryption = true; + this.pubParam = publicKey; + this.param = (IESParameters)params; + this.keyPairGenerator = ephemeralKeyPairGenerator; + } + + /** + * Initialise the encryptor. + * + * @param privateKey the recipient's private key. + * @param params encoding and derivation parameters. + * @param publicKeyParser the parser for reading the ephemeral public key. + */ + public void init(AsymmetricKeyParameter privateKey, CipherParameters params, KeyParser publicKeyParser) + { + this.forEncryption = false; + this.privParam = privateKey; + this.param = (IESParameters)params; + this.keyParser = publicKeyParser; + } + + public BufferedBlockCipher getCipher() + { + return cipher; + } + + public Mac getMac() + { + return mac; + } + + private byte[] encryptBlock( + byte[] in, + int inOff, + int inLen) + throws InvalidCipherTextException + { + byte[] C = null, K = null, K1 = null, K2 = null; + int len; + + if (cipher == null) + { + // Streaming mode. + K1 = new byte[inLen]; + K2 = new byte[param.getMacKeySize() / 8]; + K = new byte[K1.length + K2.length]; + + kdf.generateBytes(K, 0, K.length); + + if (V.length != 0) + { + System.arraycopy(K, 0, K2, 0, K2.length); + System.arraycopy(K, K2.length, K1, 0, K1.length); + } + else + { + System.arraycopy(K, 0, K1, 0, K1.length); + System.arraycopy(K, inLen, K2, 0, K2.length); + } + + C = new byte[inLen]; + + for (int i = 0; i != inLen; i++) + { + C[i] = (byte)(in[inOff + i] ^ K1[i]); + } + len = inLen; + } + else + { + // Block cipher mode. + K1 = new byte[((IESWithCipherParameters)param).getCipherKeySize() / 8]; + K2 = new byte[param.getMacKeySize() / 8]; + K = new byte[K1.length + K2.length]; + + kdf.generateBytes(K, 0, K.length); + System.arraycopy(K, 0, K1, 0, K1.length); + System.arraycopy(K, K1.length, K2, 0, K2.length); + + cipher.init(true, new KeyParameter(K1)); + C = new byte[cipher.getOutputSize(inLen)]; + len = cipher.processBytes(in, inOff, inLen, C, 0); + len += cipher.doFinal(C, len); + } + + + // Convert the length of the encoding vector into a byte array. + byte[] P2 = param.getEncodingV(); + byte[] L2 = new byte[4]; + if (V.length != 0 && P2 != null) + { + Pack.intToBigEndian(P2.length * 8, L2, 0); + } + + + // Apply the MAC. + byte[] T = new byte[mac.getMacSize()]; + + mac.init(new KeyParameter(K2)); + mac.update(C, 0, C.length); + if (P2 != null) + { + mac.update(P2, 0, P2.length); + } + if (V.length != 0) + { + mac.update(L2, 0, L2.length); + } + mac.doFinal(T, 0); + + + // Output the triple (V,C,T). + byte[] Output = new byte[V.length + len + T.length]; + System.arraycopy(V, 0, Output, 0, V.length); + System.arraycopy(C, 0, Output, V.length, len); + System.arraycopy(T, 0, Output, V.length + len, T.length); + return Output; + } + + private byte[] decryptBlock( + byte[] in_enc, + int inOff, + int inLen) + throws InvalidCipherTextException + { + byte[] M = null, K = null, K1 = null, K2 = null; + int len; + + if (cipher == null) + { + // Streaming mode. + K1 = new byte[inLen - V.length - mac.getMacSize()]; + K2 = new byte[param.getMacKeySize() / 8]; + K = new byte[K1.length + K2.length]; + + kdf.generateBytes(K, 0, K.length); + + if (V.length != 0) + { + System.arraycopy(K, 0, K2, 0, K2.length); + System.arraycopy(K, K2.length, K1, 0, K1.length); + } + else + { + System.arraycopy(K, 0, K1, 0, K1.length); + System.arraycopy(K, K1.length, K2, 0, K2.length); + } + + M = new byte[K1.length]; + + for (int i = 0; i != K1.length; i++) + { + M[i] = (byte)(in_enc[inOff + V.length + i] ^ K1[i]); + } + + len = K1.length; + } + else + { + // Block cipher mode. + K1 = new byte[((IESWithCipherParameters)param).getCipherKeySize() / 8]; + K2 = new byte[param.getMacKeySize() / 8]; + K = new byte[K1.length + K2.length]; + + kdf.generateBytes(K, 0, K.length); + System.arraycopy(K, 0, K1, 0, K1.length); + System.arraycopy(K, K1.length, K2, 0, K2.length); + + cipher.init(false, new KeyParameter(K1)); + + M = new byte[cipher.getOutputSize(inLen - V.length - mac.getMacSize())]; + len = cipher.processBytes(in_enc, inOff + V.length, inLen - V.length - mac.getMacSize(), M, 0); + len += cipher.doFinal(M, len); + } + + + // Convert the length of the encoding vector into a byte array. + byte[] P2 = param.getEncodingV(); + byte[] L2 = new byte[4]; + if (V.length != 0 && P2 != null) + { + Pack.intToBigEndian(P2.length * 8, L2, 0); + } + + + // Verify the MAC. + int end = inOff + inLen; + byte[] T1 = Arrays.copyOfRange(in_enc, end - mac.getMacSize(), end); + + byte[] T2 = new byte[T1.length]; + mac.init(new KeyParameter(K2)); + mac.update(in_enc, inOff + V.length, inLen - V.length - T2.length); + + if (P2 != null) + { + mac.update(P2, 0, P2.length); + } + if (V.length != 0) + { + mac.update(L2, 0, L2.length); + } + mac.doFinal(T2, 0); + + if (!Arrays.constantTimeAreEqual(T1, T2)) + { + throw new InvalidCipherTextException("Invalid MAC."); + } + + + // Output the message. + return Arrays.copyOfRange(M, 0, len); + } + + + public byte[] processBlock( + byte[] in, + int inOff, + int inLen) + throws InvalidCipherTextException + { + if (forEncryption) + { + if (keyPairGenerator != null) + { + EphemeralKeyPair ephKeyPair = keyPairGenerator.generate(); + + this.privParam = ephKeyPair.getKeyPair().getPrivate(); + this.V = ephKeyPair.getEncodedPublicKey(); + } + } + else + { + if (keyParser != null) + { + ByteArrayInputStream bIn = new ByteArrayInputStream(in, inOff, inLen); + + try + { + this.pubParam = keyParser.readKey(bIn); + } + catch (IOException e) + { + throw new InvalidCipherTextException("unable to recover ephemeral public key: " + e.getMessage(), e); + } + + int encLength = (inLen - bIn.available()); + this.V = Arrays.copyOfRange(in, inOff, inOff + encLength); + } + } + + // Compute the common value and convert to byte array. + agree.init(privParam); + BigInteger z = agree.calculateAgreement(pubParam); + byte[] Z = BigIntegers.asUnsignedByteArray(agree.getFieldSize(), z); + + // Create input to KDF. + byte[] VZ; + if (V.length != 0) + { + VZ = new byte[V.length + Z.length]; + System.arraycopy(V, 0, VZ, 0, V.length); + System.arraycopy(Z, 0, VZ, V.length, Z.length); + } + else + { + VZ = Z; + } + + // Initialise the KDF. + KDFParameters kdfParam = new KDFParameters(VZ, param.getDerivationV()); + kdf.init(kdfParam); + + return forEncryption + ? encryptBlock(in, inOff, inLen) + : decryptBlock(in, inOff, inLen); + } +} diff --git a/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java b/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java index c6e39986..2ef8dd2b 100644 --- a/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java +++ b/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java @@ -1,142 +1,142 @@ -package org.bouncycastle.crypto.generators;
-
-import org.bouncycastle.crypto.DataLengthException;
-import org.bouncycastle.crypto.DerivationFunction;
-import org.bouncycastle.crypto.DerivationParameters;
-import org.bouncycastle.crypto.Digest;
-import org.bouncycastle.crypto.params.ISO18033KDFParameters;
-import org.bouncycastle.crypto.params.KDFParameters;
-import org.bouncycastle.crypto.util.Pack;
-
-/**
- * Basic KDF generator for derived keys and ivs as defined by IEEE P1363a/ISO
- * 18033 <br>
- * This implementation is based on ISO 18033/P1363a.
- */
-public class BaseKDFBytesGenerator implements DerivationFunction
-{
- private int counterStart;
- private Digest digest;
- private byte[] shared;
- private byte[] iv;
-
- /**
- * Construct a KDF Parameters generator.
- * <p>
- *
- * @param counterStart
- * value of counter.
- * @param digest
- * the digest to be used as the source of derived keys.
- */
- protected BaseKDFBytesGenerator(int counterStart, Digest digest)
- {
- this.counterStart = counterStart;
- this.digest = digest;
- }
-
- public void init(DerivationParameters param)
- {
- if (param instanceof KDFParameters)
- {
- KDFParameters p = (KDFParameters)param;
-
- shared = p.getSharedSecret();
- iv = p.getIV();
- }
- else if (param instanceof ISO18033KDFParameters)
- {
- ISO18033KDFParameters p = (ISO18033KDFParameters)param;
-
- shared = p.getSeed();
- iv = null;
- }
- else
- {
- throw new IllegalArgumentException("KDF parameters required for KDF2Generator");
- }
- }
-
- /**
- * return the underlying digest.
- */
- public Digest getDigest()
- {
- return digest;
- }
-
- /**
- * fill len bytes of the output buffer with bytes generated from the
- * derivation function.
- *
- * @throws IllegalArgumentException
- * if the size of the request will cause an overflow.
- * @throws DataLengthException
- * if the out buffer is too small.
- */
- public int generateBytes(byte[] out, int outOff, int len) throws DataLengthException,
- IllegalArgumentException
- {
- if ((out.length - len) < outOff)
- {
- throw new DataLengthException("output buffer too small");
- }
-
- long oBytes = len;
- int outLen = digest.getDigestSize();
-
- //
- // this is at odds with the standard implementation, the
- // maximum value should be hBits * (2^32 - 1) where hBits
- // is the digest output size in bits. We can't have an
- // array with a long index at the moment...
- //
- if (oBytes > ((2L << 32) - 1))
- {
- throw new IllegalArgumentException("Output length too large");
- }
-
- int cThreshold = (int)((oBytes + outLen - 1) / outLen);
-
- byte[] dig = new byte[digest.getDigestSize()];
-
- byte[] C = new byte[4];
- Pack.intToBigEndian(counterStart, C, 0);
-
- int counterBase = counterStart & ~0xFF;
-
- for (int i = 0; i < cThreshold; i++)
- {
- digest.update(shared, 0, shared.length);
- digest.update(C, 0, C.length);
-
- if (iv != null)
- {
- digest.update(iv, 0, iv.length);
- }
-
- digest.doFinal(dig, 0);
-
- if (len > outLen)
- {
- System.arraycopy(dig, 0, out, outOff, outLen);
- outOff += outLen;
- len -= outLen;
- }
- else
- {
- System.arraycopy(dig, 0, out, outOff, len);
- }
-
- if (++C[3] == 0)
- {
- counterBase += 0x100;
- Pack.intToBigEndian(counterBase, C, 0);
- }
- }
-
- digest.reset();
-
- return (int)oBytes;
- }
-}
+package org.bouncycastle.crypto.generators; + +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.DerivationFunction; +import org.bouncycastle.crypto.DerivationParameters; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.params.ISO18033KDFParameters; +import org.bouncycastle.crypto.params.KDFParameters; +import org.bouncycastle.crypto.util.Pack; + +/** + * Basic KDF generator for derived keys and ivs as defined by IEEE P1363a/ISO + * 18033 <br> + * This implementation is based on ISO 18033/P1363a. + */ +public class BaseKDFBytesGenerator implements DerivationFunction +{ + private int counterStart; + private Digest digest; + private byte[] shared; + private byte[] iv; + + /** + * Construct a KDF Parameters generator. + * <p> + * + * @param counterStart + * value of counter. + * @param digest + * the digest to be used as the source of derived keys. + */ + protected BaseKDFBytesGenerator(int counterStart, Digest digest) + { + this.counterStart = counterStart; + this.digest = digest; + } + + public void init(DerivationParameters param) + { + if (param instanceof KDFParameters) + { + KDFParameters p = (KDFParameters)param; + + shared = p.getSharedSecret(); + iv = p.getIV(); + } + else if (param instanceof ISO18033KDFParameters) + { + ISO18033KDFParameters p = (ISO18033KDFParameters)param; + + shared = p.getSeed(); + iv = null; + } + else + { + throw new IllegalArgumentException("KDF parameters required for KDF2Generator"); + } + } + + /** + * return the underlying digest. + */ + public Digest getDigest() + { + return digest; + } + + /** + * fill len bytes of the output buffer with bytes generated from the + * derivation function. + * + * @throws IllegalArgumentException + * if the size of the request will cause an overflow. + * @throws DataLengthException + * if the out buffer is too small. + */ + public int generateBytes(byte[] out, int outOff, int len) throws DataLengthException, + IllegalArgumentException + { + if ((out.length - len) < outOff) + { + throw new DataLengthException("output buffer too small"); + } + + long oBytes = len; + int outLen = digest.getDigestSize(); + + // + // this is at odds with the standard implementation, the + // maximum value should be hBits * (2^32 - 1) where hBits + // is the digest output size in bits. We can't have an + // array with a long index at the moment... + // + if (oBytes > ((2L << 32) - 1)) + { + throw new IllegalArgumentException("Output length too large"); + } + + int cThreshold = (int)((oBytes + outLen - 1) / outLen); + + byte[] dig = new byte[digest.getDigestSize()]; + + byte[] C = new byte[4]; + Pack.intToBigEndian(counterStart, C, 0); + + int counterBase = counterStart & ~0xFF; + + for (int i = 0; i < cThreshold; i++) + { + digest.update(shared, 0, shared.length); + digest.update(C, 0, C.length); + + if (iv != null) + { + digest.update(iv, 0, iv.length); + } + + digest.doFinal(dig, 0); + + if (len > outLen) + { + System.arraycopy(dig, 0, out, outOff, outLen); + outOff += outLen; + len -= outLen; + } + else + { + System.arraycopy(dig, 0, out, outOff, len); + } + + if (++C[3] == 0) + { + counterBase += 0x100; + Pack.intToBigEndian(counterBase, C, 0); + } + } + + digest.reset(); + + return (int)oBytes; + } +} diff --git a/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java b/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java index be977d71..749b0cc5 100644 --- a/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java +++ b/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java @@ -1,22 +1,23 @@ package org.bouncycastle.crypto.generators; +import java.math.BigInteger; +import java.security.SecureRandom; + import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.SHA1Digest; -import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.params.DSAParameterGenerationParameters; import org.bouncycastle.crypto.params.DSAParameters; import org.bouncycastle.crypto.params.DSAValidationParameters; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; +import org.bouncycastle.util.encoders.Hex; -import java.math.BigInteger; -import java.security.SecureRandom; - -// TODO Update javadoc to mention FIPS 186-3 when done /** - * generate suitable parameters for DSA, in line with FIPS 186-2. + * Generate suitable parameters for DSA, in line with FIPS 186-2, or FIPS 186-3. */ public class DSAParametersGenerator { + private Digest digest; private int L, N; private int certainty; private SecureRandom random; @@ -25,6 +26,19 @@ public class DSAParametersGenerator private static final BigInteger ONE = BigInteger.valueOf(1); private static final BigInteger TWO = BigInteger.valueOf(2); + private boolean use186_3; + private int usageIndex; + + public DSAParametersGenerator() + { + this(new SHA1Digest()); + } + + public DSAParametersGenerator(Digest digest) + { + this.digest = digest; + } + /** * initialise the key generator. * @@ -37,23 +51,53 @@ public class DSAParametersGenerator int certainty, SecureRandom random) { - init(size, getDefaultN(size), certainty, random); + this.use186_3 = false; + this.L = size; + this.N = getDefaultN(size); + this.certainty = certainty; + this.random = random; } - // TODO Make public to enable support for DSA keys > 1024 bits - private void init( - int L, - int N, - int certainty, - SecureRandom random) + /** + * Initialise the key generator for DSA 2. + * <p> + * Use this init method if you need to generate parameters for DSA 2 keys. + * </p> + * + * @param params DSA 2 key generation parameters. + */ + public void init( + DSAParameterGenerationParameters params) { - // TODO Check that the (L, N) pair is in the list of acceptable (L, N pairs) (see Section 4.2) // TODO Should we enforce the minimum 'certainty' values as per C.3 Table C.1? + this.use186_3 = true; + this.L = params.getL(); + this.N = params.getN(); + this.certainty = params.getCertainty(); + this.random = params.getRandom(); + this.usageIndex = params.getUsageIndex(); + + if ((L < 1024 || L > 3072) || L % 1024 != 0) + { + throw new IllegalArgumentException("L values must be between 1024 and 3072 and a multiple of 1024"); + } + else if (L == 1024 && N != 160) + { + throw new IllegalArgumentException("N must be 160 for L = 1024"); + } + else if (L == 2048 && (N != 224 && N != 256)) + { + throw new IllegalArgumentException("N must be 224 or 256 for L = 2048"); + } + else if (L == 3072 && N != 256) + { + throw new IllegalArgumentException("N must be 256 for L = 3072"); + } - this.L = L; - this.N = N; - this.certainty = certainty; - this.random = random; + if (digest.getDigestSize() * 8 < N) + { + throw new IllegalStateException("Digest output size too small for value of N"); + } } /** @@ -64,7 +108,7 @@ public class DSAParametersGenerator */ public DSAParameters generateParameters() { - return L > 1024 + return (use186_3) ? generateParameters_FIPS186_3() : generateParameters_FIPS186_2(); } @@ -75,18 +119,22 @@ public class DSAParametersGenerator byte[] part1 = new byte[20]; byte[] part2 = new byte[20]; byte[] u = new byte[20]; - SHA1Digest sha1 = new SHA1Digest(); int n = (L - 1) / 160; byte[] w = new byte[L / 8]; + if (!(digest instanceof SHA1Digest)) + { + throw new IllegalStateException("can only use SHA-1 for generating FIPS 186-2 parameters"); + } + for (;;) { random.nextBytes(seed); - hash(sha1, seed, part1); + hash(digest, seed, part1); System.arraycopy(seed, 0, part2, 0, seed.length); inc(part2); - hash(sha1, part2, part2); + hash(digest, part2, part2); for (int i = 0; i != u.length; i++) { @@ -111,12 +159,12 @@ public class DSAParametersGenerator for (int k = 0; k < n; k++) { inc(offset); - hash(sha1, offset, part1); + hash(digest, offset, part1); System.arraycopy(part1, 0, w, w.length - (k + 1) * part1.length, part1.length); } inc(offset); - hash(sha1, offset, part1); + hash(digest, offset, part1); System.arraycopy(part1, part1.length - ((w.length - (n) * part1.length)), w, 0, w.length - n * part1.length); w[0] |= (byte)0x80; @@ -166,7 +214,7 @@ public class DSAParametersGenerator { // A.1.1.2 Generation of the Probable Primes p and q Using an Approved Hash Function // FIXME This should be configurable (digest size in bits must be >= N) - Digest d = new SHA256Digest(); + Digest d = digest; int outlen = d.getDigestSize() * 8; // 1. Check that the (L, N) pair is in the list of acceptable (L, N pairs) (see Section 4.2). If @@ -192,6 +240,7 @@ public class DSAParametersGenerator // 6. U = Hash (domain_parameter_seed) mod 2^(N–1). hash(d, seed, output); + BigInteger U = new BigInteger(1, output).mod(ONE.shiftLeft(N - 1)); // 7. q = 2^(N–1) + U + 1 – ( U mod 2). @@ -253,16 +302,17 @@ public class DSAParametersGenerator { // 11.8 If p is determined to be prime, then return VALID and the values of p, q and // (optionally) the values of domain_parameter_seed and counter. - // TODO Make configurable (8-bit unsigned)? -// int index = 1; -// BigInteger g = calculateGenerator_FIPS186_3_Verifiable(d, p, q, seed, index); -// if (g != null) -// { -// // TODO Should 'index' be a part of the validation parameters? -// return new DSAParameters(p, q, g, new DSAValidationParameters(seed, counter)); -// } + if (usageIndex >= 0) + { + BigInteger g = calculateGenerator_FIPS186_3_Verifiable(d, p, q, seed, usageIndex); + if (g != null) + { + return new DSAParameters(p, q, g, new DSAValidationParameters(seed, counter, usageIndex)); + } + } BigInteger g = calculateGenerator_FIPS186_3_Unverifiable(p, q, random); + return new DSAParameters(p, q, g, new DSAValidationParameters(seed, counter)); } @@ -281,34 +331,34 @@ public class DSAParametersGenerator return calculateGenerator_FIPS186_2(p, q, r); } -// private static BigInteger calculateGenerator_FIPS186_3_Verifiable(Digest d, BigInteger p, BigInteger q, -// byte[] seed, int index) -// { -//// A.2.3 Verifiable Canonical Generation of the Generator g -// BigInteger e = p.subtract(ONE).divide(q); -// byte[] ggen = Hex.decode("6767656E"); -// -// // 7. U = domain_parameter_seed || "ggen" || index || count. -// byte[] U = new byte[seed.length + ggen.length + 1 + 2]; -// System.arraycopy(seed, 0, U, 0, seed.length); -// System.arraycopy(ggen, 0, U, seed.length, ggen.length); -// U[U.length - 3] = (byte)index; -// -// byte[] w = new byte[d.getDigestSize()]; -// for (int count = 1; count < (1 << 16); ++count) -// { -// inc(U); -// hash(d, U, w); -// BigInteger W = new BigInteger(1, w); -// BigInteger g = W.modPow(e, p); -// if (g.compareTo(TWO) >= 0) -// { -// return g; -// } -// } -// -// return null; -// } + private static BigInteger calculateGenerator_FIPS186_3_Verifiable(Digest d, BigInteger p, BigInteger q, + byte[] seed, int index) + { +// A.2.3 Verifiable Canonical Generation of the Generator g + BigInteger e = p.subtract(ONE).divide(q); + byte[] ggen = Hex.decode("6767656E"); + + // 7. U = domain_parameter_seed || "ggen" || index || count. + byte[] U = new byte[seed.length + ggen.length + 1 + 2]; + System.arraycopy(seed, 0, U, 0, seed.length); + System.arraycopy(ggen, 0, U, seed.length, ggen.length); + U[U.length - 3] = (byte)index; + + byte[] w = new byte[d.getDigestSize()]; + for (int count = 1; count < (1 << 16); ++count) + { + inc(U); + hash(d, U, w); + BigInteger W = new BigInteger(1, w); + BigInteger g = W.modPow(e, p); + if (g.compareTo(TWO) >= 0) + { + return g; + } + } + + return null; + } private static void hash(Digest d, byte[] input, byte[] output) { diff --git a/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java b/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java new file mode 100755 index 00000000..7332f264 --- /dev/null +++ b/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java @@ -0,0 +1,254 @@ +package org.bouncycastle.crypto.kems; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DerivationFunction; +import org.bouncycastle.crypto.KeyEncapsulation; +import org.bouncycastle.crypto.params.ECKeyParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.crypto.params.KDFParameters; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.BigIntegers; + +/** + * The ECIES Key Encapsulation Mechanism (ECIES-KEM) from ISO 18033-2. + */ +public class ECIESKeyEncapsulation + implements KeyEncapsulation +{ + private DerivationFunction kdf; + private SecureRandom rnd; + private ECKeyParameters key; + private boolean CofactorMode; + private boolean OldCofactorMode; + private boolean SingleHashMode; + + /** + * Set up the ECIES-KEM. + * + * @param kdf the key derivation function to be used. + * @param rnd the random source for the session key. + */ + public ECIESKeyEncapsulation( + DerivationFunction kdf, + SecureRandom rnd) + { + this.kdf = kdf; + this.rnd = rnd; + this.CofactorMode = false; + this.OldCofactorMode = false; + this.SingleHashMode = false; + } + + /** + * Set up the ECIES-KEM. + * + * @param kdf the key derivation function to be used. + * @param rnd the random source for the session key. + * @param cofactorMode true to use the new cofactor ECDH. + * @param oldCofactorMode true to use the old cofactor ECDH. + * @param singleHashMode true to use single hash mode. + */ + public ECIESKeyEncapsulation( + DerivationFunction kdf, + SecureRandom rnd, + boolean cofactorMode, + boolean oldCofactorMode, + boolean singleHashMode) + { + this.kdf = kdf; + this.rnd = rnd; + + // If both cofactorMode and oldCofactorMode are set to true + // then the implementation will use the new cofactor ECDH + this.CofactorMode = cofactorMode; + this.OldCofactorMode = oldCofactorMode; + this.SingleHashMode = singleHashMode; + } + + /** + * Initialise the ECIES-KEM. + * + * @param key the recipient's public (for encryption) or private (for decryption) key. + */ + public void init(CipherParameters key) + throws IllegalArgumentException + { + if (!(key instanceof ECKeyParameters)) + { + throw new IllegalArgumentException("EC key required"); + } + else + { + this.key = (ECKeyParameters)key; + } + } + + /** + * Generate and encapsulate a random session key. + * + * @param out the output buffer for the encapsulated key. + * @param outOff the offset for the output buffer. + * @param keyLen the length of the session key. + * @return the random session key. + */ + public CipherParameters encrypt(byte[] out, int outOff, int keyLen) + throws IllegalArgumentException + { + if (!(key instanceof ECPublicKeyParameters)) + { + throw new IllegalArgumentException("Public key required for encryption"); + } + + BigInteger n = key.getParameters().getN(); + BigInteger h = key.getParameters().getH(); + + // Generate the ephemeral key pair + BigInteger r = BigIntegers.createRandomInRange(BigInteger.ONE, n, rnd); + ECPoint gTilde = key.getParameters().getG().multiply(r); + + // Encode the ephemeral public key + byte[] C = gTilde.getEncoded(); + System.arraycopy(C, 0, out, outOff, C.length); + + // Compute the static-ephemeral key agreement + BigInteger rPrime; + if (CofactorMode) + { + rPrime = r.multiply(h).mod(n); + } + else + { + rPrime = r; + } + + ECPoint hTilde = ((ECPublicKeyParameters) key).getQ().multiply(rPrime); + + // Encode the shared secret value + int PEHlen = (key.getParameters().getCurve().getFieldSize()+7)/8; + byte[] PEH = BigIntegers.asUnsignedByteArray(PEHlen, hTilde.getX().toBigInteger()); + + // Initialise the KDF + byte[] kdfInput; + if (SingleHashMode) + { + kdfInput = new byte[C.length + PEH.length]; + System.arraycopy(C, 0, kdfInput, 0, C.length); + System.arraycopy(PEH, 0, kdfInput, C.length, PEH.length); + } + else + { + kdfInput = PEH; + } + + kdf.init(new KDFParameters(kdfInput, null)); + + // Generate the secret key + byte[] K = new byte[keyLen]; + kdf.generateBytes(K, 0, K.length); + + // Return the ciphertext + return new KeyParameter(K); + } + + /** + * Generate and encapsulate a random session key. + * + * @param out the output buffer for the encapsulated key. + * @param keyLen the length of the session key. + * @return the random session key. + */ + public CipherParameters encrypt(byte[] out, int keyLen) + { + return encrypt(out, 0, keyLen); + } + + /** + * Decrypt an encapsulated session key. + * + * @param in the input buffer for the encapsulated key. + * @param inOff the offset for the input buffer. + * @param inLen the length of the encapsulated key. + * @param keyLen the length of the session key. + * @return the session key. + */ + public CipherParameters decrypt(byte[] in, int inOff, int inLen, int keyLen) + throws IllegalArgumentException + { + if (!(key instanceof ECPrivateKeyParameters)) + { + throw new IllegalArgumentException("Private key required for encryption"); + } + + BigInteger n = key.getParameters().getN(); + BigInteger h = key.getParameters().getH(); + + // Decode the ephemeral public key + byte[] C = new byte[inLen]; + System.arraycopy(in, inOff, C, 0, inLen); + ECPoint gTilde = key.getParameters().getCurve().decodePoint(C); + + // Compute the static-ephemeral key agreement + ECPoint gHat; + if ((CofactorMode) || (OldCofactorMode)) + { + gHat = gTilde.multiply(h); + } + else + { + gHat = gTilde; + } + + BigInteger xHat; + if(CofactorMode) + { + xHat = ((ECPrivateKeyParameters) key).getD().multiply(h.modInverse(n)).mod(n); + } + else + { + xHat = ((ECPrivateKeyParameters) key).getD(); + } + + ECPoint hTilde = gHat.multiply(xHat); + + // Encode the shared secret value + int PEHlen = (key.getParameters().getCurve().getFieldSize()+7)/8; + byte[] PEH = BigIntegers.asUnsignedByteArray(PEHlen, hTilde.getX().toBigInteger()); + + // Initialise the KDF + byte[] kdfInput; + if (SingleHashMode) + { + kdfInput = new byte[C.length + PEH.length]; + System.arraycopy(C, 0, kdfInput, 0, C.length); + System.arraycopy(PEH, 0, kdfInput, C.length, PEH.length); + } + else + { + kdfInput = PEH; + } + kdf.init(new KDFParameters(kdfInput, null)); + + // Generate the secret key + byte[] K = new byte[keyLen]; + kdf.generateBytes(K, 0, K.length); + + return new KeyParameter(K); + } + + /** + * Decrypt an encapsulated session key. + * + * @param in the input buffer for the encapsulated key. + * @param keyLen the length of the session key. + * @return the session key. + */ + public CipherParameters decrypt(byte[] in, int keyLen) + { + return decrypt(in, 0, in.length, keyLen); + } +} diff --git a/src/main/java/org/bouncycastle/crypto/kems/RSAKeyEncapsulation.java b/src/main/java/org/bouncycastle/crypto/kems/RSAKeyEncapsulation.java new file mode 100755 index 00000000..5ffd356a --- /dev/null +++ b/src/main/java/org/bouncycastle/crypto/kems/RSAKeyEncapsulation.java @@ -0,0 +1,161 @@ +package org.bouncycastle.crypto.kems; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DerivationFunction; +import org.bouncycastle.crypto.KeyEncapsulation; +import org.bouncycastle.crypto.params.KDFParameters; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.RSAKeyParameters; +import org.bouncycastle.util.BigIntegers; + +/** + * The RSA Key Encapsulation Mechanism (RSA-KEM) from ISO 18033-2. + */ +public class RSAKeyEncapsulation + implements KeyEncapsulation +{ + private DerivationFunction kdf; + private SecureRandom rnd; + private RSAKeyParameters key; + + /** + * Set up the RSA-KEM. + * + * @param kdf the key derivation function to be used. + * @param rnd the random source for the session key. + */ + public RSAKeyEncapsulation( + DerivationFunction kdf, + SecureRandom rnd) + { + this.kdf = kdf; + this.rnd = rnd; + } + + + /** + * Initialise the RSA-KEM. + * + * @param key the recipient's public (for encryption) or private (for decryption) key. + */ + public void init(CipherParameters key) + throws IllegalArgumentException + { + if (!(key instanceof RSAKeyParameters)) + { + throw new IllegalArgumentException("RSA key required"); + } + else + { + this.key = (RSAKeyParameters)key; + } + } + + + /** + * Generate and encapsulate a random session key. + * + * @param out the output buffer for the encapsulated key. + * @param outOff the offset for the output buffer. + * @param keyLen the length of the random session key. + * @return the random session key. + */ + public CipherParameters encrypt(byte[] out, int outOff, int keyLen) + throws IllegalArgumentException + { + if (key.isPrivate()) + { + throw new IllegalArgumentException("Public key required for encryption"); + } + + BigInteger n = key.getModulus(); + BigInteger e = key.getExponent(); + + // Generate the ephemeral random and encode it + BigInteger r = BigIntegers.createRandomInRange(BigInteger.ZERO, n.subtract(BigInteger.ONE), rnd); + byte[] R = BigIntegers.asUnsignedByteArray((n.bitLength()+7)/8, r); + + // Encrypt the random and encode it + BigInteger c = r.modPow(e, n); + byte[] C = BigIntegers.asUnsignedByteArray((n.bitLength()+7)/8, c); + System.arraycopy(C, 0, out, outOff, C.length); + + + // Initialise the KDF + kdf.init(new KDFParameters(R, null)); + + // Generate the secret key + byte[] K = new byte[keyLen]; + kdf.generateBytes(K, 0, K.length); + + return new KeyParameter(K); + } + + + /** + * Generate and encapsulate a random session key. + * + * @param out the output buffer for the encapsulated key. + * @param keyLen the length of the random session key. + * @return the random session key. + */ + public CipherParameters encrypt(byte[] out, int keyLen) + { + return encrypt(out, 0, keyLen); + } + + + /** + * Decrypt an encapsulated session key. + * + * @param in the input buffer for the encapsulated key. + * @param inOff the offset for the input buffer. + * @param inLen the length of the encapsulated key. + * @param keyLen the length of the session key. + * @return the session key. + */ + public CipherParameters decrypt(byte[] in, int inOff, int inLen, int keyLen) + throws IllegalArgumentException + { + if (!key.isPrivate()) + { + throw new IllegalArgumentException("Private key required for decryption"); + } + + BigInteger n = key.getModulus(); + BigInteger d = key.getExponent(); + + // Decode the input + byte[] C = new byte[inLen]; + System.arraycopy(in, inOff, C, 0, C.length); + BigInteger c = new BigInteger(1, C); + + // Decrypt the ephemeral random and encode it + BigInteger r = c.modPow(d, n); + byte[] R = BigIntegers.asUnsignedByteArray((n.bitLength()+7)/8, r); + + // Initialise the KDF + kdf.init(new KDFParameters(R, null)); + + // Generate the secret key + byte[] K = new byte[keyLen]; + kdf.generateBytes(K, 0, K.length); + + return new KeyParameter(K); + } + + /** + * Decrypt an encapsulated session key. + * + * @param in the input buffer for the encapsulated key. + * @param keyLen the length of the session key. + * @return the session key. + */ + public CipherParameters decrypt(byte[] in, int keyLen) + { + return decrypt(in, 0, in.length, keyLen); + } +} diff --git a/src/main/java/org/bouncycastle/crypto/macs/GMac.java b/src/main/java/org/bouncycastle/crypto/macs/GMac.java new file mode 100644 index 00000000..91bfe13d --- /dev/null +++ b/src/main/java/org/bouncycastle/crypto/macs/GMac.java @@ -0,0 +1,108 @@ +package org.bouncycastle.crypto.macs; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.modes.GCMBlockCipher; +import org.bouncycastle.crypto.params.AEADParameters; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; + +/** + * The GMAC specialisation of Galois/Counter mode (GCM) detailed in NIST Special Publication + * 800-38D. + * <p> + * GMac is an invocation of the GCM mode where no data is encrypted (i.e. all input data to the Mac + * is processed as additional authenticated data with the underlying GCM block cipher). + */ +public class GMac implements Mac +{ + private final GCMBlockCipher cipher; + private final int macSizeBits; + + /** + * Creates a GMAC based on the operation of a block cipher in GCM mode. + * <p/> + * This will produce an authentication code the length of the block size of the cipher. + * + * @param cipher + * the cipher to be used in GCM mode to generate the MAC. + */ + public GMac(final GCMBlockCipher cipher) + { + this(cipher, 128); + } + + /** + * Creates a GMAC based on the operation of a 128 bit block cipher in GCM mode. + * + * @param macSizeBits + * the mac size to generate, in bits. Must be a multiple of 8 and >= 96 and <= 128. + * @param cipher + * the cipher to be used in GCM mode to generate the MAC. + */ + public GMac(final GCMBlockCipher cipher, final int macSizeBits) + { + this.cipher = cipher; + this.macSizeBits = macSizeBits; + } + + /** + * Initialises the GMAC - requires a {@link ParametersWithIV} providing a {@link KeyParameter} + * and a nonce. + */ + public void init(final CipherParameters params) throws IllegalArgumentException + { + if (params instanceof ParametersWithIV) + { + final ParametersWithIV param = (ParametersWithIV)params; + + final byte[] iv = param.getIV(); + final KeyParameter keyParam = (KeyParameter)param.getParameters(); + + // GCM is always operated in encrypt mode to calculate MAC + cipher.init(true, new AEADParameters(keyParam, macSizeBits, iv)); + } else + { + throw new IllegalArgumentException("GMAC requires ParametersWithIV"); + } + } + + public String getAlgorithmName() + { + return cipher.getUnderlyingCipher().getAlgorithmName() + "-GMAC"; + } + + public int getMacSize() + { + return macSizeBits / 8; + } + + public void update(byte in) throws IllegalStateException + { + cipher.processAADByte(in); + } + + public void update(byte[] in, int inOff, int len) throws DataLengthException, IllegalStateException + { + cipher.processAADBytes(in, inOff, len); + } + + public int doFinal(byte[] out, int outOff) throws DataLengthException, IllegalStateException + { + try + { + return cipher.doFinal(out, outOff); + } catch (InvalidCipherTextException e) + { + // Impossible in encrypt mode + throw new IllegalStateException(e); + } + } + + public void reset() + { + cipher.reset(); + } +} diff --git a/src/main/java/org/bouncycastle/crypto/macs/HMac.java b/src/main/java/org/bouncycastle/crypto/macs/HMac.java index 382d0e33..d4345d9b 100644 --- a/src/main/java/org/bouncycastle/crypto/macs/HMac.java +++ b/src/main/java/org/bouncycastle/crypto/macs/HMac.java @@ -8,6 +8,7 @@ import org.bouncycastle.crypto.ExtendedDigest; import org.bouncycastle.crypto.Mac; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.util.Integers; +import org.bouncycastle.util.Memoable; /** * HMAC implementation based on RFC2104 @@ -23,7 +24,9 @@ public class HMac private Digest digest; private int digestSize; private int blockLength; - + private Memoable ipadState; + private Memoable opadState; + private byte[] inputPad; private byte[] outputBuf; @@ -133,7 +136,19 @@ public class HMac xorPad(inputPad, blockLength, IPAD); xorPad(outputBuf, blockLength, OPAD); + if (digest instanceof Memoable) + { + opadState = ((Memoable)digest).copy(); + + ((Digest)opadState).update(outputBuf, 0, blockLength); + } + digest.update(inputPad, 0, inputPad.length); + + if (digest instanceof Memoable) + { + ipadState = ((Memoable)digest).copy(); + } } public int getMacSize() @@ -160,7 +175,17 @@ public class HMac int outOff) { digest.doFinal(outputBuf, blockLength); - digest.update(outputBuf, 0, outputBuf.length); + + if (opadState != null) + { + ((Memoable)digest).reset(opadState); + digest.update(outputBuf, blockLength, digest.getDigestSize()); + } + else + { + digest.update(outputBuf, 0, outputBuf.length); + } + int len = digest.doFinal(out, outOff); for (int i = blockLength; i < outputBuf.length; i++) @@ -168,7 +193,14 @@ public class HMac outputBuf[i] = 0; } - digest.update(inputPad, 0, inputPad.length); + if (ipadState != null) + { + ((Memoable)digest).reset(ipadState); + } + else + { + digest.update(inputPad, 0, inputPad.length); + } return len; } diff --git a/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java b/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java index 600dd297..9a6e2e0c 100644 --- a/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java +++ b/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java @@ -1,378 +1,378 @@ -package org.bouncycastle.crypto.modes;
-
-import java.io.ByteArrayOutputStream;
-
-import org.bouncycastle.crypto.BlockCipher;
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DataLengthException;
-import org.bouncycastle.crypto.InvalidCipherTextException;
-import org.bouncycastle.crypto.Mac;
-import org.bouncycastle.crypto.macs.CBCBlockCipherMac;
-import org.bouncycastle.crypto.params.AEADParameters;
-import org.bouncycastle.crypto.params.ParametersWithIV;
-import org.bouncycastle.util.Arrays;
-
-/**
- * Implements the Counter with Cipher Block Chaining mode (CCM) detailed in
- * NIST Special Publication 800-38C.
- * <p>
- * <b>Note</b>: this mode is a packet mode - it needs all the data up front.
- */
-public class CCMBlockCipher
- implements AEADBlockCipher
-{
- private BlockCipher cipher;
- private int blockSize;
- private boolean forEncryption;
- private byte[] nonce;
- private byte[] initialAssociatedText;
- private int macSize;
- private CipherParameters keyParam;
- private byte[] macBlock;
- private ByteArrayOutputStream associatedText = new ByteArrayOutputStream();
- private ByteArrayOutputStream data = new ByteArrayOutputStream();
-
- /**
- * Basic constructor.
- *
- * @param c the block cipher to be used.
- */
- public CCMBlockCipher(BlockCipher c)
- {
- this.cipher = c;
- this.blockSize = c.getBlockSize();
- this.macBlock = new byte[blockSize];
-
- if (blockSize != 16)
- {
- throw new IllegalArgumentException("cipher required with a block size of 16.");
- }
- }
-
- /**
- * return the underlying block cipher that we are wrapping.
- *
- * @return the underlying block cipher that we are wrapping.
- */
- public BlockCipher getUnderlyingCipher()
- {
- return cipher;
- }
-
-
- public void init(boolean forEncryption, CipherParameters params)
- throws IllegalArgumentException
- {
- this.forEncryption = forEncryption;
-
- if (params instanceof AEADParameters)
- {
- AEADParameters param = (AEADParameters)params;
-
- nonce = param.getNonce();
- initialAssociatedText = param.getAssociatedText();
- macSize = param.getMacSize() / 8;
- keyParam = param.getKey();
- }
- else if (params instanceof ParametersWithIV)
- {
- ParametersWithIV param = (ParametersWithIV)params;
-
- nonce = param.getIV();
- initialAssociatedText = null;
- macSize = macBlock.length / 2;
- keyParam = param.getParameters();
- }
- else
- {
- throw new IllegalArgumentException("invalid parameters passed to CCM");
- }
-
- if (nonce == null || nonce.length < 7 || nonce.length > 13)
- {
- throw new IllegalArgumentException("nonce must have length from 7 to 13 octets");
- }
- }
-
- public String getAlgorithmName()
- {
- return cipher.getAlgorithmName() + "/CCM";
- }
-
- public void processAADByte(byte in)
- {
- associatedText.write(in);
- }
-
- public void processAADBytes(byte[] in, int inOff, int len)
- {
- // TODO: Process AAD online
- associatedText.write(in, inOff, len);
- }
-
- public int processByte(byte in, byte[] out, int outOff)
- throws DataLengthException, IllegalStateException
- {
- data.write(in);
-
- return 0;
- }
-
- public int processBytes(byte[] in, int inOff, int inLen, byte[] out, int outOff)
- throws DataLengthException, IllegalStateException
- {
- data.write(in, inOff, inLen);
-
- return 0;
- }
-
- public int doFinal(byte[] out, int outOff)
- throws IllegalStateException, InvalidCipherTextException
- {
- byte[] text = data.toByteArray();
- byte[] enc = processPacket(text, 0, text.length);
-
- System.arraycopy(enc, 0, out, outOff, enc.length);
-
- reset();
-
- return enc.length;
- }
-
- public void reset()
- {
- cipher.reset();
- associatedText.reset();
- data.reset();
- }
-
- /**
- * Returns a byte array containing the mac calculated as part of the
- * last encrypt or decrypt operation.
- *
- * @return the last mac calculated.
- */
- public byte[] getMac()
- {
- byte[] mac = new byte[macSize];
-
- System.arraycopy(macBlock, 0, mac, 0, mac.length);
-
- return mac;
- }
-
- public int getUpdateOutputSize(int len)
- {
- return 0;
- }
-
- public int getOutputSize(int len)
- {
- int totalData = len + data.size();
-
- if (forEncryption)
- {
- return totalData + macSize;
- }
-
- return totalData < macSize ? 0 : totalData - macSize;
- }
-
- public byte[] processPacket(byte[] in, int inOff, int inLen)
- throws IllegalStateException, InvalidCipherTextException
- {
- // TODO: handle null keyParam (e.g. via RepeatedKeySpec)
- // Need to keep the CTR and CBC Mac parts around and reset
- if (keyParam == null)
- {
- throw new IllegalStateException("CCM cipher unitialized.");
- }
-
- int n = nonce.length;
- int q = 15 - n;
- if (q < 4)
- {
- int limitLen = 1 << (8 * q);
- if (inLen >= limitLen)
- {
- throw new IllegalStateException("CCM packet too large for choice of q.");
- }
- }
-
- byte[] iv = new byte[blockSize];
- iv[0] = (byte)((q - 1) & 0x7);
- System.arraycopy(nonce, 0, iv, 1, nonce.length);
-
- BlockCipher ctrCipher = new SICBlockCipher(cipher);
- ctrCipher.init(forEncryption, new ParametersWithIV(keyParam, iv));
-
- int index = inOff;
- int outOff = 0;
- byte[] output;
-
- if (forEncryption)
- {
- output = new byte[inLen + macSize];
-
- calculateMac(in, inOff, inLen, macBlock);
-
- ctrCipher.processBlock(macBlock, 0, macBlock, 0); // S0
-
- while (index < inLen - blockSize) // S1...
- {
- ctrCipher.processBlock(in, index, output, outOff);
- outOff += blockSize;
- index += blockSize;
- }
-
- byte[] block = new byte[blockSize];
-
- System.arraycopy(in, index, block, 0, inLen - index);
-
- ctrCipher.processBlock(block, 0, block, 0);
-
- System.arraycopy(block, 0, output, outOff, inLen - index);
-
- outOff += inLen - index;
-
- System.arraycopy(macBlock, 0, output, outOff, output.length - outOff);
- }
- else
- {
- output = new byte[inLen - macSize];
-
- System.arraycopy(in, inOff + inLen - macSize, macBlock, 0, macSize);
-
- ctrCipher.processBlock(macBlock, 0, macBlock, 0);
-
- for (int i = macSize; i != macBlock.length; i++)
- {
- macBlock[i] = 0;
- }
-
- while (outOff < output.length - blockSize)
- {
- ctrCipher.processBlock(in, index, output, outOff);
- outOff += blockSize;
- index += blockSize;
- }
-
- byte[] block = new byte[blockSize];
-
- System.arraycopy(in, index, block, 0, output.length - outOff);
-
- ctrCipher.processBlock(block, 0, block, 0);
-
- System.arraycopy(block, 0, output, outOff, output.length - outOff);
-
- byte[] calculatedMacBlock = new byte[blockSize];
-
- calculateMac(output, 0, output.length, calculatedMacBlock);
-
- if (!Arrays.constantTimeAreEqual(macBlock, calculatedMacBlock))
- {
- throw new InvalidCipherTextException("mac check in CCM failed");
- }
- }
-
- return output;
- }
-
- private int calculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock)
- {
- Mac cMac = new CBCBlockCipherMac(cipher, macSize * 8);
-
- cMac.init(keyParam);
-
- //
- // build b0
- //
- byte[] b0 = new byte[16];
-
- if (hasAssociatedText())
- {
- b0[0] |= 0x40;
- }
-
- b0[0] |= (((cMac.getMacSize() - 2) / 2) & 0x7) << 3;
-
- b0[0] |= ((15 - nonce.length) - 1) & 0x7;
-
- System.arraycopy(nonce, 0, b0, 1, nonce.length);
-
- int q = dataLen;
- int count = 1;
- while (q > 0)
- {
- b0[b0.length - count] = (byte)(q & 0xff);
- q >>>= 8;
- count++;
- }
-
- cMac.update(b0, 0, b0.length);
-
- //
- // process associated text
- //
- if (hasAssociatedText())
- {
- int extra;
-
- int textLength = getAssociatedTextLength();
- if (textLength < ((1 << 16) - (1 << 8)))
- {
- cMac.update((byte)(textLength >> 8));
- cMac.update((byte)textLength);
-
- extra = 2;
- }
- else // can't go any higher than 2^32
- {
- cMac.update((byte)0xff);
- cMac.update((byte)0xfe);
- cMac.update((byte)(textLength >> 24));
- cMac.update((byte)(textLength >> 16));
- cMac.update((byte)(textLength >> 8));
- cMac.update((byte)textLength);
-
- extra = 6;
- }
-
- if (initialAssociatedText != null)
- {
- cMac.update(initialAssociatedText, 0, initialAssociatedText.length);
- }
- if (associatedText.size() > 0)
- {
- byte[] tmp = associatedText.toByteArray();
- cMac.update(tmp, 0, tmp.length);
- }
-
- extra = (extra + textLength) % 16;
- if (extra != 0)
- {
- for (int i = extra; i != 16; i++)
- {
- cMac.update((byte)0x00);
- }
- }
- }
-
- //
- // add the text
- //
- cMac.update(data, dataOff, dataLen);
-
- return cMac.doFinal(macBlock, 0);
- }
-
- private int getAssociatedTextLength()
- {
- return associatedText.size() + ((initialAssociatedText == null) ? 0 : initialAssociatedText.length);
- }
-
- private boolean hasAssociatedText()
- {
- return getAssociatedTextLength() > 0;
- }
-}
+package org.bouncycastle.crypto.modes; + +import java.io.ByteArrayOutputStream; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.macs.CBCBlockCipherMac; +import org.bouncycastle.crypto.params.AEADParameters; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.Arrays; + +/** + * Implements the Counter with Cipher Block Chaining mode (CCM) detailed in + * NIST Special Publication 800-38C. + * <p> + * <b>Note</b>: this mode is a packet mode - it needs all the data up front. + */ +public class CCMBlockCipher + implements AEADBlockCipher +{ + private BlockCipher cipher; + private int blockSize; + private boolean forEncryption; + private byte[] nonce; + private byte[] initialAssociatedText; + private int macSize; + private CipherParameters keyParam; + private byte[] macBlock; + private ByteArrayOutputStream associatedText = new ByteArrayOutputStream(); + private ByteArrayOutputStream data = new ByteArrayOutputStream(); + + /** + * Basic constructor. + * + * @param c the block cipher to be used. + */ + public CCMBlockCipher(BlockCipher c) + { + this.cipher = c; + this.blockSize = c.getBlockSize(); + this.macBlock = new byte[blockSize]; + + if (blockSize != 16) + { + throw new IllegalArgumentException("cipher required with a block size of 16."); + } + } + + /** + * return the underlying block cipher that we are wrapping. + * + * @return the underlying block cipher that we are wrapping. + */ + public BlockCipher getUnderlyingCipher() + { + return cipher; + } + + + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException + { + this.forEncryption = forEncryption; + + if (params instanceof AEADParameters) + { + AEADParameters param = (AEADParameters)params; + + nonce = param.getNonce(); + initialAssociatedText = param.getAssociatedText(); + macSize = param.getMacSize() / 8; + keyParam = param.getKey(); + } + else if (params instanceof ParametersWithIV) + { + ParametersWithIV param = (ParametersWithIV)params; + + nonce = param.getIV(); + initialAssociatedText = null; + macSize = macBlock.length / 2; + keyParam = param.getParameters(); + } + else + { + throw new IllegalArgumentException("invalid parameters passed to CCM"); + } + + if (nonce == null || nonce.length < 7 || nonce.length > 13) + { + throw new IllegalArgumentException("nonce must have length from 7 to 13 octets"); + } + } + + public String getAlgorithmName() + { + return cipher.getAlgorithmName() + "/CCM"; + } + + public void processAADByte(byte in) + { + associatedText.write(in); + } + + public void processAADBytes(byte[] in, int inOff, int len) + { + // TODO: Process AAD online + associatedText.write(in, inOff, len); + } + + public int processByte(byte in, byte[] out, int outOff) + throws DataLengthException, IllegalStateException + { + data.write(in); + + return 0; + } + + public int processBytes(byte[] in, int inOff, int inLen, byte[] out, int outOff) + throws DataLengthException, IllegalStateException + { + data.write(in, inOff, inLen); + + return 0; + } + + public int doFinal(byte[] out, int outOff) + throws IllegalStateException, InvalidCipherTextException + { + byte[] text = data.toByteArray(); + byte[] enc = processPacket(text, 0, text.length); + + System.arraycopy(enc, 0, out, outOff, enc.length); + + reset(); + + return enc.length; + } + + public void reset() + { + cipher.reset(); + associatedText.reset(); + data.reset(); + } + + /** + * Returns a byte array containing the mac calculated as part of the + * last encrypt or decrypt operation. + * + * @return the last mac calculated. + */ + public byte[] getMac() + { + byte[] mac = new byte[macSize]; + + System.arraycopy(macBlock, 0, mac, 0, mac.length); + + return mac; + } + + public int getUpdateOutputSize(int len) + { + return 0; + } + + public int getOutputSize(int len) + { + int totalData = len + data.size(); + + if (forEncryption) + { + return totalData + macSize; + } + + return totalData < macSize ? 0 : totalData - macSize; + } + + public byte[] processPacket(byte[] in, int inOff, int inLen) + throws IllegalStateException, InvalidCipherTextException + { + // TODO: handle null keyParam (e.g. via RepeatedKeySpec) + // Need to keep the CTR and CBC Mac parts around and reset + if (keyParam == null) + { + throw new IllegalStateException("CCM cipher unitialized."); + } + + int n = nonce.length; + int q = 15 - n; + if (q < 4) + { + int limitLen = 1 << (8 * q); + if (inLen >= limitLen) + { + throw new IllegalStateException("CCM packet too large for choice of q."); + } + } + + byte[] iv = new byte[blockSize]; + iv[0] = (byte)((q - 1) & 0x7); + System.arraycopy(nonce, 0, iv, 1, nonce.length); + + BlockCipher ctrCipher = new SICBlockCipher(cipher); + ctrCipher.init(forEncryption, new ParametersWithIV(keyParam, iv)); + + int index = inOff; + int outOff = 0; + byte[] output; + + if (forEncryption) + { + output = new byte[inLen + macSize]; + + calculateMac(in, inOff, inLen, macBlock); + + ctrCipher.processBlock(macBlock, 0, macBlock, 0); // S0 + + while (index < inLen - blockSize) // S1... + { + ctrCipher.processBlock(in, index, output, outOff); + outOff += blockSize; + index += blockSize; + } + + byte[] block = new byte[blockSize]; + + System.arraycopy(in, index, block, 0, inLen - index); + + ctrCipher.processBlock(block, 0, block, 0); + + System.arraycopy(block, 0, output, outOff, inLen - index); + + outOff += inLen - index; + + System.arraycopy(macBlock, 0, output, outOff, output.length - outOff); + } + else + { + output = new byte[inLen - macSize]; + + System.arraycopy(in, inOff + inLen - macSize, macBlock, 0, macSize); + + ctrCipher.processBlock(macBlock, 0, macBlock, 0); + + for (int i = macSize; i != macBlock.length; i++) + { + macBlock[i] = 0; + } + + while (outOff < output.length - blockSize) + { + ctrCipher.processBlock(in, index, output, outOff); + outOff += blockSize; + index += blockSize; + } + + byte[] block = new byte[blockSize]; + + System.arraycopy(in, index, block, 0, output.length - outOff); + + ctrCipher.processBlock(block, 0, block, 0); + + System.arraycopy(block, 0, output, outOff, output.length - outOff); + + byte[] calculatedMacBlock = new byte[blockSize]; + + calculateMac(output, 0, output.length, calculatedMacBlock); + + if (!Arrays.constantTimeAreEqual(macBlock, calculatedMacBlock)) + { + throw new InvalidCipherTextException("mac check in CCM failed"); + } + } + + return output; + } + + private int calculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock) + { + Mac cMac = new CBCBlockCipherMac(cipher, macSize * 8); + + cMac.init(keyParam); + + // + // build b0 + // + byte[] b0 = new byte[16]; + + if (hasAssociatedText()) + { + b0[0] |= 0x40; + } + + b0[0] |= (((cMac.getMacSize() - 2) / 2) & 0x7) << 3; + + b0[0] |= ((15 - nonce.length) - 1) & 0x7; + + System.arraycopy(nonce, 0, b0, 1, nonce.length); + + int q = dataLen; + int count = 1; + while (q > 0) + { + b0[b0.length - count] = (byte)(q & 0xff); + q >>>= 8; + count++; + } + + cMac.update(b0, 0, b0.length); + + // + // process associated text + // + if (hasAssociatedText()) + { + int extra; + + int textLength = getAssociatedTextLength(); + if (textLength < ((1 << 16) - (1 << 8))) + { + cMac.update((byte)(textLength >> 8)); + cMac.update((byte)textLength); + + extra = 2; + } + else // can't go any higher than 2^32 + { + cMac.update((byte)0xff); + cMac.update((byte)0xfe); + cMac.update((byte)(textLength >> 24)); + cMac.update((byte)(textLength >> 16)); + cMac.update((byte)(textLength >> 8)); + cMac.update((byte)textLength); + + extra = 6; + } + + if (initialAssociatedText != null) + { + cMac.update(initialAssociatedText, 0, initialAssociatedText.length); + } + if (associatedText.size() > 0) + { + byte[] tmp = associatedText.toByteArray(); + cMac.update(tmp, 0, tmp.length); + } + + extra = (extra + textLength) % 16; + if (extra != 0) + { + for (int i = extra; i != 16; i++) + { + cMac.update((byte)0x00); + } + } + } + + // + // add the text + // + cMac.update(data, dataOff, dataLen); + + return cMac.doFinal(macBlock, 0); + } + + private int getAssociatedTextLength() + { + return associatedText.size() + ((initialAssociatedText == null) ? 0 : initialAssociatedText.length); + } + + private boolean hasAssociatedText() + { + return getAssociatedTextLength() > 0; + } +} diff --git a/src/main/java/org/bouncycastle/crypto/params/DSAParameterGenerationParameters.java b/src/main/java/org/bouncycastle/crypto/params/DSAParameterGenerationParameters.java new file mode 100644 index 00000000..ba841b8e --- /dev/null +++ b/src/main/java/org/bouncycastle/crypto/params/DSAParameterGenerationParameters.java @@ -0,0 +1,80 @@ +package org.bouncycastle.crypto.params; + +import java.security.SecureRandom; + +public class DSAParameterGenerationParameters +{ + public static final int DIGITAL_SIGNATURE_USAGE = 1; + public static final int KEY_ESTABLISHMENT_USAGE = 2; + + private final int l; + private final int n; + private final int usageIndex; + private final int certainty; + private final SecureRandom random; + + /** + * Construct without a usage index, this will do a random construction of G. + * + * @param L desired length of prime P in bits (the effective key size). + * @param N desired length of prime Q in bits. + * @param certainty certainty level for prime number generation. + * @param random the source of randomness to use. + */ + public DSAParameterGenerationParameters( + int L, + int N, + int certainty, + SecureRandom random) + { + this(L, N, certainty, random, -1); + } + + /** + * Construct for a specific usage index - this has the effect of using verifiable canonical generation of G. + * + * @param L desired length of prime P in bits (the effective key size). + * @param N desired length of prime Q in bits. + * @param certainty certainty level for prime number generation. + * @param random the source of randomness to use. + * @param usageIndex a valid usage index. + */ + public DSAParameterGenerationParameters( + int L, + int N, + int certainty, + SecureRandom random, + int usageIndex) + { + this.l = L; + this.n = N; + this.certainty = certainty; + this.usageIndex = usageIndex; + this.random = random; + } + + public int getL() + { + return l; + } + + public int getN() + { + return n; + } + + public int getCertainty() + { + return certainty; + } + + public SecureRandom getRandom() + { + return random; + } + + public int getUsageIndex() + { + return usageIndex; + } +} diff --git a/src/main/java/org/bouncycastle/crypto/params/DSAValidationParameters.java b/src/main/java/org/bouncycastle/crypto/params/DSAValidationParameters.java index 1cc4b93e..07d93d07 100644 --- a/src/main/java/org/bouncycastle/crypto/params/DSAValidationParameters.java +++ b/src/main/java/org/bouncycastle/crypto/params/DSAValidationParameters.java @@ -4,6 +4,7 @@ import org.bouncycastle.util.Arrays; public class DSAValidationParameters { + private int usageIndex; private byte[] seed; private int counter; @@ -11,8 +12,17 @@ public class DSAValidationParameters byte[] seed, int counter) { + this(seed, counter, -1); + } + + public DSAValidationParameters( + byte[] seed, + int counter, + int usageIndex) + { this.seed = seed; this.counter = counter; + this.usageIndex = usageIndex; } public int getCounter() @@ -25,6 +35,11 @@ public class DSAValidationParameters return seed; } + public int getUsageIndex() + { + return usageIndex; + } + public int hashCode() { return counter ^ Arrays.hashCode(seed); diff --git a/src/main/java/org/bouncycastle/crypto/prng/BasicEntropySourceProvider.java b/src/main/java/org/bouncycastle/crypto/prng/BasicEntropySourceProvider.java index 38527107..9f1d0427 100644 --- a/src/main/java/org/bouncycastle/crypto/prng/BasicEntropySourceProvider.java +++ b/src/main/java/org/bouncycastle/crypto/prng/BasicEntropySourceProvider.java @@ -43,6 +43,11 @@ public class BasicEntropySourceProvider { return _sr.generateSeed((bitsRequired + 7) / 8); } + + public int entropySize() + { + return bitsRequired; + } }; } } diff --git a/src/main/java/org/bouncycastle/crypto/prng/DRBGProvider.java b/src/main/java/org/bouncycastle/crypto/prng/DRBGProvider.java new file mode 100644 index 00000000..c39760c9 --- /dev/null +++ b/src/main/java/org/bouncycastle/crypto/prng/DRBGProvider.java @@ -0,0 +1,8 @@ +package org.bouncycastle.crypto.prng; + +import org.bouncycastle.crypto.prng.drbg.SP80090DRBG; + +interface DRBGProvider +{ + SP80090DRBG get(EntropySource entropySource); +} diff --git a/src/main/java/org/bouncycastle/crypto/prng/EntropySource.java b/src/main/java/org/bouncycastle/crypto/prng/EntropySource.java index 56a19711..53bc549d 100644 --- a/src/main/java/org/bouncycastle/crypto/prng/EntropySource.java +++ b/src/main/java/org/bouncycastle/crypto/prng/EntropySource.java @@ -2,7 +2,24 @@ package org.bouncycastle.crypto.prng; public interface EntropySource { + /** + * Return whether or not this entropy source is regarded as prediction resistant. + * + * @return true if it is, false otherwise. + */ boolean isPredictionResistant(); + /** + * Return a byte array of entropy. + * + * @return entropy bytes. + */ byte[] getEntropy(); + + /** + * Return the number of bits of entropy this source can produce. + * + * @return size in bits of the return value of getEntropy. + */ + int entropySize(); } diff --git a/src/main/java/org/bouncycastle/crypto/prng/SP80090DRBG.java b/src/main/java/org/bouncycastle/crypto/prng/SP80090DRBG.java deleted file mode 100644 index 9fbd88f7..00000000 --- a/src/main/java/org/bouncycastle/crypto/prng/SP80090DRBG.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.bouncycastle.crypto.prng; - -public interface SP80090DRBG -{ - - int generate(byte[] output, byte[] additionalInput, boolean predictionResistant); - - void reseed(byte[] additionalInput); -} diff --git a/src/main/java/org/bouncycastle/crypto/random/SP800SecureRandom.java b/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandom.java index 0b54ac54..e1ec6c28 100644 --- a/src/main/java/org/bouncycastle/crypto/random/SP800SecureRandom.java +++ b/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandom.java @@ -1,9 +1,8 @@ -package org.bouncycastle.crypto.random; +package org.bouncycastle.crypto.prng; import java.security.SecureRandom; -import org.bouncycastle.crypto.prng.EntropySource; -import org.bouncycastle.crypto.prng.SP80090DRBG; +import org.bouncycastle.crypto.prng.drbg.SP80090DRBG; public class SP800SecureRandom extends SecureRandom @@ -55,7 +54,12 @@ public class SP800SecureRandom drbg = drbgProvider.get(entropySource); } - drbg.generate(bytes, null, predictionResistant); + // check if a reseed is required... + if (drbg.generate(bytes, null, predictionResistant) < 0) + { + drbg.reseed(entropySource.getEntropy()); + drbg.generate(bytes, null, predictionResistant); + } } } diff --git a/src/main/java/org/bouncycastle/crypto/random/SP800SecureRandomBuilder.java b/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java index 10bd3505..66f05c5f 100644 --- a/src/main/java/org/bouncycastle/crypto/random/SP800SecureRandomBuilder.java +++ b/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java @@ -1,18 +1,15 @@ -package org.bouncycastle.crypto.random; +package org.bouncycastle.crypto.prng; import java.security.SecureRandom; import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.Mac; -import org.bouncycastle.crypto.prng.BasicEntropySourceProvider; -import org.bouncycastle.crypto.prng.CTRSP800DRBG; -import org.bouncycastle.crypto.prng.DualECSP800DRBG; -import org.bouncycastle.crypto.prng.EntropySource; -import org.bouncycastle.crypto.prng.EntropySourceProvider; -import org.bouncycastle.crypto.prng.HMacSP800DRBG; -import org.bouncycastle.crypto.prng.HashSP800DRBG; -import org.bouncycastle.crypto.prng.SP80090DRBG; +import org.bouncycastle.crypto.prng.drbg.CTRSP800DRBG; +import org.bouncycastle.crypto.prng.drbg.DualECSP800DRBG; +import org.bouncycastle.crypto.prng.drbg.HMacSP800DRBG; +import org.bouncycastle.crypto.prng.drbg.HashSP800DRBG; +import org.bouncycastle.crypto.prng.drbg.SP80090DRBG; /** * Builder class for making SecureRandom objects based on SP 800-90A Deterministic Random Bit Generators (DRBG). @@ -31,7 +28,7 @@ public class SP800SecureRandomBuilder * predictionResistant set to false. * <p> * Any SecureRandom created from a builder constructed like this will make use of input passed to SecureRandom.setSeed() if - * the passed in SecureRandom does. + * the default SecureRandom does for its generateSeed() call. * </p> */ public SP800SecureRandomBuilder() @@ -44,7 +41,7 @@ public class SP800SecureRandomBuilder * for prediction resistance. * <p> * Any SecureRandom created from a builder constructed like this will make use of input passed to SecureRandom.setSeed() if - * the passed in SecureRandom does. + * the passed in SecureRandom does for its generateSeed() call. * </p> * @param entropySource * @param predictionResistant @@ -177,7 +174,7 @@ public class SP800SecureRandomBuilder public SP80090DRBG get(EntropySource entropySource) { - return new HashSP800DRBG(digest, entropySource, nonce, personalizationString, securityStrength); + return new HashSP800DRBG(digest, securityStrength, entropySource, personalizationString, nonce); } } @@ -199,7 +196,7 @@ public class SP800SecureRandomBuilder public SP80090DRBG get(EntropySource entropySource) { - return new DualECSP800DRBG(digest, entropySource, nonce, personalizationString, securityStrength); + return new DualECSP800DRBG(digest, securityStrength, entropySource, personalizationString, nonce); } } @@ -221,7 +218,7 @@ public class SP800SecureRandomBuilder public SP80090DRBG get(EntropySource entropySource) { - return new HMacSP800DRBG(hMac, entropySource, nonce, personalizationString, securityStrength); + return new HMacSP800DRBG(hMac, securityStrength, entropySource, personalizationString, nonce); } } @@ -246,7 +243,7 @@ public class SP800SecureRandomBuilder public SP80090DRBG get(EntropySource entropySource) { - return new CTRSP800DRBG(blockCipher, keySizeInBits, entropySource, nonce, personalizationString, securityStrength); + return new CTRSP800DRBG(blockCipher, keySizeInBits, securityStrength, entropySource, personalizationString, nonce); } } } diff --git a/src/main/java/org/bouncycastle/crypto/prng/VMPCRandomGenerator.java b/src/main/java/org/bouncycastle/crypto/prng/VMPCRandomGenerator.java index 9f9ff7b2..b882b796 100644 --- a/src/main/java/org/bouncycastle/crypto/prng/VMPCRandomGenerator.java +++ b/src/main/java/org/bouncycastle/crypto/prng/VMPCRandomGenerator.java @@ -124,5 +124,4 @@ public class VMPCRandomGenerator implements RandomGenerator }
}
}
-
}
diff --git a/src/main/java/org/bouncycastle/crypto/prng/CTRSP800DRBG.java b/src/main/java/org/bouncycastle/crypto/prng/drbg/CTRSP800DRBG.java index 8311e079..84fe4a40 100644 --- a/src/main/java/org/bouncycastle/crypto/prng/CTRSP800DRBG.java +++ b/src/main/java/org/bouncycastle/crypto/prng/drbg/CTRSP800DRBG.java @@ -1,14 +1,23 @@ -package org.bouncycastle.crypto.prng; +package org.bouncycastle.crypto.prng.drbg; import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.prng.EntropySource; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; +/** + * A SP800-90A CTR DRBG. + */ public class CTRSP800DRBG implements SP80090DRBG { - private EntropySource _entropySource; + private static final long TDEA_RESEED_MAX = 1L << (32 - 1); + private static final long AES_RESEED_MAX = 1L << (48 - 1); + private static final int TDEA_MAX_BITS_REQUEST = 1 << (13 - 1); + private static final int AES_MAX_BITS_REQUEST = 1 << (19 - 1); + + private EntropySource _entropySource; private BlockCipher _engine; private int _keySizeInBits; private int _seedLength; @@ -16,62 +25,64 @@ public class CTRSP800DRBG // internal state private byte[] _Key; private byte[] _V; - private int _reseedCounter = 0; - - public CTRSP800DRBG(BlockCipher engine, int keySizeInBits, EntropySource entropySource, byte[] nonce, - byte[] personalisationString, int securityStrength) + private long _reseedCounter = 0; + private boolean _isTDEA = false; + + /** + * Construct a SP800-90A CTR DRBG. + * <p> + * Minimum entropy requirement is the security strength requested. + * </p> + * @param engine underlying block cipher to use to support DRBG + * @param keySizeInBits size of the key to use with the block cipher. + * @param securityStrength security strength required (in bits) + * @param entropySource source of entropy to use for seeding/reseeding. + * @param personalizationString personalization string to distinguish this DRBG (may be null). + * @param nonce nonce to further distinguish this DRBG (may be null). + */ + public CTRSP800DRBG(BlockCipher engine, int keySizeInBits, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce) { - _entropySource = entropySource; _engine = engine; _keySizeInBits = keySizeInBits; _seedLength = keySizeInBits + engine.getBlockSize() * 8; + _isTDEA = isTDEA(engine); - int entropyLengthInBytes = securityStrength; - if (securityStrength > 256) { - throw new IllegalStateException( - "Security strength is not supported by the derivation function"); + throw new IllegalArgumentException("Requested security strength is not supported by the derivation function"); + } + + if (getMaxSecurityStrength(engine, keySizeInBits) < securityStrength) + { + throw new IllegalArgumentException("Requested security strength is not supported by block cipher and key size"); } - - byte[] entropy = entropySource.getEntropy(); // Get_entropy_input - System.out.println("Constructor Entropy: " + new String(Hex.encode(entropy))); + if (entropySource.entropySize() < securityStrength) + { + throw new IllegalArgumentException("Not enough entropy for security strength required"); + } - CTR_DRBG_Instantiate_algorithm(entropy, nonce, personalisationString); - - System.err.println("Constructor V : " + new String(Hex.encode(_V))); - System.err.println("Constructor Key: " + new String(Hex.encode(_Key))); + byte[] entropy = entropySource.getEntropy(); // Get_entropy_input + CTR_DRBG_Instantiate_algorithm(entropy, nonce, personalizationString); } private void CTR_DRBG_Instantiate_algorithm(byte[] entropy, byte[] nonce, byte[] personalisationString) { - if (personalisationString == null) - { - personalisationString = new byte[0]; - } - byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalisationString); - - System.out.println("Constructor SeedMaterial: " + new String(Hex.encode(seedMaterial))); - byte[] seed = Block_Cipher_df(seedMaterial, _seedLength); - System.out.println("Constructor Seed: " + new String(Hex.encode(seed))); - int outlen = _engine.getBlockSize(); _Key = new byte[(_keySizeInBits + 7) / 8]; _V = new byte[outlen]; + // _Key & _V are modified by this call CTR_DRBG_Update(seed, _Key, _V); - // _Key & _V are modified by this call - System.out.println("Key: " + new String(Hex.encode(_Key))); - System.out.println("V : " + new String(Hex.encode(_V))); + _reseedCounter = 1; } @@ -83,7 +94,7 @@ public class CTRSP800DRBG int i=0; int outLen = _engine.getBlockSize(); - _engine.init(true, new KeyParameter(key)); + _engine.init(true, new KeyParameter(expandKey(key))); while (i*outLen < seed.length) { addOneTo(v); @@ -95,18 +106,22 @@ public class CTRSP800DRBG System.arraycopy(outputBlock, 0, temp, i * outLen, bytesToCopy); ++i; } - System.err.println("seed: " + new String(Hex.encode(seed))); - System.err.println("temp: " + new String(Hex.encode(temp))); + XOR(temp, seed, temp, 0); - System.err.println("temp: " + new String(Hex.encode(temp))); + System.arraycopy(temp, 0, key, 0, key.length); System.arraycopy(temp, key.length, v, 0, v.length); } private void CTR_DRBG_Reseed_algorithm(EntropySource entropy, byte[] additionalInput) { - - + byte[] seedMaterial = Arrays.concatenate(entropy.getEntropy(), additionalInput); + + seedMaterial = Block_Cipher_df(seedMaterial, _seedLength); + + CTR_DRBG_Update(seedMaterial, _Key, _V); + + _reseedCounter = 1; } private void XOR(byte[] out, byte[] a, byte[] b, int bOff) @@ -131,16 +146,6 @@ public class CTRSP800DRBG // -- Internal state migration --- private static final byte[] K_BITS = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); - - private byte[] getBytes(byte[] input) - { - byte[] K = new byte[_keySizeInBits / 8]; - System.arraycopy(K_BITS, 0, K, 0, K.length); - _engine.init(true, new KeyParameter(K)); - byte[] out = new byte[_engine.getBlockSize()]; - _engine.processBlock(input, 0, out, 0); - return out; - } // 1. If (number_of_bits_to_return > max_number_of_bits), then return an // ERROR_FLAG. @@ -216,7 +221,7 @@ public class CTRSP800DRBG System.arraycopy(inputString, 0, S, 8, L); S[8 + L] = (byte)0x80; // S already padded with zeros - System.err.println("S :" + new String(Hex.encode(S))); + byte[] temp = new byte[_keySizeInBits / 8 + outLen]; byte[] bccOut = new byte[outLen]; @@ -246,7 +251,7 @@ public class CTRSP800DRBG temp = new byte[bitLength / 2]; i = 0; - _engine.init(true, new KeyParameter(K)); + _engine.init(true, new KeyParameter(expandKey(K))); while (i * outLen < temp.length) { @@ -263,7 +268,6 @@ public class CTRSP800DRBG return temp; } - /* * 1. chaining_value = 0^outlen * . Comment: Set the first chaining value to outlen zeros. @@ -284,7 +288,7 @@ public class CTRSP800DRBG byte[] inputBlock = new byte[outlen]; - _engine.init(true, new KeyParameter(k)); + _engine.init(true, new KeyParameter(expandKey(k))); _engine.processBlock(iV, 0, chainingValue, 0); @@ -305,11 +309,46 @@ public class CTRSP800DRBG buf[offSet + 3] = ((byte)(value)); } + /** + * Populate a passed in array with random data. + * + * @param output output array for generated bits. + * @param additionalInput additional input to be added to the DRBG in this step. + * @param predictionResistant true if a reseed should be forced, false otherwise. + * + * @return number of bits generated, -1 if a reseed required. + */ public int generate(byte[] output, byte[] additionalInput, boolean predictionResistant) { + if (_isTDEA) + { + if (_reseedCounter > TDEA_RESEED_MAX) + { + return -1; + } + + if (Utils.isTooLarge(output, TDEA_MAX_BITS_REQUEST / 8)) + { + throw new IllegalArgumentException("Number of bits per request limited to " + TDEA_MAX_BITS_REQUEST); + } + } + else + { + if (_reseedCounter > AES_RESEED_MAX) + { + return -1; + } + + if (Utils.isTooLarge(output, AES_MAX_BITS_REQUEST / 8)) + { + throw new IllegalArgumentException("Number of bits per request limited to " + AES_MAX_BITS_REQUEST); + } + } + if (predictionResistant) { CTR_DRBG_Reseed_algorithm(_entropySource, additionalInput); + additionalInput = null; } if (additionalInput != null) @@ -324,7 +363,7 @@ public class CTRSP800DRBG byte[] out = new byte[_V.length]; - _engine.init(true, new KeyParameter(_Key)); + _engine.init(true, new KeyParameter(expandKey(_Key))); for (int i = 0; i < output.length / out.length; i++) { @@ -339,7 +378,6 @@ public class CTRSP800DRBG System.arraycopy(out, 0, output, i * out.length, bytesToCopy); } - CTR_DRBG_Update(additionalInput, _Key, _V); _reseedCounter++; @@ -347,8 +385,84 @@ public class CTRSP800DRBG return output.length * 8; } + /** + * Reseed the DRBG. + * + * @param additionalInput additional input to be added to the DRBG in this step. + */ public void reseed(byte[] additionalInput) { CTR_DRBG_Reseed_algorithm(_entropySource, additionalInput); } + + private boolean isTDEA(BlockCipher cipher) + { + return cipher.getAlgorithmName().equals("DESede") || cipher.getAlgorithmName().equals("TDEA"); + } + + private int getMaxSecurityStrength(BlockCipher cipher, int keySizeInBits) + { + if (isTDEA(cipher) && keySizeInBits == 168) + { + return 112; + } + if (cipher.getAlgorithmName().equals("AES")) + { + return keySizeInBits; + } + + return -1; + } + + byte[] expandKey(byte[] key) + { + if (_isTDEA) + { + // expand key to 192 bits. + byte[] tmp = new byte[24]; + + padKey(key, 0, tmp, 0); + padKey(key, 7, tmp, 8); + padKey(key, 14, tmp, 16); + + return tmp; + } + else + { + return key; + } + } + + /** + * Pad out a key for TDEA, setting odd parity for each byte. + * + * @param keyMaster + * @param keyOff + * @param tmp + * @param tmpOff + */ + private void padKey(byte[] keyMaster, int keyOff, byte[] tmp, int tmpOff) + { + tmp[tmpOff + 0] = (byte)(keyMaster[keyOff + 0] & 0xfe); + tmp[tmpOff + 1] = (byte)((keyMaster[keyOff + 0] << 7) | ((keyMaster[keyOff + 1] & 0xfc) >>> 1)); + tmp[tmpOff + 2] = (byte)((keyMaster[keyOff + 1] << 6) | ((keyMaster[keyOff + 2] & 0xf8) >>> 2)); + tmp[tmpOff + 3] = (byte)((keyMaster[keyOff + 2] << 5) | ((keyMaster[keyOff + 3] & 0xf0) >>> 3)); + tmp[tmpOff + 4] = (byte)((keyMaster[keyOff + 3] << 4) | ((keyMaster[keyOff + 4] & 0xe0) >>> 4)); + tmp[tmpOff + 5] = (byte)((keyMaster[keyOff + 4] << 3) | ((keyMaster[keyOff + 5] & 0xc0) >>> 5)); + tmp[tmpOff + 6] = (byte)((keyMaster[keyOff + 5] << 2) | ((keyMaster[keyOff + 6] & 0x80) >>> 6)); + tmp[tmpOff + 7] = (byte)(keyMaster[keyOff + 6] << 1); + + for (int i = tmpOff; i <= tmpOff + 7; i++) + { + int b = tmp[i]; + tmp[i] = (byte)((b & 0xfe) | + ((((b >> 1) ^ + (b >> 2) ^ + (b >> 3) ^ + (b >> 4) ^ + (b >> 5) ^ + (b >> 6) ^ + (b >> 7)) ^ 0x01) & 0x01)); + } + } } diff --git a/src/main/java/org/bouncycastle/crypto/prng/DualECSP800DRBG.java b/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java index caeac7a0..3cee39cb 100644 --- a/src/main/java/org/bouncycastle/crypto/prng/DualECSP800DRBG.java +++ b/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java @@ -1,15 +1,19 @@ -package org.bouncycastle.crypto.prng; +package org.bouncycastle.crypto.prng.drbg; import java.math.BigInteger; import org.bouncycastle.asn1.nist.NISTNamedCurves; import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.prng.EntropySource; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECFieldElement; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; +/** + * A SP800-90A Dual EC DRBG. + */ public class DualECSP800DRBG implements SP80090DRBG { @@ -31,10 +35,13 @@ public class DualECSP800DRBG private static final BigInteger p521_Qx = new BigInteger("1b9fa3e518d683c6b65763694ac8efbaec6fab44f2276171a42726507dd08add4c3b3f4c1ebc5b1222ddba077f722943b24c3edfa0f85fe24d0c8c01591f0be6f63", 16); private static final BigInteger p521_Qy = new BigInteger("1f3bdba585295d9a1110d1df1f9430ef8442c5018976ff3437ef91b81dc0b8132c8d5c39c32d0e004a3092b7d327c0e7a4d26d2c7b69b58f9066652911e457779de", 16); - private final static int RESEED_MAX = 100000; + private static final long RESEED_MAX = 1L << (32 - 1); + private static final int MAX_ADDITIONAL_INPUT = 1 << (13 - 1); + private static final int MAX_ENTROPY_LENGTH = 1 << (13 - 1); + private static final int MAX_PERSONALIZATION_STRING = 1 << (13 -1); private Digest _digest; - private int _reseedCounter; + private long _reseedCounter; private EntropySource _entropySource; private int _securityStrength; private int _seedlen; @@ -43,28 +50,44 @@ public class DualECSP800DRBG private ECPoint _P; private ECPoint _Q; private byte[] _s; - private int _sLength; - - public DualECSP800DRBG(Digest digest, EntropySource entropySource, byte[] nonce, - byte[] personalisationString, int securityStrength) + private int _sLength; + + /** + * Construct a SP800-90A Dual EC DRBG. + * <p> + * Minimum entropy requirement is the security strength requested. + * </p> + * @param digest source digest to use with the DRB stream. + * @param securityStrength security strength required (in bits) + * @param entropySource source of entropy to use for seeding/reseeding. + * @param personalizationString personalization string to distinguish this DRBG (may be null). + * @param nonce nonce to further distinguish this DRBG (may be null). + */ + public DualECSP800DRBG(Digest digest, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce) { - if (securityStrength > digest.getDigestSize() * 8) // TODO: this may, or may not be correct, but it's good enough for now - { - throw new IllegalStateException( - "Security strength is not supported by the derivation function"); - } - - // TODO: validate digest choice _digest = digest; _entropySource = entropySource; _securityStrength = securityStrength; - // TODO: validate entropy length + if (Utils.isTooLarge(personalizationString, MAX_PERSONALIZATION_STRING / 8)) + { + throw new IllegalArgumentException("Personalization string too large"); + } + + if (entropySource.entropySize() < securityStrength || entropySource.entropySize() > MAX_ENTROPY_LENGTH) + { + throw new IllegalArgumentException("EntropySource must provide between " + securityStrength + " and " + MAX_ENTROPY_LENGTH + " bits"); + } + byte[] entropy = entropySource.getEntropy(); - byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalisationString); + byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalizationString); if (securityStrength <= 128) { + if (Utils.getMaxSecurityStrength(digest) < 128) + { + throw new IllegalArgumentException("Requested security strength is not supported by digest"); + } _seedlen = 256; _outlen = 240 / 8; _curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-256").getCurve(); @@ -73,6 +96,10 @@ public class DualECSP800DRBG } else if (securityStrength <= 192) { + if (Utils.getMaxSecurityStrength(digest) < 192) + { + throw new IllegalArgumentException("Requested security strength is not supported by digest"); + } _seedlen = 384; _outlen = 368 / 8; _curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-384").getCurve(); @@ -81,6 +108,10 @@ public class DualECSP800DRBG } else if (securityStrength <= 256) { + if (Utils.getMaxSecurityStrength(digest) < 256) + { + throw new IllegalArgumentException("Requested security strength is not supported by digest"); + } _seedlen = 521; _outlen = 504 / 8; _curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-521").getCurve(); @@ -92,19 +123,37 @@ public class DualECSP800DRBG throw new IllegalArgumentException("security strength cannot be greater than 256 bits"); } - _s = hash_df(seedMaterial, _seedlen); + _s = Utils.hash_df(_digest, seedMaterial, _seedlen); _sLength = _s.length; - //System.err.println(new String(Hex.encode(_s))); _reseedCounter = 0; - } + /** + * Populate a passed in array with random data. + * + * @param output output array for generated bits. + * @param additionalInput additional input to be added to the DRBG in this step. + * @param predictionResistant true if a reseed should be forced, false otherwise. + * + * @return number of bits generated, -1 if a reseed required. + */ public int generate(byte[] output, byte[] additionalInput, boolean predictionResistant) { int numberOfBits = output.length*8; - - if (predictionResistant || _reseedCounter > RESEED_MAX) + int m = output.length / _outlen; + + if (Utils.isTooLarge(additionalInput, MAX_ADDITIONAL_INPUT / 8)) + { + throw new IllegalArgumentException("Additional input too large"); + } + + if (_reseedCounter + m > RESEED_MAX) + { + return -1; + } + + if (predictionResistant) { reseed(additionalInput); additionalInput = null; @@ -113,11 +162,9 @@ public class DualECSP800DRBG if (additionalInput != null) { // Note: we ignore the use of pad8 on the additional input as we mandate byte arrays for it. - additionalInput = hash_df(additionalInput, _seedlen); + additionalInput = Utils.hash_df(_digest, additionalInput, _seedlen); } - int m = output.length / _outlen; - for (int i = 0; i < m; i++) { BigInteger t = new BigInteger(1, xor(_s, additionalInput)); @@ -139,6 +186,8 @@ public class DualECSP800DRBG //System.err.println("R: " + new String(Hex.encode(r))); additionalInput = null; + + _reseedCounter++; } if (m * _outlen < output.length) @@ -155,84 +204,29 @@ public class DualECSP800DRBG // Need to preserve length of S as unsigned int. _s = BigIntegers.asUnsignedByteArray(_sLength, _P.multiply(new BigInteger(1, _s)).getX().toBigInteger()); - _reseedCounter++; - return numberOfBits; } + /** + * Reseed the DRBG. + * + * @param additionalInput additional input to be added to the DRBG in this step. + */ public void reseed(byte[] additionalInput) { - if (additionalInput == null) + if (Utils.isTooLarge(additionalInput, MAX_ADDITIONAL_INPUT / 8)) { - additionalInput = new byte[0]; + throw new IllegalArgumentException("Additional input string too large"); } byte[] entropy = _entropySource.getEntropy(); byte[] seedMaterial = Arrays.concatenate(pad8(_s, _seedlen), entropy, additionalInput); - _s = hash_df(seedMaterial, _seedlen); + _s = Utils.hash_df(_digest, seedMaterial, _seedlen); _reseedCounter = 0; } - // 1. temp = the Null string. - // 2. . - // 3. counter = an 8-bit binary value representing the integer "1". - // 4. For i = 1 to len do - // Comment : In step 4.1, no_of_bits_to_return - // is used as a 32-bit string. - // 4.1 temp = temp || Hash (counter || no_of_bits_to_return || - // input_string). - // 4.2 counter = counter + 1. - // 5. requested_bits = Leftmost (no_of_bits_to_return) of temp. - // 6. Return SUCCESS and requested_bits. - private byte[] hash_df(byte[] seedMaterial, int seedLength) - { - byte[] temp = new byte[(seedLength + 7) / 8]; - - int len = temp.length / _digest.getDigestSize(); - int counter = 1; - - byte[] dig = new byte[_digest.getDigestSize()]; - - for (int i = 0; i <= len; i++) - { - _digest.update((byte)counter); - - _digest.update((byte)(seedLength >> 24)); - _digest.update((byte)(seedLength >> 16)); - _digest.update((byte)(seedLength >> 8)); - _digest.update((byte)seedLength); - - _digest.update(seedMaterial, 0, seedMaterial.length); - - _digest.doFinal(dig, 0); - - int bytesToCopy = ((temp.length - i * dig.length) > dig.length) - ? dig.length - : (temp.length - i * dig.length); - System.arraycopy(dig, 0, temp, i * dig.length, bytesToCopy); - - counter++; - } - - // do a left shift to get rid of excess bits. - if (seedLength % 8 != 0) - { - int shift = 8 - (seedLength % 8); - int carry = 0; - - for (int i = 0; i != temp.length; i++) - { - int b = temp[i] & 0xff; - temp[i] = (byte)((b >>> shift) | (carry << (8 - shift))); - carry = b; - } - } - - return temp; - } - private byte[] xor(byte[] a, byte[] b) { if (b == null) diff --git a/src/main/java/org/bouncycastle/crypto/prng/HMacSP800DRBG.java b/src/main/java/org/bouncycastle/crypto/prng/drbg/HMacSP800DRBG.java index dd22f4c6..3ddeaac6 100644 --- a/src/main/java/org/bouncycastle/crypto/prng/HMacSP800DRBG.java +++ b/src/main/java/org/bouncycastle/crypto/prng/drbg/HMacSP800DRBG.java @@ -1,29 +1,53 @@ -package org.bouncycastle.crypto.prng; +package org.bouncycastle.crypto.prng.drbg; import org.bouncycastle.crypto.Mac; import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.prng.EntropySource; import org.bouncycastle.util.Arrays; +/** + * A SP800-90A HMAC DRBG. + */ public class HMacSP800DRBG implements SP80090DRBG { + private final static long RESEED_MAX = 1L << (48 - 1); + private final static int MAX_BITS_REQUEST = 1 << (19 - 1); + private byte[] _K; private byte[] _V; - private int _reseedCounter; + private long _reseedCounter; private EntropySource _entropySource; private Mac _hMac; - public HMacSP800DRBG(Mac hMac, EntropySource entropySource, byte[] nonce, - byte[] personalisationString, int securityStrength) + /** + * Construct a SP800-90A Hash DRBG. + * <p> + * Minimum entropy requirement is the security strength requested. + * </p> + * @param hMac Hash MAC to base the DRBG on. + * @param securityStrength security strength required (in bits) + * @param entropySource source of entropy to use for seeding/reseeding. + * @param personalizationString personalization string to distinguish this DRBG (may be null). + * @param nonce nonce to further distinguish this DRBG (may be null). + */ + public HMacSP800DRBG(Mac hMac, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce) { - // TODO: validate security strength + if (securityStrength > Utils.getMaxSecurityStrength(hMac)) + { + throw new IllegalArgumentException("Requested security strength is not supported by the derivation function"); + } + + if (entropySource.entropySize() < securityStrength) + { + throw new IllegalArgumentException("Not enough entropy for security strength required"); + } _entropySource = entropySource; _hMac = hMac; - // TODO: validate entropy length byte[] entropy = entropySource.getEntropy(); - byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalisationString); + byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalizationString); _K = new byte[hMac.getMacSize()]; _V = new byte[_K.length]; @@ -63,12 +87,29 @@ public class HMacSP800DRBG _hMac.doFinal(_V, 0); } + /** + * Populate a passed in array with random data. + * + * @param output output array for generated bits. + * @param additionalInput additional input to be added to the DRBG in this step. + * @param predictionResistant true if a reseed should be forced, false otherwise. + * + * @return number of bits generated, -1 if a reseed required. + */ public int generate(byte[] output, byte[] additionalInput, boolean predictionResistant) { - // TODO: check reseed counter - int numberOfBits = output.length * 8; + if (numberOfBits > MAX_BITS_REQUEST) + { + throw new IllegalArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST); + } + + if (_reseedCounter > RESEED_MAX) + { + return -1; + } + if (predictionResistant) { reseed(additionalInput); @@ -113,6 +154,11 @@ public class HMacSP800DRBG return numberOfBits; } + /** + * Reseed the DRBG. + * + * @param additionalInput additional input to be added to the DRBG in this step. + */ public void reseed(byte[] additionalInput) { byte[] entropy = _entropySource.getEntropy(); diff --git a/src/main/java/org/bouncycastle/crypto/prng/HashSP800DRBG.java b/src/main/java/org/bouncycastle/crypto/prng/drbg/HashSP800DRBG.java index c52dd917..4ed57163 100644 --- a/src/main/java/org/bouncycastle/crypto/prng/HashSP800DRBG.java +++ b/src/main/java/org/bouncycastle/crypto/prng/drbg/HashSP800DRBG.java @@ -1,305 +1,269 @@ -package org.bouncycastle.crypto.prng;
-
-import java.util.Hashtable;
-
-import org.bouncycastle.crypto.Digest;
-import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.Integers;
-
-public class HashSP800DRBG implements SP80090DRBG
-{
- private final static byte[] ONE = { 0x01 };
- private final static int RESEED_MAX = 100000;
- private final static Hashtable seedlens = new Hashtable();
-
- static
- {
- seedlens.put("SHA-1", Integers.valueOf(440));
- seedlens.put("SHA-224", Integers.valueOf(440));
- seedlens.put("SHA-256", Integers.valueOf(440));
- seedlens.put("SHA-512/256", Integers.valueOf(440));
- seedlens.put("SHA-512/224", Integers.valueOf(440));
- seedlens.put("SHA-384", Integers.valueOf(888));
- seedlens.put("SHA-512", Integers.valueOf(888));
- }
-
- private Digest _digest;
- private byte[] _V;
- private byte[] _C;
- private int _reseedCounter;
- private EntropySource _entropySource;
- private int _securityStrength;
- private int _seedLength;
-
- public HashSP800DRBG(Digest digest, EntropySource entropySource, byte[] nonce,
- byte[] personalisationString, int securityStrength)
- {
- if (securityStrength > digest.getDigestSize() * 8) // TODO: this may, or may not be correct, but it's good enough for now
- {
- throw new IllegalStateException(
- "Security strength is not supported by the derivation function");
- }
-
- _digest = digest;
- _entropySource = entropySource;
- _securityStrength = securityStrength;
- _seedLength = ((Integer)seedlens.get(digest.getAlgorithmName())).intValue();
-
- // 1. seed_material = entropy_input || nonce || personalization_string.
- // 2. seed = Hash_df (seed_material, seedlen).
- // 3. V = seed.
- // 4. C = Hash_df ((0x00 || V), seedlen). Comment: Preceed V with a byte
- // of zeros.
- // 5. reseed_counter = 1.
- // 6. Return V, C, and reseed_counter as the initial_working_state
-
- byte[] entropy = entropySource.getEntropy();
- byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalisationString);
- byte[] seed = hash_df(seedMaterial, _seedLength);
-
- _V = seed;
- byte[] subV = new byte[_V.length + 1];
- System.arraycopy(_V, 0, subV, 1, _V.length);
- _C = hash_df(subV, _seedLength);
- _reseedCounter = 1;
-
-// System.out.println("Constructor V: "+ new String(Hex.encode(_V)));
-// System.out.println("Constructor C: "+ new String(Hex.encode(_C)));
-
- }
-
- // 1. If reseed_counter > reseed_interval, then return an indication that a
- // reseed is required.
- // 2. If (additional_input != Null), then do
- // 2.1 w = Hash (0x02 || V || additional_input).
- // 2.2 V = (V + w) mod 2^seedlen
- // .
- // 3. (returned_bits) = Hashgen (requested_number_of_bits, V).
- // 4. H = Hash (0x03 || V).
- // 5. V = (V + H + C + reseed_counter) mod 2^seedlen
- // .
- // 6. reseed_counter = reseed_counter + 1.
- // 7. Return SUCCESS, returned_bits, and the new values of V, C, and
- // reseed_counter for the new_working_state.
- public int generate(byte[] output, byte[] additionalInput, boolean predictionResistant)
- {
- int numberOfBits = output.length*8;
-
- if (predictionResistant || _reseedCounter > RESEED_MAX)
- {
- reseed(additionalInput);
- additionalInput = null;
- }
-
- // 2.
- if (additionalInput != null)
- {
- byte[] newInput = new byte[1 + _V.length + additionalInput.length];
- newInput[0] = 0x02;
- System.arraycopy(_V, 0, newInput, 1, _V.length);
- // TODO: inOff / inLength
- System.arraycopy(additionalInput, 0, newInput, 1 + _V.length, additionalInput.length);
- byte[] w = hash(newInput);
-
- addTo(_V, w);
- }
-
- // 3.
- byte[] rv = hashgen(_V, numberOfBits);
-
- // 4.
- byte[] subH = new byte[_V.length + 1];
- System.arraycopy(_V, 0, subH, 1, _V.length);
- subH[0] = 0x03;
-
- byte[] H = hash(subH);
-
- // 5.
- addTo(_V, H);
- addTo(_V, _C);
- byte[] c = new byte[4];
- c[0] = (byte)(_reseedCounter >> 24);
- c[1] = (byte)(_reseedCounter >> 16);
- c[2] = (byte)(_reseedCounter >> 8);
- c[3] = (byte)_reseedCounter;
-
- addTo(_V, c);
- _reseedCounter++;
-
- System.arraycopy(rv, 0, output, 0, output.length);
-// System.out.println("Generate V: "+ new String(Hex.encode(_V)));
-// System.out.println("Generate C: "+ new String(Hex.encode(_C)));
-
- return numberOfBits;
- }
-
- // this will always add the shorter length byte array mathematically to the
- // longer length byte array.
- // be careful....
- private void addTo(byte[] longer, byte[] shorter)
- {
- int carry = 0;
- for (int i=1;i <= shorter.length; i++) // warning
- {
- int res = (longer[longer.length-i] & 0xff) + (shorter[shorter.length-i] & 0xff) + carry;
- carry = (res > 0xff) ? 1 : 0;
- longer[longer.length-i] = (byte)res;
- }
-
- for (int i=shorter.length+1;i <= longer.length; i++) // warning
- {
- int res = (longer[longer.length-i] & 0xff) + carry;
- carry = (res > 0xff) ? 1 : 0;
- longer[longer.length-i] = (byte)res;
- }
- }
-
- // 1. seed_material = 0x01 || V || entropy_input || additional_input.
- //
- // 2. seed = Hash_df (seed_material, seedlen).
- //
- // 3. V = seed.
- //
- // 4. C = Hash_df ((0x00 || V), seedlen).
- //
- // 5. reseed_counter = 1.
- //
- // 6. Return V, C, and reseed_counter for the new_working_state.
- //
- // Comment: Precede with a byte of all zeros.
- public void reseed(byte[] additionalInput)
- {
- if (additionalInput == null)
- {
- additionalInput = new byte[0];
- }
-
- byte[] entropy = _entropySource.getEntropy();
-
-// System.out.println("Reseed Entropy: "+ new String(Hex.encode(entropy)));
-
- byte[] seedMaterial = Arrays.concatenate(ONE, _V, entropy, additionalInput);
-
-// System.out.println("Reseed SeedMaterial: "+ new String(Hex.encode(seedMaterial)));
-
- byte[] seed = hash_df(seedMaterial, _seedLength);
-
-// System.out.println("Reseed Seed: "+ new String(Hex.encode(seed)));
-
- _V = seed;
- byte[] subV = new byte[_V.length + 1];
- subV[0] = 0x00;
- System.arraycopy(_V, 0, subV, 1, _V.length);
- _C = hash_df(subV, _seedLength);
- _reseedCounter = 1;
-
-// System.out.println("Reseed V: "+ new String(Hex.encode(_V)));
-// System.out.println("Reseed C: "+ new String(Hex.encode(_C)));
- }
-
-
- // ---- Internal manipulation ---
- // ---- Migrating from the external HashDF class --
-
-
- // 1. temp = the Null string.
- // 2. .
- // 3. counter = an 8-bit binary value representing the integer "1".
- // 4. For i = 1 to len do
- // Comment : In step 4.1, no_of_bits_to_return
- // is used as a 32-bit string.
- // 4.1 temp = temp || Hash (counter || no_of_bits_to_return ||
- // input_string).
- // 4.2 counter = counter + 1.
- // 5. requested_bits = Leftmost (no_of_bits_to_return) of temp.
- // 6. Return SUCCESS and requested_bits.
- private byte[] hash_df(byte[] seedMaterial, int seedLength)
- {
- byte[] temp = new byte[seedLength / 8];
-
- int len = temp.length / _digest.getDigestSize();
- int counter = 1;
-
- byte[] dig = new byte[_digest.getDigestSize()];
-
- for (int i = 0; i <= len; i++)
- {
- _digest.update((byte)counter);
-
- _digest.update((byte)(seedLength >> 24));
- _digest.update((byte)(seedLength >> 16));
- _digest.update((byte)(seedLength >> 8));
- _digest.update((byte)seedLength);
-
- _digest.update(seedMaterial, 0, seedMaterial.length);
-
- _digest.doFinal(dig, 0);
-
- int bytesToCopy = ((temp.length - i * dig.length) > dig.length)
- ? dig.length
- : (temp.length - i * dig.length);
- System.arraycopy(dig, 0, temp, i * dig.length, bytesToCopy);
-
- counter++;
- }
-
- // do a left shift to get rid of excess bits.
- if (seedLength % 8 != 0)
- {
- int shift = 8 - (seedLength % 8);
- int carry = 0;
-
- for (int i = 0; i != temp.length; i++)
- {
- int b = temp[i] & 0xff;
- temp[i] = (byte)((b >>> shift) | (carry << (8 - shift)));
- carry = b;
- }
- }
-
- return temp;
- }
-
- private byte[] hash(byte[] input)
- {
- _digest.update(input, 0, input.length);
- byte[] hash = new byte[_digest.getDigestSize()];
- _digest.doFinal(hash, 0);
- return hash;
- }
-
- // 1. m = [requested_number_of_bits / outlen]
- // 2. data = V.
- // 3. W = the Null string.
- // 4. For i = 1 to m
- // 4.1 wi = Hash (data).
- // 4.2 W = W || wi.
- // 4.3 data = (data + 1) mod 2^seedlen
- // .
- // 5. returned_bits = Leftmost (requested_no_of_bits) bits of W.
- private byte[] hashgen(byte[] input, int lengthInBits)
- {
- int digestSize = _digest.getDigestSize();
- int m = (lengthInBits / 8) / digestSize;
-
- byte[] data = new byte[input.length];
- System.arraycopy(input, 0, data, 0, input.length);
-
- byte[] W = new byte[lengthInBits / 8];
-
- byte[] dig;
- for (int i = 0; i <= m; i++)
- {
- dig = hash(data);
-
- int bytesToCopy = ((W.length - i * dig.length) > dig.length)
- ? dig.length
- : (W.length - i * dig.length);
- System.arraycopy(dig, 0, W, i * dig.length, bytesToCopy);
-
- addTo(data, ONE);
- }
-
- return W;
- }
-}
+package org.bouncycastle.crypto.prng.drbg; + +import java.util.Hashtable; + +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.prng.EntropySource; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Integers; + +/** + * A SP800-90A Hash DRBG. + */ +public class HashSP800DRBG + implements SP80090DRBG +{ + private final static byte[] ONE = { 0x01 }; + + private final static long RESEED_MAX = 1L << (48 - 1); + private final static int MAX_BITS_REQUEST = 1 << (19 - 1); + + private final static Hashtable seedlens = new Hashtable(); + + static + { + seedlens.put("SHA-1", Integers.valueOf(440)); + seedlens.put("SHA-224", Integers.valueOf(440)); + seedlens.put("SHA-256", Integers.valueOf(440)); + seedlens.put("SHA-512/256", Integers.valueOf(440)); + seedlens.put("SHA-512/224", Integers.valueOf(440)); + seedlens.put("SHA-384", Integers.valueOf(888)); + seedlens.put("SHA-512", Integers.valueOf(888)); + } + + private Digest _digest; + private byte[] _V; + private byte[] _C; + private long _reseedCounter; + private EntropySource _entropySource; + private int _securityStrength; + private int _seedLength; + + /** + * Construct a SP800-90A Hash DRBG. + * <p> + * Minimum entropy requirement is the security strength requested. + * </p> + * @param digest source digest to use for DRB stream. + * @param securityStrength security strength required (in bits) + * @param entropySource source of entropy to use for seeding/reseeding. + * @param personalizationString personalization string to distinguish this DRBG (may be null). + * @param nonce nonce to further distinguish this DRBG (may be null). + */ + public HashSP800DRBG(Digest digest, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce) + { + if (securityStrength > Utils.getMaxSecurityStrength(digest)) + { + throw new IllegalArgumentException("Requested security strength is not supported by the derivation function"); + } + + if (entropySource.entropySize() < securityStrength) + { + throw new IllegalArgumentException("Not enough entropy for security strength required"); + } + + _digest = digest; + _entropySource = entropySource; + _securityStrength = securityStrength; + _seedLength = ((Integer)seedlens.get(digest.getAlgorithmName())).intValue(); + + // 1. seed_material = entropy_input || nonce || personalization_string. + // 2. seed = Hash_df (seed_material, seedlen). + // 3. V = seed. + // 4. C = Hash_df ((0x00 || V), seedlen). Comment: Preceed V with a byte + // of zeros. + // 5. reseed_counter = 1. + // 6. Return V, C, and reseed_counter as the initial_working_state + + byte[] entropy = entropySource.getEntropy(); + byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalizationString); + byte[] seed = Utils.hash_df(_digest, seedMaterial, _seedLength); + + _V = seed; + byte[] subV = new byte[_V.length + 1]; + System.arraycopy(_V, 0, subV, 1, _V.length); + _C = Utils.hash_df(_digest, subV, _seedLength); + + _reseedCounter = 1; + } + + /** + * Populate a passed in array with random data. + * + * @param output output array for generated bits. + * @param additionalInput additional input to be added to the DRBG in this step. + * @param predictionResistant true if a reseed should be forced, false otherwise. + * + * @return number of bits generated, -1 if a reseed required. + */ + public int generate(byte[] output, byte[] additionalInput, boolean predictionResistant) + { + // 1. If reseed_counter > reseed_interval, then return an indication that a + // reseed is required. + // 2. If (additional_input != Null), then do + // 2.1 w = Hash (0x02 || V || additional_input). + // 2.2 V = (V + w) mod 2^seedlen + // . + // 3. (returned_bits) = Hashgen (requested_number_of_bits, V). + // 4. H = Hash (0x03 || V). + // 5. V = (V + H + C + reseed_counter) mod 2^seedlen + // . + // 6. reseed_counter = reseed_counter + 1. + // 7. Return SUCCESS, returned_bits, and the new values of V, C, and + // reseed_counter for the new_working_state. + int numberOfBits = output.length*8; + + if (numberOfBits > MAX_BITS_REQUEST) + { + throw new IllegalArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST); + } + + if (_reseedCounter > RESEED_MAX) + { + return -1; + } + + if (predictionResistant) + { + reseed(additionalInput); + additionalInput = null; + } + + // 2. + if (additionalInput != null) + { + byte[] newInput = new byte[1 + _V.length + additionalInput.length]; + newInput[0] = 0x02; + System.arraycopy(_V, 0, newInput, 1, _V.length); + // TODO: inOff / inLength + System.arraycopy(additionalInput, 0, newInput, 1 + _V.length, additionalInput.length); + byte[] w = hash(newInput); + + addTo(_V, w); + } + + // 3. + byte[] rv = hashgen(_V, numberOfBits); + + // 4. + byte[] subH = new byte[_V.length + 1]; + System.arraycopy(_V, 0, subH, 1, _V.length); + subH[0] = 0x03; + + byte[] H = hash(subH); + + // 5. + addTo(_V, H); + addTo(_V, _C); + byte[] c = new byte[4]; + c[0] = (byte)(_reseedCounter >> 24); + c[1] = (byte)(_reseedCounter >> 16); + c[2] = (byte)(_reseedCounter >> 8); + c[3] = (byte)_reseedCounter; + + addTo(_V, c); + + _reseedCounter++; + + System.arraycopy(rv, 0, output, 0, output.length); + + return numberOfBits; + } + + // this will always add the shorter length byte array mathematically to the + // longer length byte array. + // be careful.... + private void addTo(byte[] longer, byte[] shorter) + { + int carry = 0; + for (int i=1;i <= shorter.length; i++) // warning + { + int res = (longer[longer.length-i] & 0xff) + (shorter[shorter.length-i] & 0xff) + carry; + carry = (res > 0xff) ? 1 : 0; + longer[longer.length-i] = (byte)res; + } + + for (int i=shorter.length+1;i <= longer.length; i++) // warning + { + int res = (longer[longer.length-i] & 0xff) + carry; + carry = (res > 0xff) ? 1 : 0; + longer[longer.length-i] = (byte)res; + } + } + + /** + * Reseed the DRBG. + * + * @param additionalInput additional input to be added to the DRBG in this step. + */ + public void reseed(byte[] additionalInput) + { + // 1. seed_material = 0x01 || V || entropy_input || additional_input. + // + // 2. seed = Hash_df (seed_material, seedlen). + // + // 3. V = seed. + // + // 4. C = Hash_df ((0x00 || V), seedlen). + // + // 5. reseed_counter = 1. + // + // 6. Return V, C, and reseed_counter for the new_working_state. + // + // Comment: Precede with a byte of all zeros. + byte[] entropy = _entropySource.getEntropy(); + byte[] seedMaterial = Arrays.concatenate(ONE, _V, entropy, additionalInput); + byte[] seed = Utils.hash_df(_digest, seedMaterial, _seedLength); + + _V = seed; + byte[] subV = new byte[_V.length + 1]; + subV[0] = 0x00; + System.arraycopy(_V, 0, subV, 1, _V.length); + _C = Utils.hash_df(_digest, subV, _seedLength); + + _reseedCounter = 1; + } + + private byte[] hash(byte[] input) + { + _digest.update(input, 0, input.length); + byte[] hash = new byte[_digest.getDigestSize()]; + _digest.doFinal(hash, 0); + return hash; + } + + // 1. m = [requested_number_of_bits / outlen] + // 2. data = V. + // 3. W = the Null string. + // 4. For i = 1 to m + // 4.1 wi = Hash (data). + // 4.2 W = W || wi. + // 4.3 data = (data + 1) mod 2^seedlen + // . + // 5. returned_bits = Leftmost (requested_no_of_bits) bits of W. + private byte[] hashgen(byte[] input, int lengthInBits) + { + int digestSize = _digest.getDigestSize(); + int m = (lengthInBits / 8) / digestSize; + + byte[] data = new byte[input.length]; + System.arraycopy(input, 0, data, 0, input.length); + + byte[] W = new byte[lengthInBits / 8]; + + byte[] dig; + for (int i = 0; i <= m; i++) + { + dig = hash(data); + + int bytesToCopy = ((W.length - i * dig.length) > dig.length) + ? dig.length + : (W.length - i * dig.length); + System.arraycopy(dig, 0, W, i * dig.length, bytesToCopy); + + addTo(data, ONE); + } + + return W; + } +}
\ No newline at end of file diff --git a/src/main/java/org/bouncycastle/crypto/prng/drbg/SP80090DRBG.java b/src/main/java/org/bouncycastle/crypto/prng/drbg/SP80090DRBG.java new file mode 100644 index 00000000..93bc8945 --- /dev/null +++ b/src/main/java/org/bouncycastle/crypto/prng/drbg/SP80090DRBG.java @@ -0,0 +1,25 @@ +package org.bouncycastle.crypto.prng.drbg; + +/** + * Interface to SP800-90A deterministic random bit generators. + */ +public interface SP80090DRBG +{ + /** + * Populate a passed in array with random data. + * + * @param output output array for generated bits. + * @param additionalInput additional input to be added to the DRBG in this step. + * @param predictionResistant true if a reseed should be forced, false otherwise. + * + * @return number of bits generated, -1 if a reseed required. + */ + int generate(byte[] output, byte[] additionalInput, boolean predictionResistant); + + /** + * Reseed the DRBG. + * + * @param additionalInput additional input to be added to the DRBG in this step. + */ + void reseed(byte[] additionalInput); +} diff --git a/src/main/java/org/bouncycastle/crypto/prng/drbg/Utils.java b/src/main/java/org/bouncycastle/crypto/prng/drbg/Utils.java new file mode 100644 index 00000000..f7a41176 --- /dev/null +++ b/src/main/java/org/bouncycastle/crypto/prng/drbg/Utils.java @@ -0,0 +1,103 @@ +package org.bouncycastle.crypto.prng.drbg; + +import java.util.Hashtable; + +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.util.Integers; + +class Utils +{ + static final Hashtable maxSecurityStrengths = new Hashtable(); + + static + { + maxSecurityStrengths.put("SHA-1", Integers.valueOf(128)); + + maxSecurityStrengths.put("SHA-224", Integers.valueOf(192)); + maxSecurityStrengths.put("SHA-256", Integers.valueOf(256)); + maxSecurityStrengths.put("SHA-384", Integers.valueOf(256)); + maxSecurityStrengths.put("SHA-512", Integers.valueOf(256)); + + maxSecurityStrengths.put("SHA-512/224", Integers.valueOf(192)); + maxSecurityStrengths.put("SHA-512/256", Integers.valueOf(256)); + } + + static int getMaxSecurityStrength(Digest d) + { + return ((Integer)maxSecurityStrengths.get(d.getAlgorithmName())).intValue(); + } + + static int getMaxSecurityStrength(Mac m) + { + String name = m.getAlgorithmName(); + + return ((Integer)maxSecurityStrengths.get(name.substring(0, name.indexOf("/")))).intValue(); + } + + /** + * Used by both Dual EC and Hash. + */ + static byte[] hash_df(Digest digest, byte[] seedMaterial, int seedLength) + { + // 1. temp = the Null string. + // 2. . + // 3. counter = an 8-bit binary value representing the integer "1". + // 4. For i = 1 to len do + // Comment : In step 4.1, no_of_bits_to_return + // is used as a 32-bit string. + // 4.1 temp = temp || Hash (counter || no_of_bits_to_return || + // input_string). + // 4.2 counter = counter + 1. + // 5. requested_bits = Leftmost (no_of_bits_to_return) of temp. + // 6. Return SUCCESS and requested_bits. + byte[] temp = new byte[(seedLength + 7) / 8]; + + int len = temp.length / digest.getDigestSize(); + int counter = 1; + + byte[] dig = new byte[digest.getDigestSize()]; + + for (int i = 0; i <= len; i++) + { + digest.update((byte)counter); + + digest.update((byte)(seedLength >> 24)); + digest.update((byte)(seedLength >> 16)); + digest.update((byte)(seedLength >> 8)); + digest.update((byte)seedLength); + + digest.update(seedMaterial, 0, seedMaterial.length); + + digest.doFinal(dig, 0); + + int bytesToCopy = ((temp.length - i * dig.length) > dig.length) + ? dig.length + : (temp.length - i * dig.length); + System.arraycopy(dig, 0, temp, i * dig.length, bytesToCopy); + + counter++; + } + + // do a left shift to get rid of excess bits. + if (seedLength % 8 != 0) + { + int shift = 8 - (seedLength % 8); + int carry = 0; + + for (int i = 0; i != temp.length; i++) + { + int b = temp[i] & 0xff; + temp[i] = (byte)((b >>> shift) | (carry << (8 - shift))); + carry = b; + } + } + + return temp; + } + + static boolean isTooLarge(byte[] bytes, int maxBytes) + { + return bytes != null && bytes.length > maxBytes; + } +} diff --git a/src/main/java/org/bouncycastle/crypto/prng/drbg/package.html b/src/main/java/org/bouncycastle/crypto/prng/drbg/package.html new file mode 100644 index 00000000..c0061660 --- /dev/null +++ b/src/main/java/org/bouncycastle/crypto/prng/drbg/package.html @@ -0,0 +1,5 @@ +<html> +<body bgcolor="#ffffff"> +SP800-90A deterministic random bit generators, can be used stand alone or in conjunction with SP800SecureRandomBuilder class. +</body> +</html> diff --git a/src/main/java/org/bouncycastle/crypto/prng/package.html b/src/main/java/org/bouncycastle/crypto/prng/package.html index 99525fbf..bb583abd 100644 --- a/src/main/java/org/bouncycastle/crypto/prng/package.html +++ b/src/main/java/org/bouncycastle/crypto/prng/package.html @@ -1,5 +1,5 @@ <html> <body bgcolor="#ffffff"> -Lightweight psuedo-random number generators. +Lightweight psuedo-random number generators and SecureRandom builders. </body> </html> diff --git a/src/main/java/org/bouncycastle/crypto/random/DRBGProvider.java b/src/main/java/org/bouncycastle/crypto/random/DRBGProvider.java deleted file mode 100644 index f1ef83b2..00000000 --- a/src/main/java/org/bouncycastle/crypto/random/DRBGProvider.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.bouncycastle.crypto.random; - -import org.bouncycastle.crypto.prng.SP80090DRBG; -import org.bouncycastle.crypto.prng.EntropySource; - -interface DRBGProvider -{ - SP80090DRBG get(EntropySource entropySource); -} diff --git a/src/main/java/org/bouncycastle/crypto/util/Pack.java b/src/main/java/org/bouncycastle/crypto/util/Pack.java index b767cc9e..d2ce166b 100644 --- a/src/main/java/org/bouncycastle/crypto/util/Pack.java +++ b/src/main/java/org/bouncycastle/crypto/util/Pack.java @@ -1,192 +1,192 @@ -package org.bouncycastle.crypto.util;
-
-public abstract class Pack
-{
- public static int bigEndianToInt(byte[] bs, int off)
- {
- int n = bs[ off] << 24;
- n |= (bs[++off] & 0xff) << 16;
- n |= (bs[++off] & 0xff) << 8;
- n |= (bs[++off] & 0xff);
- return n;
- }
-
- public static void bigEndianToInt(byte[] bs, int off, int[] ns)
- {
- for (int i = 0; i < ns.length; ++i)
- {
- ns[i] = bigEndianToInt(bs, off);
- off += 4;
- }
- }
-
- public static byte[] intToBigEndian(int n)
- {
- byte[] bs = new byte[4];
- intToBigEndian(n, bs, 0);
- return bs;
- }
-
- public static void intToBigEndian(int n, byte[] bs, int off)
- {
- bs[ off] = (byte)(n >>> 24);
- bs[++off] = (byte)(n >>> 16);
- bs[++off] = (byte)(n >>> 8);
- bs[++off] = (byte)(n );
- }
-
- public static byte[] intToBigEndian(int[] ns)
- {
- byte[] bs = new byte[4 * ns.length];
- intToBigEndian(ns, bs, 0);
- return bs;
- }
-
- public static void intToBigEndian(int[] ns, byte[] bs, int off)
- {
- for (int i = 0; i < ns.length; ++i)
- {
- intToBigEndian(ns[i], bs, off);
- off += 4;
- }
- }
-
- public static long bigEndianToLong(byte[] bs, int off)
- {
- int hi = bigEndianToInt(bs, off);
- int lo = bigEndianToInt(bs, off + 4);
- return ((long)(hi & 0xffffffffL) << 32) | (long)(lo & 0xffffffffL);
- }
-
- public static void bigEndianToLong(byte[] bs, int off, long[] ns)
- {
- for (int i = 0; i < ns.length; ++i)
- {
- ns[i] = bigEndianToLong(bs, off);
- off += 8;
- }
- }
-
- public static byte[] longToBigEndian(long n)
- {
- byte[] bs = new byte[8];
- longToBigEndian(n, bs, 0);
- return bs;
- }
-
- public static void longToBigEndian(long n, byte[] bs, int off)
- {
- intToBigEndian((int)(n >>> 32), bs, off);
- intToBigEndian((int)(n & 0xffffffffL), bs, off + 4);
- }
-
- public static byte[] longToBigEndian(long[] ns)
- {
- byte[] bs = new byte[8 * ns.length];
- longToBigEndian(ns, bs, 0);
- return bs;
- }
-
- public static void longToBigEndian(long[] ns, byte[] bs, int off)
- {
- for (int i = 0; i < ns.length; ++i)
- {
- longToBigEndian(ns[i], bs, off);
- off += 8;
- }
- }
-
- public static int littleEndianToInt(byte[] bs, int off)
- {
- int n = bs[ off] & 0xff;
- n |= (bs[++off] & 0xff) << 8;
- n |= (bs[++off] & 0xff) << 16;
- n |= bs[++off] << 24;
- return n;
- }
-
- public static void littleEndianToInt(byte[] bs, int off, int[] ns)
- {
- for (int i = 0; i < ns.length; ++i)
- {
- ns[i] = littleEndianToInt(bs, off);
- off += 4;
- }
- }
-
- public static byte[] intToLittleEndian(int n)
- {
- byte[] bs = new byte[4];
- intToLittleEndian(n, bs, 0);
- return bs;
- }
-
- public static void intToLittleEndian(int n, byte[] bs, int off)
- {
- bs[ off] = (byte)(n );
- bs[++off] = (byte)(n >>> 8);
- bs[++off] = (byte)(n >>> 16);
- bs[++off] = (byte)(n >>> 24);
- }
-
- public static byte[] intToLittleEndian(int[] ns)
- {
- byte[] bs = new byte[4 * ns.length];
- intToLittleEndian(ns, bs, 0);
- return bs;
- }
-
- public static void intToLittleEndian(int[] ns, byte[] bs, int off)
- {
- for (int i = 0; i < ns.length; ++i)
- {
- intToLittleEndian(ns[i], bs, off);
- off += 4;
- }
- }
-
- public static long littleEndianToLong(byte[] bs, int off)
- {
- int lo = littleEndianToInt(bs, off);
- int hi = littleEndianToInt(bs, off + 4);
- return ((long)(hi & 0xffffffffL) << 32) | (long)(lo & 0xffffffffL);
- }
-
- public static void littleEndianToLong(byte[] bs, int off, long[] ns)
- {
- for (int i = 0; i < ns.length; ++i)
- {
- ns[i] = littleEndianToLong(bs, off);
- off += 8;
- }
- }
-
- public static byte[] longToLittleEndian(long n)
- {
- byte[] bs = new byte[8];
- longToLittleEndian(n, bs, 0);
- return bs;
- }
-
- public static void longToLittleEndian(long n, byte[] bs, int off)
- {
- intToLittleEndian((int)(n & 0xffffffffL), bs, off);
- intToLittleEndian((int)(n >>> 32), bs, off + 4);
- }
-
- public static byte[] longToLittleEndian(long[] ns)
- {
- byte[] bs = new byte[8 * ns.length];
- longToLittleEndian(ns, bs, 0);
- return bs;
- }
-
- public static void longToLittleEndian(long[] ns, byte[] bs, int off)
- {
- for (int i = 0; i < ns.length; ++i)
- {
- longToLittleEndian(ns[i], bs, off);
- off += 8;
- }
- }
-}
+package org.bouncycastle.crypto.util; + +public abstract class Pack +{ + public static int bigEndianToInt(byte[] bs, int off) + { + int n = bs[ off] << 24; + n |= (bs[++off] & 0xff) << 16; + n |= (bs[++off] & 0xff) << 8; + n |= (bs[++off] & 0xff); + return n; + } + + public static void bigEndianToInt(byte[] bs, int off, int[] ns) + { + for (int i = 0; i < ns.length; ++i) + { + ns[i] = bigEndianToInt(bs, off); + off += 4; + } + } + + public static byte[] intToBigEndian(int n) + { + byte[] bs = new byte[4]; + intToBigEndian(n, bs, 0); + return bs; + } + + public static void intToBigEndian(int n, byte[] bs, int off) + { + bs[ off] = (byte)(n >>> 24); + bs[++off] = (byte)(n >>> 16); + bs[++off] = (byte)(n >>> 8); + bs[++off] = (byte)(n ); + } + + public static byte[] intToBigEndian(int[] ns) + { + byte[] bs = new byte[4 * ns.length]; + intToBigEndian(ns, bs, 0); + return bs; + } + + public static void intToBigEndian(int[] ns, byte[] bs, int off) + { + for (int i = 0; i < ns.length; ++i) + { + intToBigEndian(ns[i], bs, off); + off += 4; + } + } + + public static long bigEndianToLong(byte[] bs, int off) + { + int hi = bigEndianToInt(bs, off); + int lo = bigEndianToInt(bs, off + 4); + return ((long)(hi & 0xffffffffL) << 32) | (long)(lo & 0xffffffffL); + } + + public static void bigEndianToLong(byte[] bs, int off, long[] ns) + { + for (int i = 0; i < ns.length; ++i) + { + ns[i] = bigEndianToLong(bs, off); + off += 8; + } + } + + public static byte[] longToBigEndian(long n) + { + byte[] bs = new byte[8]; + longToBigEndian(n, bs, 0); + return bs; + } + + public static void longToBigEndian(long n, byte[] bs, int off) + { + intToBigEndian((int)(n >>> 32), bs, off); + intToBigEndian((int)(n & 0xffffffffL), bs, off + 4); + } + + public static byte[] longToBigEndian(long[] ns) + { + byte[] bs = new byte[8 * ns.length]; + longToBigEndian(ns, bs, 0); + return bs; + } + + public static void longToBigEndian(long[] ns, byte[] bs, int off) + { + for (int i = 0; i < ns.length; ++i) + { + longToBigEndian(ns[i], bs, off); + off += 8; + } + } + + public static int littleEndianToInt(byte[] bs, int off) + { + int n = bs[ off] & 0xff; + n |= (bs[++off] & 0xff) << 8; + n |= (bs[++off] & 0xff) << 16; + n |= bs[++off] << 24; + return n; + } + + public static void littleEndianToInt(byte[] bs, int off, int[] ns) + { + for (int i = 0; i < ns.length; ++i) + { + ns[i] = littleEndianToInt(bs, off); + off += 4; + } + } + + public static byte[] intToLittleEndian(int n) + { + byte[] bs = new byte[4]; + intToLittleEndian(n, bs, 0); + return bs; + } + + public static void intToLittleEndian(int n, byte[] bs, int off) + { + bs[ off] = (byte)(n ); + bs[++off] = (byte)(n >>> 8); + bs[++off] = (byte)(n >>> 16); + bs[++off] = (byte)(n >>> 24); + } + + public static byte[] intToLittleEndian(int[] ns) + { + byte[] bs = new byte[4 * ns.length]; + intToLittleEndian(ns, bs, 0); + return bs; + } + + public static void intToLittleEndian(int[] ns, byte[] bs, int off) + { + for (int i = 0; i < ns.length; ++i) + { + intToLittleEndian(ns[i], bs, off); + off += 4; + } + } + + public static long littleEndianToLong(byte[] bs, int off) + { + int lo = littleEndianToInt(bs, off); + int hi = littleEndianToInt(bs, off + 4); + return ((long)(hi & 0xffffffffL) << 32) | (long)(lo & 0xffffffffL); + } + + public static void littleEndianToLong(byte[] bs, int off, long[] ns) + { + for (int i = 0; i < ns.length; ++i) + { + ns[i] = littleEndianToLong(bs, off); + off += 8; + } + } + + public static byte[] longToLittleEndian(long n) + { + byte[] bs = new byte[8]; + longToLittleEndian(n, bs, 0); + return bs; + } + + public static void longToLittleEndian(long n, byte[] bs, int off) + { + intToLittleEndian((int)(n & 0xffffffffL), bs, off); + intToLittleEndian((int)(n >>> 32), bs, off + 4); + } + + public static byte[] longToLittleEndian(long[] ns) + { + byte[] bs = new byte[8 * ns.length]; + longToLittleEndian(ns, bs, 0); + return bs; + } + + public static void longToLittleEndian(long[] ns, byte[] bs, int off) + { + for (int i = 0; i < ns.length; ++i) + { + longToLittleEndian(ns[i], bs, off); + off += 8; + } + } +} diff --git a/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EC.java b/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EC.java index 31226180..4c2ca28b 100644 --- a/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EC.java +++ b/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EC.java @@ -82,6 +82,8 @@ public class EC addSignatureAlgorithm(provider, "SHA1", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_1); addSignatureAlgorithm(provider, "SHA224", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA224", EACObjectIdentifiers.id_TA_ECDSA_SHA_224); addSignatureAlgorithm(provider, "SHA256", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA256", EACObjectIdentifiers.id_TA_ECDSA_SHA_256); + addSignatureAlgorithm(provider, "SHA384", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA384", EACObjectIdentifiers.id_TA_ECDSA_SHA_384); + addSignatureAlgorithm(provider, "SHA512", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA512", EACObjectIdentifiers.id_TA_ECDSA_SHA_512); } } } diff --git a/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java b/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java index 2e5ee565..d850e5de 100644 --- a/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java +++ b/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java @@ -7,25 +7,36 @@ import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.DSAParameterSpec; +import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.generators.DSAParametersGenerator; +import org.bouncycastle.crypto.params.DSAParameterGenerationParameters; import org.bouncycastle.crypto.params.DSAParameters; import org.bouncycastle.jce.provider.BouncyCastleProvider; -//import org.bouncycastle.jce.spec.GOST3410ParameterSpec; -//import org.bouncycastle.jce.spec.GOST3410PublicKeyParameterSetSpec; public class AlgorithmParameterGeneratorSpi extends java.security.AlgorithmParameterGeneratorSpi { protected SecureRandom random; protected int strength = 1024; + protected DSAParameterGenerationParameters params; protected void engineInit( int strength, SecureRandom random) { - if (strength < 512 || strength > 1024 || strength % 64 != 0) + if (strength < 512 || strength > 3072) { - throw new InvalidParameterException("strength must be from 512 - 1024 and a multiple of 64"); + throw new InvalidParameterException("strength must be from 512 - 3072"); + } + + if (strength <= 1024 && strength % 64 != 0) + { + throw new InvalidParameterException("strength must be a multiple of 64 below 1024 bits."); + } + + if (strength > 1024 && strength % 1024 != 0) + { + throw new InvalidParameterException("strength must be a multiple of 1024 above 1024 bits."); } this.strength = strength; @@ -42,15 +53,35 @@ public class AlgorithmParameterGeneratorSpi protected AlgorithmParameters engineGenerateParameters() { - DSAParametersGenerator pGen = new DSAParametersGenerator(); + DSAParametersGenerator pGen; - if (random != null) + if (strength <= 1024) { - pGen.init(strength, 20, random); + pGen = new DSAParametersGenerator(); } else { - pGen.init(strength, 20, new SecureRandom()); + pGen = new DSAParametersGenerator(new SHA256Digest()); + } + + if (random == null) + { + random = new SecureRandom(); + } + + if (strength == 1024) + { + params = new DSAParameterGenerationParameters(1024, 160, 80, random); + pGen.init(params); + } + else if (strength > 1024) + { + params = new DSAParameterGenerationParameters(strength, 256, 80, random); + pGen.init(params); + } + else + { + pGen.init(strength, 20, random); } DSAParameters p = pGen.generateParameters(); diff --git a/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java b/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java index 8c8a5b9e..ef12b4f5 100644 --- a/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java +++ b/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java @@ -52,11 +52,6 @@ public class DSASigner { CipherParameters param; -// if (publicKey instanceof GOST3410Key) -// { -// param = GOST3410Util.generatePublicKeyParameter(publicKey); -// } -// else if (publicKey instanceof DSAKey) if (publicKey instanceof DSAKey) { param = DSAUtil.generatePublicKeyParameter(publicKey); @@ -103,14 +98,7 @@ public class DSASigner { CipherParameters param; -// if (privateKey instanceof GOST3410Key) -// { -// param = GOST3410Util.generatePrivateKeyParameter(privateKey); -// } -// else -// { - param = DSAUtil.generatePrivateKeyParameter(privateKey); -// } + param = DSAUtil.generatePrivateKeyParameter(privateKey); if (random != null) { diff --git a/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java b/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java index 8fc79b86..fc3a8bb7 100644 --- a/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java +++ b/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java @@ -199,6 +199,24 @@ public class SignatureSpi } } + static public class ecCVCDSA384 + extends SignatureSpi + { + public ecCVCDSA384() + { + super(new SHA384Digest(), new ECDSASigner(), new CVCDSAEncoder()); + } + } + + static public class ecCVCDSA512 + extends SignatureSpi + { + public ecCVCDSA512() + { + super(new SHA512Digest(), new ECDSASigner(), new CVCDSAEncoder()); + } + } + private static class StdDSAEncoder implements DSAEncoder { diff --git a/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java b/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java index ad7483a4..44220622 100644 --- a/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java +++ b/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java @@ -66,7 +66,7 @@ import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Integers; import org.bouncycastle.util.encoders.Hex; -class X509CertificateObject +class X509CertificateObject extends X509Certificate implements PKCS12BagAttributeCarrier { diff --git a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java index 07f47ead..7a6f7b04 100644 --- a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java +++ b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java @@ -16,8 +16,10 @@ import org.bouncycastle.crypto.engines.AESFastEngine; import org.bouncycastle.crypto.engines.AESWrapEngine; import org.bouncycastle.crypto.engines.RFC3211WrapEngine; import org.bouncycastle.crypto.macs.CMac; +import org.bouncycastle.crypto.macs.GMac; import org.bouncycastle.crypto.modes.CBCBlockCipher; import org.bouncycastle.crypto.modes.CFBBlockCipher; +import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.crypto.modes.OFBBlockCipher; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator; @@ -28,7 +30,6 @@ import org.bouncycastle.jcajce.provider.symmetric.util.BaseWrapCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider; import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters; import org.bouncycastle.jcajce.provider.symmetric.util.PBESecretKeyFactory; -import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider; public final class AES @@ -88,6 +89,15 @@ public final class AES } } + public static class AESGMAC + extends BaseMac + { + public AESGMAC() + { + super(new GMac(new GCMBlockCipher(new AESFastEngine()))); + } + } + static public class Wrap extends BaseWrapCipher { @@ -159,7 +169,6 @@ public final class AES super(256); } } - /** * PBEWithSHA1And128BitAES-BC @@ -317,7 +326,7 @@ public final class AES } public static class Mappings - extends AlgorithmProvider + extends SymmetricAlgorithmProvider { private static final String PREFIX = AES.class.getName(); @@ -473,6 +482,8 @@ public final class AES provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes128_cbc.getId(), "PKCS12PBE"); provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes192_cbc.getId(), "PKCS12PBE"); provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc.getId(), "PKCS12PBE"); + + addGMacAlgorithm(provider, "AES", PREFIX + "$AESGMAC", PREFIX + "$KeyGen128"); } } } diff --git a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java index 9ac54e73..68605f45 100644 --- a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java +++ b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java @@ -2,10 +2,12 @@ package org.bouncycastle.jcajce.provider.symmetric; import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.CAST6Engine; +import org.bouncycastle.crypto.macs.GMac; +import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; -import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; public final class CAST6 { @@ -31,8 +33,17 @@ public final class CAST6 } } + public static class GMAC + extends BaseMac + { + public GMAC() + { + super(new GMac(new GCMBlockCipher(new CAST6Engine()))); + } + } + public static class Mappings - extends AlgorithmProvider + extends SymmetricAlgorithmProvider { private static final String PREFIX = CAST6.class.getName(); @@ -44,6 +55,8 @@ public final class CAST6 { provider.addAlgorithm("Cipher.CAST6", PREFIX + "$ECB"); provider.addAlgorithm("KeyGenerator.CAST6", PREFIX + "$KeyGen"); + + addGMacAlgorithm(provider, "CAST6", PREFIX + "$GMAC", PREFIX + "$KeyGen"); } } } diff --git a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java index 5c98e961..38b5ca7b 100644 --- a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java +++ b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java @@ -13,15 +13,17 @@ import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.CamelliaEngine; import org.bouncycastle.crypto.engines.CamelliaWrapEngine; import org.bouncycastle.crypto.engines.RFC3211WrapEngine; +import org.bouncycastle.crypto.macs.GMac; import org.bouncycastle.crypto.modes.CBCBlockCipher; +import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator; import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; import org.bouncycastle.jcajce.provider.symmetric.util.BaseWrapCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider; import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters; -import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider; public final class Camellia @@ -72,6 +74,15 @@ public final class Camellia } } + public static class GMAC + extends BaseMac + { + public GMAC() + { + super(new GMac(new GCMBlockCipher(new CamelliaEngine()))); + } + } + public static class KeyGen extends BaseKeyGenerator { @@ -161,7 +172,7 @@ public final class Camellia } public static class Mappings - extends AlgorithmProvider + extends SymmetricAlgorithmProvider { private static final String PREFIX = Camellia.class.getName(); @@ -201,6 +212,7 @@ public final class Camellia provider.addAlgorithm("KeyGenerator." + NTTObjectIdentifiers.id_camellia192_cbc, PREFIX + "$KeyGen192"); provider.addAlgorithm("KeyGenerator." + NTTObjectIdentifiers.id_camellia256_cbc, PREFIX + "$KeyGen256"); + addGMacAlgorithm(provider, "CAMELLIA", PREFIX + "$GMAC", PREFIX + "$KeyGen"); } } } diff --git a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/DESede.java b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/DESede.java index 67908f9d..0f53e504 100644 --- a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/DESede.java +++ b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/DESede.java @@ -366,6 +366,13 @@ public final class DESede provider.addAlgorithm("Cipher." + PKCSObjectIdentifiers.id_alg_CMS3DESwrap, PREFIX + "$Wrap"); provider.addAlgorithm("Cipher.DESEDERFC3211WRAP", PREFIX + "$RFC3211"); + provider.addAlgorithm("Alg.Alias.Cipher.TDEA", "DESEDE"); + provider.addAlgorithm("Alg.Alias.Cipher.TDEAWRAP", "DESEDEWRAP"); + provider.addAlgorithm("Alg.Alias.KeyGenerator.TDEA", "DESEDE"); + provider.addAlgorithm("Alg.Alias.AlgorithmParameters.TDEA", "DESEDE"); + provider.addAlgorithm("Alg.Alias.AlgorithmParameterGenerator.TDEA", "DESEDE"); + provider.addAlgorithm("Alg.Alias.SecretKeyFactory.TDEA", "DESEDE"); + if (provider.hasAlgorithm("MessageDigest", "SHA-1")) { provider.addAlgorithm("Cipher.PBEWITHSHAAND3-KEYTRIPLEDES-CBC", PREFIX + "$PBEWithSHAAndDES3Key"); diff --git a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java index d488ac2c..2d089cc7 100644 --- a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java +++ b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java @@ -9,12 +9,14 @@ import javax.crypto.spec.IvParameterSpec; import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.NoekeonEngine; +import org.bouncycastle.crypto.macs.GMac; +import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator; import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters; -import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider; public final class Noekeon @@ -41,6 +43,15 @@ public final class Noekeon } } + public static class GMAC + extends BaseMac + { + public GMAC() + { + super(new GMac(new GCMBlockCipher(new NoekeonEngine()))); + } + } + public static class AlgParamGen extends BaseAlgorithmParameterGenerator { @@ -89,7 +100,7 @@ public final class Noekeon } public static class Mappings - extends AlgorithmProvider + extends SymmetricAlgorithmProvider { private static final String PREFIX = Noekeon.class.getName(); @@ -108,6 +119,7 @@ public final class Noekeon provider.addAlgorithm("KeyGenerator.NOEKEON", PREFIX + "$KeyGen"); + addGMacAlgorithm(provider, "NOEKEON", PREFIX + "$GMAC", PREFIX + "$KeyGen"); } } } diff --git a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java index 0861f13b..a29e717c 100644 --- a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java +++ b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java @@ -11,16 +11,18 @@ import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.RC6Engine; +import org.bouncycastle.crypto.macs.GMac; import org.bouncycastle.crypto.modes.CBCBlockCipher; import org.bouncycastle.crypto.modes.CFBBlockCipher; +import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.crypto.modes.OFBBlockCipher; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator; import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider; import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters; -import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider; public final class RC6 @@ -71,6 +73,15 @@ public final class RC6 } } + public static class GMAC + extends BaseMac + { + public GMAC() + { + super(new GMac(new GCMBlockCipher(new RC6Engine()))); + } + } + public static class KeyGen extends BaseKeyGenerator { @@ -128,7 +139,7 @@ public final class RC6 } public static class Mappings - extends AlgorithmProvider + extends SymmetricAlgorithmProvider { private static final String PREFIX = RC6.class.getName(); @@ -143,6 +154,7 @@ public final class RC6 provider.addAlgorithm("KeyGenerator.RC6", PREFIX + "$KeyGen"); provider.addAlgorithm("AlgorithmParameters.RC6", PREFIX + "$AlgParams"); + addGMacAlgorithm(provider, "RC6", PREFIX + "$GMAC", PREFIX + "$KeyGen"); } } } diff --git a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java index e4cffe30..2ad41bff 100644 --- a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java +++ b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java @@ -12,15 +12,17 @@ import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.SEEDEngine; import org.bouncycastle.crypto.engines.SEEDWrapEngine; +import org.bouncycastle.crypto.macs.GMac; import org.bouncycastle.crypto.modes.CBCBlockCipher; +import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator; import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; import org.bouncycastle.jcajce.provider.symmetric.util.BaseWrapCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider; import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters; -import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider; public final class SEED @@ -71,6 +73,15 @@ public final class SEED } } + public static class GMAC + extends BaseMac + { + public GMAC() + { + super(new GMac(new GCMBlockCipher(new SEEDEngine()))); + } + } + public static class AlgParamGen extends BaseAlgorithmParameterGenerator { @@ -119,7 +130,7 @@ public final class SEED } public static class Mappings - extends AlgorithmProvider + extends SymmetricAlgorithmProvider { private static final String PREFIX = SEED.class.getName(); @@ -146,6 +157,7 @@ public final class SEED provider.addAlgorithm("KeyGenerator." + KISAObjectIdentifiers.id_seedCBC, PREFIX + "$KeyGen"); provider.addAlgorithm("KeyGenerator." + KISAObjectIdentifiers.id_npki_app_cmsSeed_wrap, PREFIX + "$KeyGen"); + addGMacAlgorithm(provider, "SEED", PREFIX + "$GMAC", PREFIX + "$KeyGen"); } } } diff --git a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java index 02959ec4..578de32d 100644 --- a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java +++ b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java @@ -3,12 +3,14 @@ package org.bouncycastle.jcajce.provider.symmetric; import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.SerpentEngine; +import org.bouncycastle.crypto.macs.GMac; +import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider; import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters; -import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; public final class Serpent { @@ -40,6 +42,15 @@ public final class Serpent } } + public static class SerpentGMAC + extends BaseMac + { + public SerpentGMAC() + { + super(new GMac(new GCMBlockCipher(new SerpentEngine()))); + } + } + public static class AlgParams extends IvAlgorithmParameters { @@ -50,7 +61,7 @@ public final class Serpent } public static class Mappings - extends AlgorithmProvider + extends SymmetricAlgorithmProvider { private static final String PREFIX = Serpent.class.getName(); @@ -65,6 +76,7 @@ public final class Serpent provider.addAlgorithm("KeyGenerator.Serpent", PREFIX + "$KeyGen"); provider.addAlgorithm("AlgorithmParameters.Serpent", PREFIX + "$AlgParams"); + addGMacAlgorithm(provider, "SERPENT", PREFIX + "$SerpentGMAC", PREFIX + "$KeyGen"); } } } diff --git a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SymmetricAlgorithmProvider.java b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SymmetricAlgorithmProvider.java new file mode 100644 index 00000000..49656c27 --- /dev/null +++ b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SymmetricAlgorithmProvider.java @@ -0,0 +1,21 @@ +package org.bouncycastle.jcajce.provider.symmetric; + +import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; +import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; + +abstract class SymmetricAlgorithmProvider + extends AlgorithmProvider +{ + protected void addGMacAlgorithm( + ConfigurableProvider provider, + String algorithm, + String algorithmClassName, + String keyGeneratorClassName) + { + provider.addAlgorithm("Mac." + algorithm + "-GMAC", algorithmClassName); + provider.addAlgorithm("Alg.Alias.Mac." + algorithm + "GMAC", algorithm + "-GMAC"); + + provider.addAlgorithm("KeyGenerator." + algorithm + "-GMAC", keyGeneratorClassName); + provider.addAlgorithm("Alg.Alias.KeyGenerator." + algorithm + "GMAC", algorithm + "-GMAC"); + } +} diff --git a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Twofish.java b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Twofish.java index 604e7663..67b9f663 100644 --- a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Twofish.java +++ b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Twofish.java @@ -3,14 +3,16 @@ package org.bouncycastle.jcajce.provider.symmetric; import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.TwofishEngine; +import org.bouncycastle.crypto.macs.GMac; import org.bouncycastle.crypto.modes.CBCBlockCipher; +import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider; import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters; import org.bouncycastle.jcajce.provider.symmetric.util.PBESecretKeyFactory; -import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; public final class Twofish { @@ -42,6 +44,14 @@ public final class Twofish } } + public static class GMAC + extends BaseMac + { + public GMAC() + { + super(new GMac(new GCMBlockCipher(new TwofishEngine()))); + } + } /** * PBEWithSHAAndTwofish-CBC @@ -77,7 +87,7 @@ public final class Twofish } public static class Mappings - extends AlgorithmProvider + extends SymmetricAlgorithmProvider { private static final String PREFIX = Twofish.class.getName(); @@ -95,6 +105,8 @@ public final class Twofish provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDTWOFISH-CBC", "PKCS12PBE"); provider.addAlgorithm("Cipher.PBEWITHSHAANDTWOFISH-CBC", PREFIX + "$PBEWithSHA"); provider.addAlgorithm("SecretKeyFactory.PBEWITHSHAANDTWOFISH-CBC", PREFIX + "$PBEWithSHAKeyFactory"); + + addGMacAlgorithm(provider, "Twofish", PREFIX + "$GMAC", PREFIX + "$KeyGen"); } } } diff --git a/src/main/java/org/bouncycastle/math/ec/ECPoint.java b/src/main/java/org/bouncycastle/math/ec/ECPoint.java index ac67152a..cbc5aaf4 100644 --- a/src/main/java/org/bouncycastle/math/ec/ECPoint.java +++ b/src/main/java/org/bouncycastle/math/ec/ECPoint.java @@ -1,593 +1,593 @@ -package org.bouncycastle.math.ec;
-
-import java.math.BigInteger;
-
-import org.bouncycastle.asn1.x9.X9IntegerConverter;
-
-/**
- * base class for points on elliptic curves.
- */
-public abstract class ECPoint
-{
- ECCurve curve;
- ECFieldElement x;
- ECFieldElement y;
-
- protected boolean withCompression;
-
- protected ECMultiplier multiplier = null;
-
- protected PreCompInfo preCompInfo = null;
-
- private static X9IntegerConverter converter = new X9IntegerConverter();
-
- protected ECPoint(ECCurve curve, ECFieldElement x, ECFieldElement y)
- {
- this.curve = curve;
- this.x = x;
- this.y = y;
- }
-
- public ECCurve getCurve()
- {
- return curve;
- }
-
- public ECFieldElement getX()
- {
- return x;
- }
-
- public ECFieldElement getY()
- {
- return y;
- }
-
- public boolean isInfinity()
- {
- return x == null && y == null;
- }
-
- public boolean isCompressed()
- {
- return withCompression;
- }
-
- public boolean equals(
- Object other)
- {
- if (other == this)
- {
- return true;
- }
-
- if (!(other instanceof ECPoint))
- {
- return false;
- }
-
- ECPoint o = (ECPoint)other;
-
- if (this.isInfinity())
- {
- return o.isInfinity();
- }
-
- return x.equals(o.x) && y.equals(o.y);
- }
-
- public int hashCode()
- {
- if (this.isInfinity())
- {
- return 0;
- }
-
- return x.hashCode() ^ y.hashCode();
- }
-
-// /**
-// * Mainly for testing. Explicitly set the <code>ECMultiplier</code>.
-// * @param multiplier The <code>ECMultiplier</code> to be used to multiply
-// * this <code>ECPoint</code>.
-// */
-// public void setECMultiplier(ECMultiplier multiplier)
-// {
-// this.multiplier = multiplier;
-// }
-
- /**
- * Sets the <code>PreCompInfo</code>. Used by <code>ECMultiplier</code>s
- * to save the precomputation for this <code>ECPoint</code> to store the
- * precomputation result for use by subsequent multiplication.
- * @param preCompInfo The values precomputed by the
- * <code>ECMultiplier</code>.
- */
- void setPreCompInfo(PreCompInfo preCompInfo)
- {
- this.preCompInfo = preCompInfo;
- }
-
- public byte[] getEncoded()
- {
- return getEncoded(withCompression);
- }
-
- public abstract byte[] getEncoded(boolean compressed);
-
- public abstract ECPoint add(ECPoint b);
- public abstract ECPoint subtract(ECPoint b);
- public abstract ECPoint negate();
- public abstract ECPoint twice();
-
- /**
- * Sets the default <code>ECMultiplier</code>, unless already set.
- */
- synchronized void assertECMultiplier()
- {
- if (this.multiplier == null)
- {
- this.multiplier = new FpNafMultiplier();
- }
- }
-
- /**
- * Multiplies this <code>ECPoint</code> by the given number.
- * @param k The multiplicator.
- * @return <code>k * this</code>.
- */
- public ECPoint multiply(BigInteger k)
- {
- if (k.signum() < 0)
- {
- throw new IllegalArgumentException("The multiplicator cannot be negative");
- }
-
- if (this.isInfinity())
- {
- return this;
- }
-
- if (k.signum() == 0)
- {
- return this.curve.getInfinity();
- }
-
- assertECMultiplier();
- return this.multiplier.multiply(this, k, preCompInfo);
- }
-
- /**
- * Elliptic curve points over Fp
- */
- public static class Fp extends ECPoint
- {
-
- /**
- * Create a point which encodes with point compression.
- *
- * @param curve the curve to use
- * @param x affine x co-ordinate
- * @param y affine y co-ordinate
- */
- public Fp(ECCurve curve, ECFieldElement x, ECFieldElement y)
- {
- this(curve, x, y, false);
- }
-
- /**
- * Create a point that encodes with or without point compresion.
- *
- * @param curve the curve to use
- * @param x affine x co-ordinate
- * @param y affine y co-ordinate
- * @param withCompression if true encode with point compression
- */
- public Fp(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
- {
- super(curve, x, y);
-
- if ((x != null && y == null) || (x == null && y != null))
- {
- throw new IllegalArgumentException("Exactly one of the field elements is null");
- }
-
- this.withCompression = withCompression;
- }
-
- /**
- * return the field element encoded with point compression. (S 4.3.6)
- */
- public byte[] getEncoded(boolean compressed)
- {
- if (this.isInfinity())
- {
- return new byte[1];
- }
-
- int qLength = converter.getByteLength(x);
-
- if (compressed)
- {
- byte PC;
-
- if (this.getY().toBigInteger().testBit(0))
- {
- PC = 0x03;
- }
- else
- {
- PC = 0x02;
- }
-
- byte[] X = converter.integerToBytes(this.getX().toBigInteger(), qLength);
- byte[] PO = new byte[X.length + 1];
-
- PO[0] = PC;
- System.arraycopy(X, 0, PO, 1, X.length);
-
- return PO;
- }
- else
- {
- byte[] X = converter.integerToBytes(this.getX().toBigInteger(), qLength);
- byte[] Y = converter.integerToBytes(this.getY().toBigInteger(), qLength);
- byte[] PO = new byte[X.length + Y.length + 1];
-
- PO[0] = 0x04;
- System.arraycopy(X, 0, PO, 1, X.length);
- System.arraycopy(Y, 0, PO, X.length + 1, Y.length);
-
- return PO;
- }
- }
-
- // B.3 pg 62
- public ECPoint add(ECPoint b)
- {
- if (this.isInfinity())
- {
- return b;
- }
-
- if (b.isInfinity())
- {
- return this;
- }
-
- // Check if b = this or b = -this
- if (this.x.equals(b.x))
- {
- if (this.y.equals(b.y))
- {
- // this = b, i.e. this must be doubled
- return this.twice();
- }
-
- // this = -b, i.e. the result is the point at infinity
- return this.curve.getInfinity();
- }
-
- ECFieldElement gamma = b.y.subtract(this.y).divide(b.x.subtract(this.x));
-
- ECFieldElement x3 = gamma.square().subtract(this.x).subtract(b.x);
- ECFieldElement y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y);
-
- return new ECPoint.Fp(curve, x3, y3, withCompression);
- }
-
- // B.3 pg 62
- public ECPoint twice()
- {
- if (this.isInfinity())
- {
- // Twice identity element (point at infinity) is identity
- return this;
- }
-
- if (this.y.toBigInteger().signum() == 0)
- {
- // if y1 == 0, then (x1, y1) == (x1, -y1)
- // and hence this = -this and thus 2(x1, y1) == infinity
- return this.curve.getInfinity();
- }
-
- ECFieldElement TWO = this.curve.fromBigInteger(BigInteger.valueOf(2));
- ECFieldElement THREE = this.curve.fromBigInteger(BigInteger.valueOf(3));
- ECFieldElement gamma = this.x.square().multiply(THREE).add(curve.a).divide(y.multiply(TWO));
-
- ECFieldElement x3 = gamma.square().subtract(this.x.multiply(TWO));
- ECFieldElement y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y);
-
- return new ECPoint.Fp(curve, x3, y3, this.withCompression);
- }
-
- // D.3.2 pg 102 (see Note:)
- public ECPoint subtract(ECPoint b)
- {
- if (b.isInfinity())
- {
- return this;
- }
-
- // Add -b
- return add(b.negate());
- }
-
- public ECPoint negate()
- {
- return new ECPoint.Fp(curve, this.x, this.y.negate(), this.withCompression);
- }
-
- /**
- * Sets the default <code>ECMultiplier</code>, unless already set.
- */
- synchronized void assertECMultiplier()
- {
- if (this.multiplier == null)
- {
- this.multiplier = new WNafMultiplier();
- }
- }
- }
-
- /**
- * Elliptic curve points over F2m
- */
- public static class F2m extends ECPoint
- {
- /**
- * @param curve base curve
- * @param x x point
- * @param y y point
- */
- public F2m(ECCurve curve, ECFieldElement x, ECFieldElement y)
- {
- this(curve, x, y, false);
- }
-
- /**
- * @param curve base curve
- * @param x x point
- * @param y y point
- * @param withCompression true if encode with point compression.
- */
- public F2m(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
- {
- super(curve, x, y);
-
- if ((x != null && y == null) || (x == null && y != null))
- {
- throw new IllegalArgumentException("Exactly one of the field elements is null");
- }
-
- if (x != null)
- {
- // Check if x and y are elements of the same field
- ECFieldElement.F2m.checkFieldElements(this.x, this.y);
-
- // Check if x and a are elements of the same field
- if (curve != null)
- {
- ECFieldElement.F2m.checkFieldElements(this.x, this.curve.getA());
- }
- }
-
- this.withCompression = withCompression;
- }
-
- /* (non-Javadoc)
- * @see org.bouncycastle.math.ec.ECPoint#getEncoded()
- */
- public byte[] getEncoded(boolean compressed)
- {
- if (this.isInfinity())
- {
- return new byte[1];
- }
-
- int byteCount = converter.getByteLength(this.x);
- byte[] X = converter.integerToBytes(this.getX().toBigInteger(), byteCount);
- byte[] PO;
-
- if (compressed)
- {
- // See X9.62 4.3.6 and 4.2.2
- PO = new byte[byteCount + 1];
-
- PO[0] = 0x02;
- // X9.62 4.2.2 and 4.3.6:
- // if x = 0 then ypTilde := 0, else ypTilde is the rightmost
- // bit of y * x^(-1)
- // if ypTilde = 0, then PC := 02, else PC := 03
- // Note: PC === PO[0]
- if (!(this.getX().toBigInteger().equals(ECConstants.ZERO)))
- {
- if (this.getY().multiply(this.getX().invert())
- .toBigInteger().testBit(0))
- {
- // ypTilde = 1, hence PC = 03
- PO[0] = 0x03;
- }
- }
-
- System.arraycopy(X, 0, PO, 1, byteCount);
- }
- else
- {
- byte[] Y = converter.integerToBytes(this.getY().toBigInteger(), byteCount);
-
- PO = new byte[byteCount + byteCount + 1];
-
- PO[0] = 0x04;
- System.arraycopy(X, 0, PO, 1, byteCount);
- System.arraycopy(Y, 0, PO, byteCount + 1, byteCount);
- }
-
- return PO;
- }
-
- /**
- * Check, if two <code>ECPoint</code>s can be added or subtracted.
- * @param a The first <code>ECPoint</code> to check.
- * @param b The second <code>ECPoint</code> to check.
- * @throws IllegalArgumentException if <code>a</code> and <code>b</code>
- * cannot be added.
- */
- private static void checkPoints(ECPoint a, ECPoint b)
- {
- // Check, if points are on the same curve
- if (!(a.curve.equals(b.curve)))
- {
- throw new IllegalArgumentException("Only points on the same "
- + "curve can be added or subtracted");
- }
-
-// ECFieldElement.F2m.checkFieldElements(a.x, b.x);
- }
-
- /* (non-Javadoc)
- * @see org.bouncycastle.math.ec.ECPoint#add(org.bouncycastle.math.ec.ECPoint)
- */
- public ECPoint add(ECPoint b)
- {
- checkPoints(this, b);
- return addSimple((ECPoint.F2m)b);
- }
-
- /**
- * Adds another <code>ECPoints.F2m</code> to <code>this</code> without
- * checking if both points are on the same curve. Used by multiplication
- * algorithms, because there all points are a multiple of the same point
- * and hence the checks can be omitted.
- * @param b The other <code>ECPoints.F2m</code> to add to
- * <code>this</code>.
- * @return <code>this + b</code>
- */
- public ECPoint.F2m addSimple(ECPoint.F2m b)
- {
- ECPoint.F2m other = b;
- if (this.isInfinity())
- {
- return other;
- }
-
- if (other.isInfinity())
- {
- return this;
- }
-
- ECFieldElement.F2m x2 = (ECFieldElement.F2m)other.getX();
- ECFieldElement.F2m y2 = (ECFieldElement.F2m)other.getY();
-
- // Check if other = this or other = -this
- if (this.x.equals(x2))
- {
- if (this.y.equals(y2))
- {
- // this = other, i.e. this must be doubled
- return (ECPoint.F2m)this.twice();
- }
-
- // this = -other, i.e. the result is the point at infinity
- return (ECPoint.F2m)this.curve.getInfinity();
- }
-
- ECFieldElement.F2m lambda
- = (ECFieldElement.F2m)(this.y.add(y2)).divide(this.x.add(x2));
-
- ECFieldElement.F2m x3
- = (ECFieldElement.F2m)lambda.square().add(lambda).add(this.x).add(x2).add(this.curve.getA());
-
- ECFieldElement.F2m y3
- = (ECFieldElement.F2m)lambda.multiply(this.x.add(x3)).add(x3).add(this.y);
-
- return new ECPoint.F2m(curve, x3, y3, withCompression);
- }
-
- /* (non-Javadoc)
- * @see org.bouncycastle.math.ec.ECPoint#subtract(org.bouncycastle.math.ec.ECPoint)
- */
- public ECPoint subtract(ECPoint b)
- {
- checkPoints(this, b);
- return subtractSimple((ECPoint.F2m)b);
- }
-
- /**
- * Subtracts another <code>ECPoints.F2m</code> from <code>this</code>
- * without checking if both points are on the same curve. Used by
- * multiplication algorithms, because there all points are a multiple
- * of the same point and hence the checks can be omitted.
- * @param b The other <code>ECPoints.F2m</code> to subtract from
- * <code>this</code>.
- * @return <code>this - b</code>
- */
- public ECPoint.F2m subtractSimple(ECPoint.F2m b)
- {
- if (b.isInfinity())
- {
- return this;
- }
-
- // Add -b
- return addSimple((ECPoint.F2m)b.negate());
- }
-
- /* (non-Javadoc)
- * @see org.bouncycastle.math.ec.ECPoint#twice()
- */
- public ECPoint twice()
- {
- if (this.isInfinity())
- {
- // Twice identity element (point at infinity) is identity
- return this;
- }
-
- if (this.x.toBigInteger().signum() == 0)
- {
- // if x1 == 0, then (x1, y1) == (x1, x1 + y1)
- // and hence this = -this and thus 2(x1, y1) == infinity
- return this.curve.getInfinity();
- }
-
- ECFieldElement.F2m lambda
- = (ECFieldElement.F2m)this.x.add(this.y.divide(this.x));
-
- ECFieldElement.F2m x3
- = (ECFieldElement.F2m)lambda.square().add(lambda).
- add(this.curve.getA());
-
- ECFieldElement ONE = this.curve.fromBigInteger(ECConstants.ONE);
- ECFieldElement.F2m y3
- = (ECFieldElement.F2m)this.x.square().add(
- x3.multiply(lambda.add(ONE)));
-
- return new ECPoint.F2m(this.curve, x3, y3, withCompression);
- }
-
- public ECPoint negate()
- {
- return new ECPoint.F2m(curve, this.getX(), this.getY().add(this.getX()), withCompression);
- }
-
- /**
- * Sets the appropriate <code>ECMultiplier</code>, unless already set.
- */
- synchronized void assertECMultiplier()
- {
- if (this.multiplier == null)
- {
- if (((ECCurve.F2m)this.curve).isKoblitz())
- {
- this.multiplier = new WTauNafMultiplier();
- }
- else
- {
- this.multiplier = new WNafMultiplier();
- }
- }
- }
- }
-}
+package org.bouncycastle.math.ec; + +import java.math.BigInteger; + +import org.bouncycastle.asn1.x9.X9IntegerConverter; + +/** + * base class for points on elliptic curves. + */ +public abstract class ECPoint +{ + ECCurve curve; + ECFieldElement x; + ECFieldElement y; + + protected boolean withCompression; + + protected ECMultiplier multiplier = null; + + protected PreCompInfo preCompInfo = null; + + private static X9IntegerConverter converter = new X9IntegerConverter(); + + protected ECPoint(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + this.curve = curve; + this.x = x; + this.y = y; + } + + public ECCurve getCurve() + { + return curve; + } + + public ECFieldElement getX() + { + return x; + } + + public ECFieldElement getY() + { + return y; + } + + public boolean isInfinity() + { + return x == null && y == null; + } + + public boolean isCompressed() + { + return withCompression; + } + + public boolean equals( + Object other) + { + if (other == this) + { + return true; + } + + if (!(other instanceof ECPoint)) + { + return false; + } + + ECPoint o = (ECPoint)other; + + if (this.isInfinity()) + { + return o.isInfinity(); + } + + return x.equals(o.x) && y.equals(o.y); + } + + public int hashCode() + { + if (this.isInfinity()) + { + return 0; + } + + return x.hashCode() ^ y.hashCode(); + } + +// /** +// * Mainly for testing. Explicitly set the <code>ECMultiplier</code>. +// * @param multiplier The <code>ECMultiplier</code> to be used to multiply +// * this <code>ECPoint</code>. +// */ +// public void setECMultiplier(ECMultiplier multiplier) +// { +// this.multiplier = multiplier; +// } + + /** + * Sets the <code>PreCompInfo</code>. Used by <code>ECMultiplier</code>s + * to save the precomputation for this <code>ECPoint</code> to store the + * precomputation result for use by subsequent multiplication. + * @param preCompInfo The values precomputed by the + * <code>ECMultiplier</code>. + */ + void setPreCompInfo(PreCompInfo preCompInfo) + { + this.preCompInfo = preCompInfo; + } + + public byte[] getEncoded() + { + return getEncoded(withCompression); + } + + public abstract byte[] getEncoded(boolean compressed); + + public abstract ECPoint add(ECPoint b); + public abstract ECPoint subtract(ECPoint b); + public abstract ECPoint negate(); + public abstract ECPoint twice(); + + /** + * Sets the default <code>ECMultiplier</code>, unless already set. + */ + synchronized void assertECMultiplier() + { + if (this.multiplier == null) + { + this.multiplier = new FpNafMultiplier(); + } + } + + /** + * Multiplies this <code>ECPoint</code> by the given number. + * @param k The multiplicator. + * @return <code>k * this</code>. + */ + public ECPoint multiply(BigInteger k) + { + if (k.signum() < 0) + { + throw new IllegalArgumentException("The multiplicator cannot be negative"); + } + + if (this.isInfinity()) + { + return this; + } + + if (k.signum() == 0) + { + return this.curve.getInfinity(); + } + + assertECMultiplier(); + return this.multiplier.multiply(this, k, preCompInfo); + } + + /** + * Elliptic curve points over Fp + */ + public static class Fp extends ECPoint + { + + /** + * Create a point which encodes with point compression. + * + * @param curve the curve to use + * @param x affine x co-ordinate + * @param y affine y co-ordinate + */ + public Fp(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + this(curve, x, y, false); + } + + /** + * Create a point that encodes with or without point compresion. + * + * @param curve the curve to use + * @param x affine x co-ordinate + * @param y affine y co-ordinate + * @param withCompression if true encode with point compression + */ + public Fp(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x != null && y == null) || (x == null && y != null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + /** + * return the field element encoded with point compression. (S 4.3.6) + */ + public byte[] getEncoded(boolean compressed) + { + if (this.isInfinity()) + { + return new byte[1]; + } + + int qLength = converter.getByteLength(x); + + if (compressed) + { + byte PC; + + if (this.getY().toBigInteger().testBit(0)) + { + PC = 0x03; + } + else + { + PC = 0x02; + } + + byte[] X = converter.integerToBytes(this.getX().toBigInteger(), qLength); + byte[] PO = new byte[X.length + 1]; + + PO[0] = PC; + System.arraycopy(X, 0, PO, 1, X.length); + + return PO; + } + else + { + byte[] X = converter.integerToBytes(this.getX().toBigInteger(), qLength); + byte[] Y = converter.integerToBytes(this.getY().toBigInteger(), qLength); + byte[] PO = new byte[X.length + Y.length + 1]; + + PO[0] = 0x04; + System.arraycopy(X, 0, PO, 1, X.length); + System.arraycopy(Y, 0, PO, X.length + 1, Y.length); + + return PO; + } + } + + // B.3 pg 62 + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + + if (b.isInfinity()) + { + return this; + } + + // Check if b = this or b = -this + if (this.x.equals(b.x)) + { + if (this.y.equals(b.y)) + { + // this = b, i.e. this must be doubled + return this.twice(); + } + + // this = -b, i.e. the result is the point at infinity + return this.curve.getInfinity(); + } + + ECFieldElement gamma = b.y.subtract(this.y).divide(b.x.subtract(this.x)); + + ECFieldElement x3 = gamma.square().subtract(this.x).subtract(b.x); + ECFieldElement y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y); + + return new ECPoint.Fp(curve, x3, y3, withCompression); + } + + // B.3 pg 62 + public ECPoint twice() + { + if (this.isInfinity()) + { + // Twice identity element (point at infinity) is identity + return this; + } + + if (this.y.toBigInteger().signum() == 0) + { + // if y1 == 0, then (x1, y1) == (x1, -y1) + // and hence this = -this and thus 2(x1, y1) == infinity + return this.curve.getInfinity(); + } + + ECFieldElement TWO = this.curve.fromBigInteger(BigInteger.valueOf(2)); + ECFieldElement THREE = this.curve.fromBigInteger(BigInteger.valueOf(3)); + ECFieldElement gamma = this.x.square().multiply(THREE).add(curve.a).divide(y.multiply(TWO)); + + ECFieldElement x3 = gamma.square().subtract(this.x.multiply(TWO)); + ECFieldElement y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y); + + return new ECPoint.Fp(curve, x3, y3, this.withCompression); + } + + // D.3.2 pg 102 (see Note:) + public ECPoint subtract(ECPoint b) + { + if (b.isInfinity()) + { + return this; + } + + // Add -b + return add(b.negate()); + } + + public ECPoint negate() + { + return new ECPoint.Fp(curve, this.x, this.y.negate(), this.withCompression); + } + + /** + * Sets the default <code>ECMultiplier</code>, unless already set. + */ + synchronized void assertECMultiplier() + { + if (this.multiplier == null) + { + this.multiplier = new WNafMultiplier(); + } + } + } + + /** + * Elliptic curve points over F2m + */ + public static class F2m extends ECPoint + { + /** + * @param curve base curve + * @param x x point + * @param y y point + */ + public F2m(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + this(curve, x, y, false); + } + + /** + * @param curve base curve + * @param x x point + * @param y y point + * @param withCompression true if encode with point compression. + */ + public F2m(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x != null && y == null) || (x == null && y != null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + if (x != null) + { + // Check if x and y are elements of the same field + ECFieldElement.F2m.checkFieldElements(this.x, this.y); + + // Check if x and a are elements of the same field + if (curve != null) + { + ECFieldElement.F2m.checkFieldElements(this.x, this.curve.getA()); + } + } + + this.withCompression = withCompression; + } + + /* (non-Javadoc) + * @see org.bouncycastle.math.ec.ECPoint#getEncoded() + */ + public byte[] getEncoded(boolean compressed) + { + if (this.isInfinity()) + { + return new byte[1]; + } + + int byteCount = converter.getByteLength(this.x); + byte[] X = converter.integerToBytes(this.getX().toBigInteger(), byteCount); + byte[] PO; + + if (compressed) + { + // See X9.62 4.3.6 and 4.2.2 + PO = new byte[byteCount + 1]; + + PO[0] = 0x02; + // X9.62 4.2.2 and 4.3.6: + // if x = 0 then ypTilde := 0, else ypTilde is the rightmost + // bit of y * x^(-1) + // if ypTilde = 0, then PC := 02, else PC := 03 + // Note: PC === PO[0] + if (!(this.getX().toBigInteger().equals(ECConstants.ZERO))) + { + if (this.getY().multiply(this.getX().invert()) + .toBigInteger().testBit(0)) + { + // ypTilde = 1, hence PC = 03 + PO[0] = 0x03; + } + } + + System.arraycopy(X, 0, PO, 1, byteCount); + } + else + { + byte[] Y = converter.integerToBytes(this.getY().toBigInteger(), byteCount); + + PO = new byte[byteCount + byteCount + 1]; + + PO[0] = 0x04; + System.arraycopy(X, 0, PO, 1, byteCount); + System.arraycopy(Y, 0, PO, byteCount + 1, byteCount); + } + + return PO; + } + + /** + * Check, if two <code>ECPoint</code>s can be added or subtracted. + * @param a The first <code>ECPoint</code> to check. + * @param b The second <code>ECPoint</code> to check. + * @throws IllegalArgumentException if <code>a</code> and <code>b</code> + * cannot be added. + */ + private static void checkPoints(ECPoint a, ECPoint b) + { + // Check, if points are on the same curve + if (!(a.curve.equals(b.curve))) + { + throw new IllegalArgumentException("Only points on the same " + + "curve can be added or subtracted"); + } + +// ECFieldElement.F2m.checkFieldElements(a.x, b.x); + } + + /* (non-Javadoc) + * @see org.bouncycastle.math.ec.ECPoint#add(org.bouncycastle.math.ec.ECPoint) + */ + public ECPoint add(ECPoint b) + { + checkPoints(this, b); + return addSimple((ECPoint.F2m)b); + } + + /** + * Adds another <code>ECPoints.F2m</code> to <code>this</code> without + * checking if both points are on the same curve. Used by multiplication + * algorithms, because there all points are a multiple of the same point + * and hence the checks can be omitted. + * @param b The other <code>ECPoints.F2m</code> to add to + * <code>this</code>. + * @return <code>this + b</code> + */ + public ECPoint.F2m addSimple(ECPoint.F2m b) + { + ECPoint.F2m other = b; + if (this.isInfinity()) + { + return other; + } + + if (other.isInfinity()) + { + return this; + } + + ECFieldElement.F2m x2 = (ECFieldElement.F2m)other.getX(); + ECFieldElement.F2m y2 = (ECFieldElement.F2m)other.getY(); + + // Check if other = this or other = -this + if (this.x.equals(x2)) + { + if (this.y.equals(y2)) + { + // this = other, i.e. this must be doubled + return (ECPoint.F2m)this.twice(); + } + + // this = -other, i.e. the result is the point at infinity + return (ECPoint.F2m)this.curve.getInfinity(); + } + + ECFieldElement.F2m lambda + = (ECFieldElement.F2m)(this.y.add(y2)).divide(this.x.add(x2)); + + ECFieldElement.F2m x3 + = (ECFieldElement.F2m)lambda.square().add(lambda).add(this.x).add(x2).add(this.curve.getA()); + + ECFieldElement.F2m y3 + = (ECFieldElement.F2m)lambda.multiply(this.x.add(x3)).add(x3).add(this.y); + + return new ECPoint.F2m(curve, x3, y3, withCompression); + } + + /* (non-Javadoc) + * @see org.bouncycastle.math.ec.ECPoint#subtract(org.bouncycastle.math.ec.ECPoint) + */ + public ECPoint subtract(ECPoint b) + { + checkPoints(this, b); + return subtractSimple((ECPoint.F2m)b); + } + + /** + * Subtracts another <code>ECPoints.F2m</code> from <code>this</code> + * without checking if both points are on the same curve. Used by + * multiplication algorithms, because there all points are a multiple + * of the same point and hence the checks can be omitted. + * @param b The other <code>ECPoints.F2m</code> to subtract from + * <code>this</code>. + * @return <code>this - b</code> + */ + public ECPoint.F2m subtractSimple(ECPoint.F2m b) + { + if (b.isInfinity()) + { + return this; + } + + // Add -b + return addSimple((ECPoint.F2m)b.negate()); + } + + /* (non-Javadoc) + * @see org.bouncycastle.math.ec.ECPoint#twice() + */ + public ECPoint twice() + { + if (this.isInfinity()) + { + // Twice identity element (point at infinity) is identity + return this; + } + + if (this.x.toBigInteger().signum() == 0) + { + // if x1 == 0, then (x1, y1) == (x1, x1 + y1) + // and hence this = -this and thus 2(x1, y1) == infinity + return this.curve.getInfinity(); + } + + ECFieldElement.F2m lambda + = (ECFieldElement.F2m)this.x.add(this.y.divide(this.x)); + + ECFieldElement.F2m x3 + = (ECFieldElement.F2m)lambda.square().add(lambda). + add(this.curve.getA()); + + ECFieldElement ONE = this.curve.fromBigInteger(ECConstants.ONE); + ECFieldElement.F2m y3 + = (ECFieldElement.F2m)this.x.square().add( + x3.multiply(lambda.add(ONE))); + + return new ECPoint.F2m(this.curve, x3, y3, withCompression); + } + + public ECPoint negate() + { + return new ECPoint.F2m(curve, this.getX(), this.getY().add(this.getX()), withCompression); + } + + /** + * Sets the appropriate <code>ECMultiplier</code>, unless already set. + */ + synchronized void assertECMultiplier() + { + if (this.multiplier == null) + { + if (((ECCurve.F2m)this.curve).isKoblitz()) + { + this.multiplier = new WTauNafMultiplier(); + } + else + { + this.multiplier = new WNafMultiplier(); + } + } + } + } +} diff --git a/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java b/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java index 5303ab85..58643a78 100644 --- a/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java +++ b/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java @@ -379,7 +379,19 @@ public class PGPSecretKey { return pub.isMasterKey(); } - + + /** + * Detect if the Secret Key's Private Key is empty or not + * + * @return boolean whether or not the private key is empty + */ + public boolean isPrivateKeyEmpty() + { + byte[] secKeyData = secret.getSecretKeyData(); + + return (secKeyData == null || secKeyData.length < 1); + } + /** * return the algorithm the key is encrypted with. * @@ -491,9 +503,12 @@ public class PGPSecretKey } // - // verify checksum + // verify and copy checksum // + data[pos] = encData[pos]; + data[pos + 1] = encData[pos + 1]; + int cs = ((encData[pos] << 8) & 0xff00) | (encData[pos + 1] & 0xff); int calcCs = 0; for (int j = 0; j < data.length - 2; j++) @@ -573,8 +588,7 @@ public class PGPSecretKey PBESecretKeyDecryptor decryptorFactory) throws PGPException { - byte[] secKeyData = secret.getSecretKeyData(); - if (secKeyData == null || secKeyData.length < 1) + if (isPrivateKeyEmpty()) { return null; } @@ -759,8 +773,8 @@ public class PGPSecretKey * password and the passed in algorithm. * * @param key the PGPSecretKey to be copied. - * @param oldKeyDecryptor the current password for key. - * @param newKeyEncryptor the encryptor for encrypting the secret key material. + * @param oldKeyDecryptor the current decryptor based on the current password for key. + * @param newKeyEncryptor a new encryptor based on a new password for encrypting the secret key material. */ public static PGPSecretKey copyWithNewPassword( PGPSecretKey key, @@ -768,7 +782,12 @@ public class PGPSecretKey PBESecretKeyEncryptor newKeyEncryptor) throws PGPException { - byte[] rawKeyData = key.extractKeyData(oldKeyDecryptor); + if (key.isPrivateKeyEmpty()) + { + throw new PGPException("no private key in this SecretKey - public key present only."); + } + + byte[] rawKeyData = key.extractKeyData(oldKeyDecryptor); int s2kUsage = key.secret.getS2KUsage(); byte[] iv = null; S2K s2k = null; @@ -796,13 +815,67 @@ public class PGPSecretKey } else { - keyData = newKeyEncryptor.encryptKeyData(rawKeyData, 0, rawKeyData.length); + if (key.secret.getPublicKeyPacket().getVersion() < 4) + { + // Version 2 or 3 - RSA Keys only - iv = newKeyEncryptor.getCipherIV(); + byte[] encKey = newKeyEncryptor.getKey(); + keyData = new byte[rawKeyData.length]; - s2k = newKeyEncryptor.getS2K(); + if (newKeyEncryptor.getS2K() != null) + { + throw new PGPException("MD5 Digest Calculator required for version 3 key encryptor."); + } + + // + // process 4 numbers + // + int pos = 0; + for (int i = 0; i != 4; i++) + { + int encLen = (((rawKeyData[pos] << 8) | (rawKeyData[pos + 1] & 0xff)) + 7) / 8; - newEncAlgorithm = newKeyEncryptor.getAlgorithm(); + keyData[pos] = rawKeyData[pos]; + keyData[pos + 1] = rawKeyData[pos + 1]; + + byte[] tmp; + if (i == 0) + { + tmp = newKeyEncryptor.encryptKeyData(encKey, rawKeyData, pos + 2, encLen); + iv = newKeyEncryptor.getCipherIV(); + + } + else + { + byte[] tmpIv = new byte[iv.length]; + + System.arraycopy(keyData, pos - iv.length, tmpIv, 0, tmpIv.length); + tmp = newKeyEncryptor.encryptKeyData(encKey, tmpIv, rawKeyData, pos + 2, encLen); + } + + System.arraycopy(tmp, 0, keyData, pos + 2, tmp.length); + pos += 2 + encLen; + } + + // + // copy in checksum. + // + keyData[pos] = rawKeyData[pos]; + keyData[pos + 1] = rawKeyData[pos + 1]; + + s2k = newKeyEncryptor.getS2K(); + newEncAlgorithm = newKeyEncryptor.getAlgorithm(); + } + else + { + keyData = newKeyEncryptor.encryptKeyData(rawKeyData, 0, rawKeyData.length); + + iv = newKeyEncryptor.getCipherIV(); + + s2k = newKeyEncryptor.getS2K(); + + newEncAlgorithm = newKeyEncryptor.getAlgorithm(); + } } SecretKeyPacket secret; @@ -819,6 +892,7 @@ public class PGPSecretKey return new PGPSecretKey(secret, key.pub); } + /** * Return a copy of the passed in secret key, encrypted using a new * password and the passed in algorithm. diff --git a/src/main/java/org/bouncycastle/openpgp/PGPSecretKeyRing.java b/src/main/java/org/bouncycastle/openpgp/PGPSecretKeyRing.java index fd66178f..2e30e737 100644 --- a/src/main/java/org/bouncycastle/openpgp/PGPSecretKeyRing.java +++ b/src/main/java/org/bouncycastle/openpgp/PGPSecretKeyRing.java @@ -20,6 +20,8 @@ import org.bouncycastle.bcpg.SecretKeyPacket; import org.bouncycastle.bcpg.SecretSubkeyPacket; import org.bouncycastle.bcpg.TrustPacket; import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator; +import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; /** @@ -312,6 +314,7 @@ public class PGPSecretKeyRing * @param newEncAlgorithm the algorithm to be used for the encryption. * @param rand source of randomness. * @param provider name of the provider to use + * @deprecated use version taking PBESecretKeyEncryptor/PBESecretKeyDecryptor */ public static PGPSecretKeyRing copyWithNewPassword( PGPSecretKeyRing ring, @@ -335,6 +338,7 @@ public class PGPSecretKeyRing * @param newEncAlgorithm the algorithm to be used for the encryption. * @param rand source of randomness. * @param provider provider to use + * @deprecated use version taking PBESecretKeyEncryptor/PBESecretKeyDecryptor */ public static PGPSecretKeyRing copyWithNewPassword( PGPSecretKeyRing ring, @@ -356,6 +360,40 @@ public class PGPSecretKeyRing } /** + * Return a copy of the passed in secret key ring, with the private keys (where present) associated with the master key and sub keys + * are encrypted using a new password and the passed in algorithm. + * + * @param ring the PGPSecretKeyRing to be copied. + * @param oldKeyDecryptor the current decryptor based on the current password for key. + * @param newKeyEncryptor a new encryptor based on a new password for encrypting the secret key material. + * @return the updated key ring. + */ + public static PGPSecretKeyRing copyWithNewPassword( + PGPSecretKeyRing ring, + PBESecretKeyDecryptor oldKeyDecryptor, + PBESecretKeyEncryptor newKeyEncryptor) + throws PGPException + { + List newKeys = new ArrayList(ring.keys.size()); + + for (Iterator keys = ring.getSecretKeys(); keys.hasNext();) + { + PGPSecretKey key = (PGPSecretKey)keys.next(); + + if (key.isPrivateKeyEmpty()) + { + newKeys.add(key); + } + else + { + newKeys.add(PGPSecretKey.copyWithNewPassword(key, oldKeyDecryptor, newKeyEncryptor)); + } + } + + return new PGPSecretKeyRing(newKeys, ring.extraPubKeys); + } + + /** * Returns a new key ring with the secret key passed in either added or * replacing an existing one with the same key ID. * diff --git a/src/main/java/org/bouncycastle/openpgp/operator/PBESecretKeyEncryptor.java b/src/main/java/org/bouncycastle/openpgp/operator/PBESecretKeyEncryptor.java index 905e53de..a0d44570 100644 --- a/src/main/java/org/bouncycastle/openpgp/operator/PBESecretKeyEncryptor.java +++ b/src/main/java/org/bouncycastle/openpgp/operator/PBESecretKeyEncryptor.java @@ -2,6 +2,7 @@ package org.bouncycastle.openpgp.operator; import java.security.SecureRandom; +import org.bouncycastle.bcpg.HashAlgorithmTags; import org.bouncycastle.bcpg.S2K; import org.bouncycastle.openpgp.PGPException; @@ -43,7 +44,7 @@ public abstract class PBESecretKeyEncryptor public byte[] getKey() throws PGPException { - if (s2k == null) + if (s2k == null && s2kDigestCalculator.getAlgorithm() != HashAlgorithmTags.MD5) { byte[] iv = new byte[8]; @@ -69,5 +70,17 @@ public abstract class PBESecretKeyEncryptor public abstract byte[] encryptKeyData(byte[] key, byte[] keyData, int keyOff, int keyLen) throws PGPException; + /** + * Encrypt the passed in keyData using the key and the iv provided. + * <p> + * This method is only used for processing version 3 keys. + * </p> + */ + public byte[] encryptKeyData(byte[] key, byte[] iv, byte[] keyData, int keyOff, int keyLen) + throws PGPException + { + throw new PGPException("encryption of version 3 keys not supported."); + } + public abstract byte[] getCipherIV(); } diff --git a/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPBESecretKeyEncryptorBuilder.java b/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPBESecretKeyEncryptorBuilder.java index 7e475e0a..2258484e 100644 --- a/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPBESecretKeyEncryptorBuilder.java +++ b/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPBESecretKeyEncryptorBuilder.java @@ -32,6 +32,13 @@ public class BcPBESecretKeyEncryptorBuilder this(encAlgorithm, new SHA1PGPDigestCalculator(), s2kCount); } + /** + * Create a builder which will make encryptors using the passed in digest calculator. If a MD5 calculator is + * passed in the builder will assume the encryptors are for use with version 3 keys. + * + * @param encAlgorithm encryption algorithm to use. + * @param s2kDigestCalculator digest calculator to use. + */ public BcPBESecretKeyEncryptorBuilder(int encAlgorithm, PGPDigestCalculator s2kDigestCalculator) { this(encAlgorithm, s2kDigestCalculator, 0x60); @@ -85,18 +92,31 @@ public class BcPBESecretKeyEncryptorBuilder public byte[] encryptKeyData(byte[] key, byte[] keyData, int keyOff, int keyLen) throws PGPException { + return encryptKeyData(key, null, keyData, keyOff, keyLen); + } + + public byte[] encryptKeyData(byte[] key, byte[] iv, byte[] keyData, int keyOff, int keyLen) + throws PGPException + { try { - if (this.random == null) - { - this.random = new SecureRandom(); - } - BlockCipher engine = BcImplProvider.createBlockCipher(this.encAlgorithm); - iv = new byte[engine.getBlockSize()]; + if (iv != null) + { // to deal with V3 key encryption + this.iv = iv; + } + else + { + if (this.random == null) + { + this.random = new SecureRandom(); + } + + this.iv = iv = new byte[engine.getBlockSize()]; - this.random.nextBytes(iv); + this.random.nextBytes(iv); + } BufferedBlockCipher c = BcUtil.createSymmetricKeyWrapper(true, engine, key, iv); diff --git a/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBESecretKeyEncryptorBuilder.java b/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBESecretKeyEncryptorBuilder.java index 4d7c94d7..0890ddb9 100644 --- a/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBESecretKeyEncryptorBuilder.java +++ b/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBESecretKeyEncryptorBuilder.java @@ -1,5 +1,6 @@ package org.bouncycastle.openpgp.operator.jcajce; +import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Provider; import java.security.SecureRandom; @@ -7,6 +8,7 @@ import java.security.SecureRandom; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; +import javax.crypto.spec.IvParameterSpec; import org.bouncycastle.jcajce.DefaultJcaJceHelper; import org.bouncycastle.jcajce.NamedJcaJceHelper; @@ -29,7 +31,7 @@ public class JcePBESecretKeyEncryptorBuilder } /** - * Create an SecretKeyEncryptorBuilder with the S2K count different to the default of 0x60. + * Create a SecretKeyEncryptorBuilder with the S2K count different to the default of 0x60. * * @param encAlgorithm encryption algorithm to use. * @param s2kCount iteration count to use for S2K function. @@ -39,6 +41,13 @@ public class JcePBESecretKeyEncryptorBuilder this(encAlgorithm, new SHA1PGPDigestCalculator(), s2kCount); } + /** + * Create a builder which will make encryptors using the passed in digest calculator. If a MD5 calculator is + * passed in the builder will assume the encryptors are for use with version 3 keys. + * + * @param encAlgorithm encryption algorithm to use. + * @param s2kDigestCalculator digest calculator to use. + */ public JcePBESecretKeyEncryptorBuilder(int encAlgorithm, PGPDigestCalculator s2kDigestCalculator) { this(encAlgorithm, s2kDigestCalculator, 0x60); @@ -131,6 +140,37 @@ public class JcePBESecretKeyEncryptorBuilder } } + public byte[] encryptKeyData(byte[] key, byte[] iv, byte[] keyData, int keyOff, int keyLen) + throws PGPException + { + try + { + c = helper.createCipher(PGPUtil.getSymmetricCipherName(this.encAlgorithm) + "/CFB/NoPadding"); + + c.init(Cipher.ENCRYPT_MODE, PGPUtil.makeSymmetricKey(this.encAlgorithm, key), new IvParameterSpec(iv)); + + this.iv = iv; + + return c.doFinal(keyData, keyOff, keyLen); + } + catch (IllegalBlockSizeException e) + { + throw new PGPException("illegal block size: " + e.getMessage(), e); + } + catch (BadPaddingException e) + { + throw new PGPException("bad padding: " + e.getMessage(), e); + } + catch (InvalidKeyException e) + { + throw new PGPException("invalid key: " + e.getMessage(), e); + } + catch (InvalidAlgorithmParameterException e) + { + throw new PGPException("invalid iv: " + e.getMessage(), e); + } + } + public byte[] getCipherIV() { return iv; diff --git a/src/main/java/org/bouncycastle/util/Arrays.java b/src/main/java/org/bouncycastle/util/Arrays.java index bbabc8b8..6c825cd2 100644 --- a/src/main/java/org/bouncycastle/util/Arrays.java +++ b/src/main/java/org/bouncycastle/util/Arrays.java @@ -716,13 +716,21 @@ public final class Arrays return rv; } + else if (d == null) + { + return concatenate(a, b, c); + } + else if (c == null) + { + return concatenate(a, b, d); + } else if (b == null) { return concatenate(a, c, d); } else { - return concatenate(a, b, d); + return concatenate(b, c, d); } } } diff --git a/src/main/java/org/bouncycastle/util/Memoable.java b/src/main/java/org/bouncycastle/util/Memoable.java new file mode 100644 index 00000000..0be91711 --- /dev/null +++ b/src/main/java/org/bouncycastle/util/Memoable.java @@ -0,0 +1,23 @@ +package org.bouncycastle.util; + +public interface Memoable +{ + /** + * Produce a copy of this object with its configuration and in its current state. + * <p/> + * The returned object may be used simply to store the state, or may be used as a similar object + * starting from the copied state. + */ + public Memoable copy(); + + /** + * Restore a copied object state into this object. + * <p/> + * Implementations of this method <em>should</em> try to avoid or minimise memory allocation to perform the reset. + * + * @param other an object originally {@link #copy() copied} from an object of the same type as this instance. + * @throws ClassCastException if the provided object is not of the correct type. + * @throws MemoableResetException if the <b>other</b> parameter is in some other way invalid. + */ + public void reset(Memoable other); +} diff --git a/src/main/java/org/bouncycastle/util/MemoableResetException.java b/src/main/java/org/bouncycastle/util/MemoableResetException.java new file mode 100644 index 00000000..6552bd49 --- /dev/null +++ b/src/main/java/org/bouncycastle/util/MemoableResetException.java @@ -0,0 +1,22 @@ +package org.bouncycastle.util; + +/** + * Exception to be thrown on a failure to reset an object implementing Memoable. + * <p> + * The exception extends ClassCastException to enable users to have a single handling case, + * only introducing specific handling of this one if required. + * </p> + */ +public class MemoableResetException + extends ClassCastException +{ + /** + * Basic Constructor. + * + * @param msg message to be associated with this exception. + */ + public MemoableResetException(String msg) + { + super(msg); + } +} diff --git a/src/main/java/org/bouncycastle/util/encoders/Base64.java b/src/main/java/org/bouncycastle/util/encoders/Base64.java index 9b7d0e43..83806295 100644 --- a/src/main/java/org/bouncycastle/util/encoders/Base64.java +++ b/src/main/java/org/bouncycastle/util/encoders/Base64.java @@ -1,151 +1,151 @@ -package org.bouncycastle.util.encoders;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-import org.bouncycastle.util.Strings;
-
-public class Base64
-{
- private static final Encoder encoder = new Base64Encoder();
-
- public static String toBase64String(
- byte[] data)
- {
- return toBase64String(data, 0, data.length);
- }
-
- public static String toBase64String(
- byte[] data,
- int off,
- int length)
- {
- byte[] encoded = encode(data, off, length);
- return Strings.fromByteArray(encoded);
- }
-
- /**
- * encode the input data producing a base 64 encoded byte array.
- *
- * @return a byte array containing the base 64 encoded data.
- */
- public static byte[] encode(
- byte[] data)
- {
- return encode(data, 0, data.length);
- }
-
- /**
- * encode the input data producing a base 64 encoded byte array.
- *
- * @return a byte array containing the base 64 encoded data.
- */
- public static byte[] encode(
- byte[] data,
- int off,
- int length)
- {
- int len = (length + 2) / 3 * 4;
- ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
-
- try
- {
- encoder.encode(data, off, length, bOut);
- }
- catch (Exception e)
- {
- throw new EncoderException("exception encoding base64 string: " + e.getMessage(), e);
- }
-
- return bOut.toByteArray();
- }
-
- /**
- * Encode the byte data to base 64 writing it to the given output stream.
- *
- * @return the number of bytes produced.
- */
- public static int encode(
- byte[] data,
- OutputStream out)
- throws IOException
- {
- return encoder.encode(data, 0, data.length, out);
- }
-
- /**
- * Encode the byte data to base 64 writing it to the given output stream.
- *
- * @return the number of bytes produced.
- */
- public static int encode(
- byte[] data,
- int off,
- int length,
- OutputStream out)
- throws IOException
- {
- return encoder.encode(data, off, length, out);
- }
-
- /**
- * decode the base 64 encoded input data. It is assumed the input data is valid.
- *
- * @return a byte array representing the decoded data.
- */
- public static byte[] decode(
- byte[] data)
- {
- int len = data.length / 4 * 3;
- ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
-
- try
- {
- encoder.decode(data, 0, data.length, bOut);
- }
- catch (Exception e)
- {
- throw new DecoderException("unable to decode base64 data: " + e.getMessage(), e);
- }
-
- return bOut.toByteArray();
- }
-
- /**
- * decode the base 64 encoded String data - whitespace will be ignored.
- *
- * @return a byte array representing the decoded data.
- */
- public static byte[] decode(
- String data)
- {
- int len = data.length() / 4 * 3;
- ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
-
- try
- {
- encoder.decode(data, bOut);
- }
- catch (Exception e)
- {
- throw new DecoderException("unable to decode base64 string: " + e.getMessage(), e);
- }
-
- return bOut.toByteArray();
- }
-
- /**
- * decode the base 64 encoded String data writing it to the given output stream,
- * whitespace characters will be ignored.
- *
- * @return the number of bytes produced.
- */
- public static int decode(
- String data,
- OutputStream out)
- throws IOException
- {
- return encoder.decode(data, out);
- }
-}
+package org.bouncycastle.util.encoders; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import org.bouncycastle.util.Strings; + +public class Base64 +{ + private static final Encoder encoder = new Base64Encoder(); + + public static String toBase64String( + byte[] data) + { + return toBase64String(data, 0, data.length); + } + + public static String toBase64String( + byte[] data, + int off, + int length) + { + byte[] encoded = encode(data, off, length); + return Strings.fromByteArray(encoded); + } + + /** + * encode the input data producing a base 64 encoded byte array. + * + * @return a byte array containing the base 64 encoded data. + */ + public static byte[] encode( + byte[] data) + { + return encode(data, 0, data.length); + } + + /** + * encode the input data producing a base 64 encoded byte array. + * + * @return a byte array containing the base 64 encoded data. + */ + public static byte[] encode( + byte[] data, + int off, + int length) + { + int len = (length + 2) / 3 * 4; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(len); + + try + { + encoder.encode(data, off, length, bOut); + } + catch (Exception e) + { + throw new EncoderException("exception encoding base64 string: " + e.getMessage(), e); + } + + return bOut.toByteArray(); + } + + /** + * Encode the byte data to base 64 writing it to the given output stream. + * + * @return the number of bytes produced. + */ + public static int encode( + byte[] data, + OutputStream out) + throws IOException + { + return encoder.encode(data, 0, data.length, out); + } + + /** + * Encode the byte data to base 64 writing it to the given output stream. + * + * @return the number of bytes produced. + */ + public static int encode( + byte[] data, + int off, + int length, + OutputStream out) + throws IOException + { + return encoder.encode(data, off, length, out); + } + + /** + * decode the base 64 encoded input data. It is assumed the input data is valid. + * + * @return a byte array representing the decoded data. + */ + public static byte[] decode( + byte[] data) + { + int len = data.length / 4 * 3; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(len); + + try + { + encoder.decode(data, 0, data.length, bOut); + } + catch (Exception e) + { + throw new DecoderException("unable to decode base64 data: " + e.getMessage(), e); + } + + return bOut.toByteArray(); + } + + /** + * decode the base 64 encoded String data - whitespace will be ignored. + * + * @return a byte array representing the decoded data. + */ + public static byte[] decode( + String data) + { + int len = data.length() / 4 * 3; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(len); + + try + { + encoder.decode(data, bOut); + } + catch (Exception e) + { + throw new DecoderException("unable to decode base64 string: " + e.getMessage(), e); + } + + return bOut.toByteArray(); + } + + /** + * decode the base 64 encoded String data writing it to the given output stream, + * whitespace characters will be ignored. + * + * @return the number of bytes produced. + */ + public static int decode( + String data, + OutputStream out) + throws IOException + { + return encoder.decode(data, out); + } +} diff --git a/src/main/java/org/bouncycastle/util/encoders/Hex.java b/src/main/java/org/bouncycastle/util/encoders/Hex.java index 6a1c0f8e..d49f1ef4 100644 --- a/src/main/java/org/bouncycastle/util/encoders/Hex.java +++ b/src/main/java/org/bouncycastle/util/encoders/Hex.java @@ -1,148 +1,148 @@ -package org.bouncycastle.util.encoders;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-import org.bouncycastle.util.Strings;
-
-public class Hex
-{
- private static final Encoder encoder = new HexEncoder();
-
- public static String toHexString(
- byte[] data)
- {
- return toHexString(data, 0, data.length);
- }
-
- public static String toHexString(
- byte[] data,
- int off,
- int length)
- {
- byte[] encoded = encode(data, off, length);
- return Strings.fromByteArray(encoded);
- }
-
- /**
- * encode the input data producing a Hex encoded byte array.
- *
- * @return a byte array containing the Hex encoded data.
- */
- public static byte[] encode(
- byte[] data)
- {
- return encode(data, 0, data.length);
- }
-
- /**
- * encode the input data producing a Hex encoded byte array.
- *
- * @return a byte array containing the Hex encoded data.
- */
- public static byte[] encode(
- byte[] data,
- int off,
- int length)
- {
- ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
- try
- {
- encoder.encode(data, off, length, bOut);
- }
- catch (Exception e)
- {
- throw new EncoderException("exception encoding Hex string: " + e.getMessage(), e);
- }
-
- return bOut.toByteArray();
- }
-
- /**
- * Hex encode the byte data writing it to the given output stream.
- *
- * @return the number of bytes produced.
- */
- public static int encode(
- byte[] data,
- OutputStream out)
- throws IOException
- {
- return encoder.encode(data, 0, data.length, out);
- }
-
- /**
- * Hex encode the byte data writing it to the given output stream.
- *
- * @return the number of bytes produced.
- */
- public static int encode(
- byte[] data,
- int off,
- int length,
- OutputStream out)
- throws IOException
- {
- return encoder.encode(data, off, length, out);
- }
-
- /**
- * decode the Hex encoded input data. It is assumed the input data is valid.
- *
- * @return a byte array representing the decoded data.
- */
- public static byte[] decode(
- byte[] data)
- {
- ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
- try
- {
- encoder.decode(data, 0, data.length, bOut);
- }
- catch (Exception e)
- {
- throw new DecoderException("exception decoding Hex data: " + e.getMessage(), e);
- }
-
- return bOut.toByteArray();
- }
-
- /**
- * decode the Hex encoded String data - whitespace will be ignored.
- *
- * @return a byte array representing the decoded data.
- */
- public static byte[] decode(
- String data)
- {
- ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
- try
- {
- encoder.decode(data, bOut);
- }
- catch (Exception e)
- {
- throw new DecoderException("exception decoding Hex string: " + e.getMessage(), e);
- }
-
- return bOut.toByteArray();
- }
-
- /**
- * decode the Hex encoded String data writing it to the given output stream,
- * whitespace characters will be ignored.
- *
- * @return the number of bytes produced.
- */
- public static int decode(
- String data,
- OutputStream out)
- throws IOException
- {
- return encoder.decode(data, out);
- }
-}
+package org.bouncycastle.util.encoders; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import org.bouncycastle.util.Strings; + +public class Hex +{ + private static final Encoder encoder = new HexEncoder(); + + public static String toHexString( + byte[] data) + { + return toHexString(data, 0, data.length); + } + + public static String toHexString( + byte[] data, + int off, + int length) + { + byte[] encoded = encode(data, off, length); + return Strings.fromByteArray(encoded); + } + + /** + * encode the input data producing a Hex encoded byte array. + * + * @return a byte array containing the Hex encoded data. + */ + public static byte[] encode( + byte[] data) + { + return encode(data, 0, data.length); + } + + /** + * encode the input data producing a Hex encoded byte array. + * + * @return a byte array containing the Hex encoded data. + */ + public static byte[] encode( + byte[] data, + int off, + int length) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + try + { + encoder.encode(data, off, length, bOut); + } + catch (Exception e) + { + throw new EncoderException("exception encoding Hex string: " + e.getMessage(), e); + } + + return bOut.toByteArray(); + } + + /** + * Hex encode the byte data writing it to the given output stream. + * + * @return the number of bytes produced. + */ + public static int encode( + byte[] data, + OutputStream out) + throws IOException + { + return encoder.encode(data, 0, data.length, out); + } + + /** + * Hex encode the byte data writing it to the given output stream. + * + * @return the number of bytes produced. + */ + public static int encode( + byte[] data, + int off, + int length, + OutputStream out) + throws IOException + { + return encoder.encode(data, off, length, out); + } + + /** + * decode the Hex encoded input data. It is assumed the input data is valid. + * + * @return a byte array representing the decoded data. + */ + public static byte[] decode( + byte[] data) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + try + { + encoder.decode(data, 0, data.length, bOut); + } + catch (Exception e) + { + throw new DecoderException("exception decoding Hex data: " + e.getMessage(), e); + } + + return bOut.toByteArray(); + } + + /** + * decode the Hex encoded String data - whitespace will be ignored. + * + * @return a byte array representing the decoded data. + */ + public static byte[] decode( + String data) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + try + { + encoder.decode(data, bOut); + } + catch (Exception e) + { + throw new DecoderException("exception decoding Hex string: " + e.getMessage(), e); + } + + return bOut.toByteArray(); + } + + /** + * decode the Hex encoded String data writing it to the given output stream, + * whitespace characters will be ignored. + * + * @return the number of bytes produced. + */ + public static int decode( + String data, + OutputStream out) + throws IOException + { + return encoder.decode(data, out); + } +} diff --git a/src/test/java/org/bouncycastle/cert/ocsp/test/OCSPTest.java b/src/test/java/org/bouncycastle/cert/ocsp/test/OCSPTest.java index 06a8ef29..5df298ae 100644 --- a/src/test/java/org/bouncycastle/cert/ocsp/test/OCSPTest.java +++ b/src/test/java/org/bouncycastle/cert/ocsp/test/OCSPTest.java @@ -1,5 +1,6 @@ package org.bouncycastle.cert.ocsp.test; +import java.io.IOException; import java.math.BigInteger; import java.security.KeyPair; import java.security.Security; @@ -931,6 +932,35 @@ public class OCSPTest testRSA(); testIrregularVersionReq(); testInvalidResp(); + + // + // Empty data test + // + try + { + response = new OCSPResp(new byte[0]); + fail("no exception thrown"); + } + catch (IOException e) + { + if (!e.getMessage().equals("malformed response: no response data found")) + { + fail("wrong exception"); + } + } + + try + { + req = new OCSPReq(new byte[0]); + fail("no exception thrown"); + } + catch (IOException e) + { + if (!e.getMessage().equals("malformed request: no request data found")) + { + fail("wrong exception"); + } + } } public static void main( diff --git a/src/test/java/org/bouncycastle/crypto/prng/test/AllTests.java b/src/test/java/org/bouncycastle/crypto/prng/test/AllTests.java new file mode 100644 index 00000000..4c0504f8 --- /dev/null +++ b/src/test/java/org/bouncycastle/crypto/prng/test/AllTests.java @@ -0,0 +1,39 @@ +package org.bouncycastle.crypto.prng.test; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.bouncycastle.util.test.SimpleTestResult; + +public class AllTests + extends TestCase +{ + public void testCrypto() + { + org.bouncycastle.util.test.Test[] tests = RegressionTest.tests; + + for (int i = 0; i != tests.length; i++) + { + SimpleTestResult result = (SimpleTestResult)tests[i].perform(); + + if (!result.isSuccessful()) + { + fail(result.toString()); + } + } + } + + public static void main (String[] args) + { + junit.textui.TestRunner.run(suite()); + } + + public static Test suite() + { + TestSuite suite = new TestSuite("Lightweight Crypto PRNG Tests"); + + suite.addTestSuite(AllTests.class); + + return suite; + } +} diff --git a/src/test/java/org/bouncycastle/crypto/prng/test/CTRDRBGTest.java b/src/test/java/org/bouncycastle/crypto/prng/test/CTRDRBGTest.java new file mode 100644 index 00000000..ca555c18 --- /dev/null +++ b/src/test/java/org/bouncycastle/crypto/prng/test/CTRDRBGTest.java @@ -0,0 +1,513 @@ +package org.bouncycastle.crypto.prng.test; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.engines.AESEngine; +import org.bouncycastle.crypto.engines.AESFastEngine; +import org.bouncycastle.crypto.engines.DESedeEngine; +import org.bouncycastle.crypto.params.DESedeParameters; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.prng.drbg.CTRSP800DRBG; +import org.bouncycastle.crypto.prng.drbg.SP80090DRBG; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; + +/** + * CTR DRBG Test + */ +public class CTRDRBGTest + extends SimpleTest +{ + public String getName() + { + return "CTRDRBGTest"; + } + + public static void main(String[] args) + { + runTest(new CTRDRBGTest()); + } + + private DRBGTestVector[] createTestVectorData() + { + return new DRBGTestVector[] + { + new DRBGTestVector( + new DESedeEngine(), 168, + new Bit232EntropyProvider().get(232), + false, + "20212223242526", + 112, + new String[] + { + "ABC88224514D0316EA3D48AEE3C9A2B4", + "D3D3F372E43E7ABDC4FA293743EED076" + } + ), + new DRBGTestVector( + new DESedeEngine(), 168, + new Bit232EntropyProvider().get(232), + false, + "20212223242526", + 112, + new String[] + { + "D4564EE072ACA5BD279536E14F94CB12", + "1CCD9AFEF15A9679BA75E35225585DEA" + } + ) + .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C") + .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBC"), + new DRBGTestVector( + new DESedeEngine(), 168, + new Bit232EntropyProvider().get(232), + false, + "20212223242526", + 112, + new String[] + { + "760BED7D92B083B10AF31CF0656081EB", + "FD1AC41482384D823CF3FD6F0E6C88B3" + } + ) + .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C"), + new DRBGTestVector( + new DESedeEngine(), 168, + new Bit232EntropyProvider().get(232), + false, + "20212223242526", + 112, + new String[] + { + "7A4C1D7ADC8A67FDB50100ED23583A2C", + "43044D311C0E07541CA5C8B0916976B2" + } + ) + .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C") + .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C") + .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBC"), + new DRBGTestVector( + new DESedeEngine(), 168, + new Bit232EntropyProvider().get(232), + true, + "20212223242526", + 112, + new String[] + { + "8FB78ABCA75C9F284E974E36141866BC", + "9D9745FF31C42A4488CBB771B13B5D86" + } + ), + new DRBGTestVector( + new DESedeEngine(), 168, + new Bit232EntropyProvider().get(232), + true, + "20212223242526", + 112, + new String[] + { + "0E389920A09B485AA4ABD0CA7E60D89C", + "F4478EC6659A0D3577625B0C73A211DD" + } + ) + .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C") + .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBC"), + new DRBGTestVector( + new DESedeEngine(), 168, + new Bit232EntropyProvider().get(232), + true, + "20212223242526", + 112, + new String[] + { + "64983055D014550B39DE699E43130B64", + "035FDDA8582A2214EC722C410A8D95D3" + } + ) + .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C"), + new DRBGTestVector( + new DESedeEngine(), 168, + new Bit232EntropyProvider().get(232), + true, + "20212223242526", + 112, + new String[] + { + "A29C1A8C42FBC562D7D1DBA7DC541FFE", + "0BDA66B049429061C013E4228C2F44C6" + } + ) + .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C") + .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C") + .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBC"), + new DRBGTestVector( + new AESFastEngine(), 128, + new Bit256EntropyProvider().get(256), + false, + "2021222324252627", + 128, + new String[] + { + "8CF59C8CF6888B96EB1C1E3E79D82387AF08A9E5FF75E23F1FBCD4559B6B997E", + "69CDEF912C692D61B1DA4C05146B52EB7B8849BD87937835328254EC25A9180E" + } + ), + new DRBGTestVector( + new AESFastEngine(), 128, + new Bit256EntropyProvider().get(256), + false, + "2021222324252627", + 128, + new String[] + { + "E8C74A4B7BFFB53BEB80E78CA86BB6DF70E2032AEB473E0DD54D2339CEFCE9D0", + "26B3F823B4DBAFC23B141375E10B3AEB7A0B5DEF1C7D760B6F827D01ECD17AC7" + } + ) + .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F") + .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"), + new DRBGTestVector( + new AESFastEngine(), 128, + new Bit256EntropyProvider().get(256), + false, + "2021222324252627", + 128, + new String[] + { + "18FDEFBDC43D7A36D5D6D862205765D1D701C9F237007030DF1B8E70EE4EEE29", + "9888F1D38BB1CCE31B363AA1BD9B39616876C30DEE1FF0B7BD8C4C441715C833" + } + ) + .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F"), + new DRBGTestVector( + new AESFastEngine(), 128, + new Bit256EntropyProvider().get(256), + true, + "2021222324252627", + 128, + new String[] + { + "BFF4B85D68C84529F24F69F9ACF1756E29BA648DDEB825C225FA32BA490EF4A9", + "9BD2635137A52AF7D0FCBEFEFB97EA93A0F4C438BD98956C0DACB04F15EE25B3" + } + ), + new DRBGTestVector( + new AESFastEngine(), 128, + new Bit256EntropyProvider().get(256), + true, + "2021222324252627", + 128, + new String[] + { + "4573AC8BBB33D7CC4DBEF3EEDF6EAE748B536C3A1082CEE4948CDB51C83A7F9C", + "99C628CDD87BD8C2F1FE443AA7F761DA16886436326323354DA6311FFF5BC678" + } + ) + .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F") + .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"), + new DRBGTestVector( + new AESFastEngine(), 128, + new Bit256EntropyProvider().get(256), + true, + "2021222324252627", + 128, + new String[] + { + "F324104E2FA14F79D8AA60DF06B93B3BC157324958F0A7EE1E193677A70E0250", + "78F4C840134F40DC001BFAD3A90B5EF4DEBDBFAC3CFDF0CD69A89DC4FD34713F" + } + ) + .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F"), + new DRBGTestVector( + new AESFastEngine(), 192, + new Bit320EntropyProvider().get(320), + false, + "202122232425262728292A2B", + 192, + new String[] + { + "E231244B3235B085C81604424357E85201E3828B5C45568679A5555F867AAC8C", + "DDD0F7BCCADADAA31A67652259CE569A271DD85CF66C3D6A7E9FAED61F38D219" + } + ) + .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6061626364656667"), + new DRBGTestVector( + new AESFastEngine(), 192, + new Bit320EntropyProvider().get(320), + true, + "202122232425262728292A2B", + 192, + new String[] + { + "F780D4A2C25CF8EE7407D948EC0B724A4235D8B20E65081392755CA7912AD7C0", + "BA14617F915BA964CB79276BDADC840C14B631BBD1A59097054FA6DFF863B238" + } + ) + .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6061626364656667"), + new DRBGTestVector( + new AESFastEngine(), 256, + new Bit384EntropyProvider().get(384), + false, + "202122232425262728292A2B2C2D2E2F", + 256, + new String[] + { + "47111E146562E9AA2FB2A1B095D37A8165AF8FC7CA611D632BE7D4C145C83900", + "98A28E3B1BA363C9DAF0F6887A1CF52B833D3354D77A7C10837DD63DD2E645F8" + } + ) + .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F") + .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F") + .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"), + new DRBGTestVector( + new AESFastEngine(), 256, + new Bit384EntropyProvider().get(384), + true, + "202122232425262728292A2B2C2D2E2F", + 256, + new String[] + { + "71BB3F9C9CEAF4E6C92A83EB4C7225010EE150AC75E23F5F77AD5073EF24D88A", + "386DEBBBF091BBF0502957B0329938FB836B82E594A2F5FDD5EB28D4E35528F4" + } + ) + .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F") + .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"), + new DRBGTestVector( + new AESFastEngine(), 256, + new Bit384EntropyProvider().get(384), + true, + "202122232425262728292A2B2C2D2E2F", + 256, + new String[] + { + "1A2E3FEE9056E98D375525FDC2B63B95B47CE51FCF594D804BD5A17F2E01139B", + "601F95384F0D85946301D1EACE8F645A825CE38F1E2565B0C0C439448E9CA8AC" + } + ) + .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F"), + new DRBGTestVector( + new AESFastEngine(), 256, + new Bit384EntropyProvider().get(384), + true, + "202122232425262728292A2B2C2D2E2F", + 256, + new String[] + { + "EAE6BCE781807E524D26605EA198077932D01EEB445B9AC6C5D99C101D29F46E", + "738E99C95AF59519AAD37FF3D5180986ADEBAB6E95836725097E50A8D1D0BD28" + } + ) + .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F") + .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F") + .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECF") + }; + } + + public void performTest() + throws Exception + { + DRBGTestVector[] tests = createTestVectorData(); + + for (int i = 0; i != tests.length; i++) + { + DRBGTestVector tv = tests[i]; + + byte[] nonce = tv.nonce(); + byte[] personalisationString = tv.personalizationString(); + + SP80090DRBG d = new CTRSP800DRBG(tv.getCipher(), tv.keySizeInBits(), tv.securityStrength(), tv.entropySource(), personalisationString, nonce); + + byte[] output = new byte[tv.expectedValue(0).length]; + + d.generate(output, tv.additionalInput(0), tv.predictionResistance()); + + byte[] expected = tv.expectedValue(0); + + if (!areEqual(expected, output)) + { + fail("Test #" + (i + 1) + ".1 failed, expected " + new String(Hex.encode(tv.expectedValue(0))) + " got " + new String(Hex.encode(output))); + } + + output = new byte[tv.expectedValue(0).length]; + + d.generate(output, tv.additionalInput(1), tv.predictionResistance()); + + expected = tv.expectedValue(1); + if (!areEqual(expected, output)) + { + fail("Test #" + (i + 1) + ".2 failed, expected " + new String(Hex.encode(tv.expectedValue(1))) + " got " + new String(Hex.encode(output))); + } + } + + // DESede/TDEA key parity test + DRBGTestVector tv = tests[0]; + + SP80090DRBG drbg = new CTRSP800DRBG(new KeyParityCipher(tv.getCipher()), tv.keySizeInBits(), tv.securityStrength(), tv.entropySource(), tv.personalizationString(), tv.nonce()); + + byte[] output = new byte[tv.expectedValue(0).length]; + + drbg.generate(output, tv.additionalInput(0), tv.predictionResistance()); + + // Exception tests + SP80090DRBG d; + try + { + d = new CTRSP800DRBG(new AESEngine(), 256, 256, new Bit232EntropyProvider().get(128), null, null); + fail("no exception thrown"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("Not enough entropy for security strength required")) + { + fail("Wrong exception", e); + } + } + + try + { + d = new CTRSP800DRBG(new DESedeEngine(), 256, 256, new Bit232EntropyProvider().get(232), null, null); + fail("no exception thrown"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("Requested security strength is not supported by block cipher and key size")) + { + fail("Wrong exception", e); + } + } + + try + { + d = new CTRSP800DRBG(new DESedeEngine(), 168, 256, new Bit232EntropyProvider().get(232), null, null); + fail("no exception thrown"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("Requested security strength is not supported by block cipher and key size")) + { + fail("Wrong exception", e); + } + } + + try + { + d = new CTRSP800DRBG(new AESEngine(), 192, 256, new Bit232EntropyProvider().get(232), null, null); + fail("no exception thrown"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("Requested security strength is not supported by block cipher and key size")) + { + fail("Wrong exception", e); + } + } + } + + private class Bit232EntropyProvider + extends TestEntropySourceProvider + { + Bit232EntropyProvider() + { + super(Hex.decode( + "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C" + + "808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C" + + "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDC"), true); + } + } + + private class Bit256EntropyProvider + extends TestEntropySourceProvider + { + Bit256EntropyProvider() + { + super(Hex.decode( + "0001020304050607"+ + "08090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"+ + "8081828384858687"+ + "88898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F"+ + "C0C1C2C3C4C5C6C7"+ + "C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF"), true); + } + } + + private class Bit320EntropyProvider + extends TestEntropySourceProvider + { + Bit320EntropyProvider() + { + super(Hex.decode( + "000102030405060708090A0B0C0D0E0F"+ + "101112131415161718191A1B1C1D1E1F2021222324252627"+ + "808182838485868788898A8B8C8D8E8F"+ + "909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7"+ + "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"+ + "D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7"), true); + } + } + + private class Bit384EntropyProvider + extends TestEntropySourceProvider + { + Bit384EntropyProvider() + { + super(Hex.decode( + "000102030405060708090A0B0C0D0E0F1011121314151617" + + "18191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F" + + "808182838485868788898A8B8C8D8E8F9091929394959697" + + "98999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAF" + + "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7" + + "D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"), true); + } + } + + private class KeyParityCipher + implements BlockCipher + { + private BlockCipher cipher; + + KeyParityCipher(BlockCipher cipher) + { + this.cipher = cipher; + } + + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException + { + byte[] k = Arrays.clone(((KeyParameter)params).getKey()); + + DESedeParameters.setOddParity(k); + + if (!Arrays.areEqual(((KeyParameter)params).getKey(), k)) + { + fail("key not odd parity"); + } + + cipher.init(forEncryption, params); + } + + public String getAlgorithmName() + { + return cipher.getAlgorithmName(); + } + + public int getBlockSize() + { + return cipher.getBlockSize(); + } + + public int processBlock(byte[] in, int inOff, byte[] out, int outOff) + throws DataLengthException, IllegalStateException + { + return cipher.processBlock(in, inOff, out, outOff); + } + + public void reset() + { + cipher.reset(); + } + } + +} diff --git a/src/test/java/org/bouncycastle/crypto/test/DRBGTestVector.java b/src/test/java/org/bouncycastle/crypto/prng/test/DRBGTestVector.java index 482db87b..dd6801c5 100644 --- a/src/test/java/org/bouncycastle/crypto/test/DRBGTestVector.java +++ b/src/test/java/org/bouncycastle/crypto/prng/test/DRBGTestVector.java @@ -1,4 +1,4 @@ -package org.bouncycastle.crypto.test; +package org.bouncycastle.crypto.prng.test; import java.util.ArrayList; import java.util.List; diff --git a/src/test/java/org/bouncycastle/crypto/test/DualECDRBGTest.java b/src/test/java/org/bouncycastle/crypto/prng/test/DualECDRBGTest.java index 9161e6e5..59ae3346 100644 --- a/src/test/java/org/bouncycastle/crypto/test/DualECDRBGTest.java +++ b/src/test/java/org/bouncycastle/crypto/prng/test/DualECDRBGTest.java @@ -1,15 +1,16 @@ -package org.bouncycastle.crypto.test; +package org.bouncycastle.crypto.prng.test; +import org.bouncycastle.crypto.digests.SHA1Digest; import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.digests.SHA384Digest; import org.bouncycastle.crypto.digests.SHA512Digest; -import org.bouncycastle.crypto.prng.DualECSP800DRBG; -import org.bouncycastle.crypto.prng.SP80090DRBG; +import org.bouncycastle.crypto.prng.drbg.DualECSP800DRBG; +import org.bouncycastle.crypto.prng.drbg.SP80090DRBG; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; /** - * Dual EC SP800-90 DRBG + * Dual EC SP800-90 DRBG test */ public class DualECDRBGTest extends SimpleTest @@ -272,7 +273,7 @@ public class DualECDRBGTest byte[] nonce = tv.nonce(); byte[] personalisationString = tv.personalizationString(); - SP80090DRBG d = new DualECSP800DRBG(tv.getDigest(), tv.entropySource(), nonce, personalisationString, tv.securityStrength()); + SP80090DRBG d = new DualECSP800DRBG(tv.getDigest(), tv.securityStrength(), tv.entropySource(), personalisationString, nonce); byte[] output = new byte[tv.expectedValue(0).length]; @@ -295,6 +296,48 @@ public class DualECDRBGTest fail("Test #" + (i + 1) + ".2 failed, expected " + new String(Hex.encode(tv.expectedValue(1))) + " got " + new String(Hex.encode(output))); } } + + // Exception tests + // + SP80090DRBG d; + try + { + d = new DualECSP800DRBG(new SHA256Digest(), 256, new SHA256EntropyProvider().get(128), null, null); + fail("no exception thrown"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("EntropySource must provide between 256 and 4096 bits")) + { + fail("Wrong exception", e); + } + } + + try + { + d = new DualECSP800DRBG(new SHA256Digest(), 256, new SHA256EntropyProvider().get(1 << (13 - 1) + 1), null, null); + fail("no exception thrown"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("EntropySource must provide between 256 and 4096 bits")) + { + fail("Wrong exception", e); + } + } + + try + { + d = new DualECSP800DRBG(new SHA1Digest(), 256, new SHA256EntropyProvider().get(256), null, null); + fail("no exception thrown"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("Requested security strength is not supported by digest")) + { + fail("Wrong exception", e); + } + } } private class SHA256EntropyProvider diff --git a/src/test/java/org/bouncycastle/crypto/test/HMacDRBGTest.java b/src/test/java/org/bouncycastle/crypto/prng/test/HMacDRBGTest.java index e98d770c..add77d5a 100644 --- a/src/test/java/org/bouncycastle/crypto/test/HMacDRBGTest.java +++ b/src/test/java/org/bouncycastle/crypto/prng/test/HMacDRBGTest.java @@ -1,12 +1,12 @@ -package org.bouncycastle.crypto.test; +package org.bouncycastle.crypto.prng.test; import org.bouncycastle.crypto.digests.SHA1Digest; import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.digests.SHA384Digest; import org.bouncycastle.crypto.digests.SHA512Digest; import org.bouncycastle.crypto.macs.HMac; -import org.bouncycastle.crypto.prng.HMacSP800DRBG; -import org.bouncycastle.crypto.prng.SP80090DRBG; +import org.bouncycastle.crypto.prng.drbg.HMacSP800DRBG; +import org.bouncycastle.crypto.prng.drbg.SP80090DRBG; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; @@ -388,7 +388,7 @@ public class HMacDRBGTest byte[] nonce = tv.nonce(); byte[] personalisationString = tv.personalizationString(); - SP80090DRBG d = new HMacSP800DRBG(new HMac(tv.getDigest()), tv.entropySource(), nonce, personalisationString, tv.securityStrength()); + SP80090DRBG d = new HMacSP800DRBG(new HMac(tv.getDigest()), tv.securityStrength(), tv.entropySource(), personalisationString, nonce); byte[] output = new byte[tv.expectedValue(0).length]; @@ -411,6 +411,22 @@ public class HMacDRBGTest fail("Test #" + (i + 1) + ".2 failed, expected " + new String(Hex.encode(tv.expectedValue(1))) + " got " + new String(Hex.encode(output))); } } + + // Exception tests + // + SP80090DRBG d; + try + { + d = new HMacSP800DRBG(new HMac(new SHA256Digest()), 256, new SHA256EntropyProvider().get(128), null, null); + fail("no exception thrown"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("Not enough entropy for security strength required")) + { + fail("Wrong exception", e); + } + } } private class SHA1EntropyProvider diff --git a/src/test/java/org/bouncycastle/crypto/test/HashDRBGTest.java b/src/test/java/org/bouncycastle/crypto/prng/test/HashDRBGTest.java index 06304d72..ee632033 100644 --- a/src/test/java/org/bouncycastle/crypto/test/HashDRBGTest.java +++ b/src/test/java/org/bouncycastle/crypto/prng/test/HashDRBGTest.java @@ -1,11 +1,11 @@ -package org.bouncycastle.crypto.test; +package org.bouncycastle.crypto.prng.test; import org.bouncycastle.crypto.digests.SHA1Digest; import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.digests.SHA384Digest; import org.bouncycastle.crypto.digests.SHA512Digest; -import org.bouncycastle.crypto.prng.HashSP800DRBG; -import org.bouncycastle.crypto.prng.SP80090DRBG; +import org.bouncycastle.crypto.prng.drbg.HashSP800DRBG; +import org.bouncycastle.crypto.prng.drbg.SP80090DRBG; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; @@ -348,7 +348,7 @@ public class HashDRBGTest byte[] nonce = tv.nonce(); byte[] personalisationString = tv.personalizationString(); - SP80090DRBG d = new HashSP800DRBG(tv.getDigest(), tv.entropySource(), nonce, personalisationString, tv.securityStrength()); + SP80090DRBG d = new HashSP800DRBG(tv.getDigest(), tv.securityStrength(), tv.entropySource(), personalisationString, nonce); byte[] output = new byte[tv.expectedValue(0).length]; @@ -371,6 +371,35 @@ public class HashDRBGTest fail("Test #" + (i + 1) + ".2 failed, expected " + new String(Hex.encode(tv.expectedValue(1))) + " got " + new String(Hex.encode(output))); } } + + // Exception tests + // + SP80090DRBG d; + try + { + d = new HashSP800DRBG(new SHA256Digest(), 256, new SHA256EntropyProvider().get(128), null, null); + fail("no exception thrown"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("Not enough entropy for security strength required")) + { + fail("Wrong exception", e); + } + } + + try + { + d = new HashSP800DRBG(new SHA1Digest(), 256, new SHA256EntropyProvider().get(256), null, null); + fail("no exception thrown"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("Requested security strength is not supported by the derivation function")) + { + fail("Wrong exception", e); + } + } } private class SHA1EntropyProvider diff --git a/src/test/java/org/bouncycastle/crypto/random/test/RegressionTest.java b/src/test/java/org/bouncycastle/crypto/prng/test/RegressionTest.java index e2f1e79c..d1f6f435 100644 --- a/src/test/java/org/bouncycastle/crypto/random/test/RegressionTest.java +++ b/src/test/java/org/bouncycastle/crypto/prng/test/RegressionTest.java @@ -1,4 +1,4 @@ -package org.bouncycastle.crypto.random.test; +package org.bouncycastle.crypto.prng.test; import org.bouncycastle.util.test.Test; import org.bouncycastle.util.test.TestResult; @@ -6,6 +6,10 @@ import org.bouncycastle.util.test.TestResult; public class RegressionTest { public static Test[] tests = { + new CTRDRBGTest(), + new DualECDRBGTest(), + new HashDRBGTest(), + new HMacDRBGTest(), new SP800RandomTest() }; diff --git a/src/test/java/org/bouncycastle/crypto/random/test/SP800RandomTest.java b/src/test/java/org/bouncycastle/crypto/prng/test/SP800RandomTest.java index b18f30b0..a164f8fc 100644 --- a/src/test/java/org/bouncycastle/crypto/random/test/SP800RandomTest.java +++ b/src/test/java/org/bouncycastle/crypto/prng/test/SP800RandomTest.java @@ -1,4 +1,4 @@ -package org.bouncycastle.crypto.random.test; +package org.bouncycastle.crypto.prng.test; import java.security.SecureRandom; @@ -6,9 +6,7 @@ import org.bouncycastle.crypto.digests.SHA1Digest; import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.engines.DESedeEngine; import org.bouncycastle.crypto.macs.HMac; -import org.bouncycastle.crypto.random.SP800SecureRandomBuilder; -import org.bouncycastle.crypto.test.DRBGTestVector; -import org.bouncycastle.crypto.test.TestEntropySourceProvider; +import org.bouncycastle.crypto.prng.SP800SecureRandomBuilder; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; @@ -220,6 +218,22 @@ public class SP800RandomTest ); doCTRTest(tv); + + tv = new DRBGTestVector( + new DESedeEngine(), 168, + new Bit232EntropyProvider().get(232), + true, + "20212223242526", + 112, + new String[] + { + "64983055D014550B39DE699E43130B64", + "035FDDA8582A2214EC722C410A8D95D3" + } + ) + .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C"); + + doCTRTest(tv); } private void doCTRTest(DRBGTestVector tv) diff --git a/src/test/java/org/bouncycastle/crypto/test/TestEntropySourceProvider.java b/src/test/java/org/bouncycastle/crypto/prng/test/TestEntropySourceProvider.java index 08576977..64e75958 100644 --- a/src/test/java/org/bouncycastle/crypto/test/TestEntropySourceProvider.java +++ b/src/test/java/org/bouncycastle/crypto/prng/test/TestEntropySourceProvider.java @@ -1,4 +1,4 @@ -package org.bouncycastle.crypto.test; +package org.bouncycastle.crypto.prng.test; import org.bouncycastle.crypto.prng.EntropySource; import org.bouncycastle.crypto.prng.EntropySourceProvider; @@ -36,6 +36,11 @@ public class TestEntropySourceProvider return rv; } + + public int entropySize() + { + return bitsRequired; + } }; } } diff --git a/src/test/java/org/bouncycastle/crypto/test/CTRDRBGTest.java b/src/test/java/org/bouncycastle/crypto/test/CTRDRBGTest.java deleted file mode 100644 index 31a98e08..00000000 --- a/src/test/java/org/bouncycastle/crypto/test/CTRDRBGTest.java +++ /dev/null @@ -1,190 +0,0 @@ -package org.bouncycastle.crypto.test; - -import org.bouncycastle.crypto.engines.AESFastEngine; -import org.bouncycastle.crypto.engines.DESedeEngine; -import org.bouncycastle.crypto.prng.CTRSP800DRBG; -import org.bouncycastle.crypto.prng.SP80090DRBG; -import org.bouncycastle.util.encoders.Hex; -import org.bouncycastle.util.test.SimpleTest; - -/** - * CTR DRBG Test - */ -public class CTRDRBGTest - extends SimpleTest -{ - public String getName() - { - return "CTRDRBGTest"; - } - - public static void main(String[] args) - { - runTest(new CTRDRBGTest()); - } - - private DRBGTestVector[] createTestVectorData() - { - return new DRBGTestVector[] - { - new DRBGTestVector( - new DESedeEngine(), 168, - new Bit232EntropyProvider().get(232), - false, - "20212223242526", - 112, - new String[] - { - "ABC88224514D0316EA3D48AEE3C9A2B4", - "D3D3F372E43E7ABDC4FA293743EED076" - } - ), - new DRBGTestVector( - new DESedeEngine(), 168, - new Bit232EntropyProvider().get(232), - false, - "20212223242526", - 112, - new String[] - { - "D4564EE072ACA5BD279536E14F94CB12", - "1CCD9AFEF15A9679BA75E35225585DEA" - } - ) - .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C") - .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBC"), - new DRBGTestVector( - new DESedeEngine(), 168, - new Bit232EntropyProvider().get(232), - false, - "20212223242526", - 112, - new String[] - { - "ABC88224514D0316EA3D48AEE3C9A2B4", - "D3D3F372E43E7ABDC4FA293743EED076" - } - ), - new DRBGTestVector( - new DESedeEngine(), 168, - new Bit232EntropyProvider().get(232), - false, - "20212223242526", - 112, - new String[] - { - "760BED7D92B083B10AF31CF0656081EB", - "FD1AC41482384D823CF3FD6F0E6C88B3" - } - ) - .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C"), - new DRBGTestVector( - new AESFastEngine(), 192, - new Bit320EntropyProvider().get(320), - false, - "202122232425262728292A2B", - 192, - new String[] - { - "E231244B3235B085C81604424357E85201E3828B5C45568679A5555F867AAC8C", - "DDD0F7BCCADADAA31A67652259CE569A271DD85CF66C3D6A7E9FAED61F38D219" - } - ) - .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6061626364656667"), - new DRBGTestVector( - new AESFastEngine(), 256, - new Bit384EntropyProvider().get(384), - false, - "202122232425262728292A2B2C2D2E2F", - 256, - new String[] - { - "47111E146562E9AA2FB2A1B095D37A8165AF8FC7CA611D632BE7D4C145C83900", - "98A28E3B1BA363C9DAF0F6887A1CF52B833D3354D77A7C10837DD63DD2E645F8" - } - ) - .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F") - .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F") - .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECF") - }; - } - - public void performTest() - throws Exception - { - DRBGTestVector[] tests = createTestVectorData(); - - for (int i = 0; i != tests.length; i++) - { - DRBGTestVector tv = tests[i]; - - byte[] nonce = tv.nonce(); - byte[] personalisationString = tv.personalizationString(); - - SP80090DRBG d = new CTRSP800DRBG(tv.getCipher(), tv.keySizeInBits(), tv.entropySource(), nonce, personalisationString, tv.securityStrength()); - - byte[] output = new byte[tv.expectedValue(0).length]; - - d.generate(output, tv.additionalInput(0), tv.predictionResistance()); - - byte[] expected = tv.expectedValue(0); - - if (!areEqual(expected, output)) - { - fail("Test #" + (i + 1) + ".1 failed, expected " + new String(Hex.encode(tv.expectedValue(0))) + " got " + new String(Hex.encode(output))); - } - - output = new byte[tv.expectedValue(0).length]; - - d.generate(output, tv.additionalInput(1), tv.predictionResistance()); - - expected = tv.expectedValue(1); - if (!areEqual(expected, output)) - { - fail("Test #" + (i + 1) + ".2 failed, expected " + new String(Hex.encode(tv.expectedValue(1))) + " got " + new String(Hex.encode(output))); - } - } - } - - private class Bit232EntropyProvider - extends TestEntropySourceProvider - { - Bit232EntropyProvider() - { - super(Hex.decode( - "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C" + - "808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C" + - "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDC"), true); - } - } - - private class Bit320EntropyProvider - extends TestEntropySourceProvider - { - Bit320EntropyProvider() - { - super(Hex.decode( - "000102030405060708090A0B0C0D0E0F"+ - "101112131415161718191A1B1C1D1E1F2021222324252627"+ - "808182838485868788898A8B8C8D8E8F"+ - "909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7"+ - "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"+ - "D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7"), true); - } - } - - private class Bit384EntropyProvider - extends TestEntropySourceProvider - { - Bit384EntropyProvider() - { - super(Hex.decode( - "000102030405060708090A0B0C0D0E0F1011121314151617" + - "18191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F" + - "808182838485868788898A8B8C8D8E8F9091929394959697" + - "98999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAF" + - "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7" + - "D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"), true); - } - } -} diff --git a/src/test/java/org/bouncycastle/crypto/test/DSATest.java b/src/test/java/org/bouncycastle/crypto/test/DSATest.java index 005ac521..02c748fa 100644 --- a/src/test/java/org/bouncycastle/crypto/test/DSATest.java +++ b/src/test/java/org/bouncycastle/crypto/test/DSATest.java @@ -1,23 +1,29 @@ package org.bouncycastle.crypto.test; +import java.math.BigInteger; +import java.security.SecureRandom; + import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.digests.SHA224Digest; +import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.generators.DSAKeyPairGenerator; import org.bouncycastle.crypto.generators.DSAParametersGenerator; import org.bouncycastle.crypto.params.DSAKeyGenerationParameters; +import org.bouncycastle.crypto.params.DSAParameterGenerationParameters; import org.bouncycastle.crypto.params.DSAParameters; +import org.bouncycastle.crypto.params.DSAPrivateKeyParameters; +import org.bouncycastle.crypto.params.DSAPublicKeyParameters; import org.bouncycastle.crypto.params.DSAValidationParameters; import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.crypto.signers.DSASigner; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.FixedSecureRandom; import org.bouncycastle.util.test.SimpleTest; -import java.math.BigInteger; -import java.security.SecureRandom; - /** - * Test based on FIPS 186-2, Appendix 5, an example of DSA. + * Test based on FIPS 186-2, Appendix 5, an example of DSA, and FIPS 168-3 test vectors. */ public class DSATest extends SimpleTest @@ -92,6 +98,476 @@ public class DSATest { fail("verification fails"); } + + dsa2Test1(); + dsa2Test2(); + dsa2Test3(); + dsa2Test4(); + } + + private void dsa2Test1() + { + byte[] seed = Hex.decode("ED8BEE8D1CB89229D2903CBF0E51EE7377F48698"); + + DSAParametersGenerator pGen = new DSAParametersGenerator(); + + pGen.init(new DSAParameterGenerationParameters(1024, 160, 80, new DSATestSecureRandom(seed))); + + DSAParameters params = pGen.generateParameters(); + + DSAValidationParameters pv = params.getValidationParameters(); + + if (pv.getCounter() != 5) + { + fail("counter incorrect"); + } + + if (!Arrays.areEqual(seed, pv.getSeed())) + { + fail("seed incorrect"); + } + + if (!params.getQ().equals(new BigInteger("E950511EAB424B9A19A2AEB4E159B7844C589C4F", 16))) + { + fail("Q incorrect"); + } + + if (!params.getP().equals(new BigInteger( + "E0A67598CD1B763B" + + "C98C8ABB333E5DDA0CD3AA0E5E1FB5BA8A7B4EABC10BA338" + + "FAE06DD4B90FDA70D7CF0CB0C638BE3341BEC0AF8A7330A3" + + "307DED2299A0EE606DF035177A239C34A912C202AA5F83B9" + + "C4A7CF0235B5316BFC6EFB9A248411258B30B839AF172440" + + "F32563056CB67A861158DDD90E6A894C72A5BBEF9E286C6B", 16))) + { + fail("P incorrect"); + } + + if (!params.getG().equals(new BigInteger( + "D29D5121B0423C27" + + "69AB21843E5A3240FF19CACC792264E3BB6BE4F78EDD1B15" + + "C4DFF7F1D905431F0AB16790E1F773B5CE01C804E509066A" + + "9919F5195F4ABC58189FD9FF987389CB5BEDF21B4DAB4F8B" + + "76A055FFE2770988FE2EC2DE11AD92219F0B351869AC24DA" + + "3D7BA87011A701CE8EE7BFE49486ED4527B7186CA4610A75", 16))) + { + fail("G incorrect"); + } + + DSAKeyPairGenerator kpGen = new DSAKeyPairGenerator(); + + kpGen.init(new DSAKeyGenerationParameters(new FixedSecureRandom(Hex.decode("D0EC4E50BB290A42E9E355C73D8809345DE2E139")), params)); + + AsymmetricCipherKeyPair kp = kpGen.generateKeyPair(); + + DSAPublicKeyParameters pub = (DSAPublicKeyParameters)kp.getPublic(); + DSAPrivateKeyParameters priv = (DSAPrivateKeyParameters)kp.getPrivate(); + + if (!pub.getY().equals(new BigInteger( + "25282217F5730501" + + "DD8DBA3EDFCF349AAFFEC20921128D70FAC44110332201BB" + + "A3F10986140CBB97C726938060473C8EC97B4731DB004293" + + "B5E730363609DF9780F8D883D8C4D41DED6A2F1E1BBBDC97" + + "9E1B9D6D3C940301F4E978D65B19041FCF1E8B518F5C0576" + + "C770FE5A7A485D8329EE2914A2DE1B5DA4A6128CEAB70F79", 16))) + { + fail("Y value incorrect"); + } + + if (!priv.getX().equals( + new BigInteger("D0EC4E50BB290A42E9E355C73D8809345DE2E139", 16))) + { + fail("X value incorrect"); + } + + DSASigner signer = new DSASigner(); + + signer.init(true, new ParametersWithRandom(kp.getPrivate(), new FixedSecureRandom(Hex.decode("349C55648DCF992F3F33E8026CFAC87C1D2BA075")))); + + byte[] msg = Hex.decode("A9993E364706816ABA3E25717850C26C9CD0D89D"); + + BigInteger[] sig = signer.generateSignature(msg); + + if (!sig[0].equals(new BigInteger("636155AC9A4633B4665D179F9E4117DF68601F34", 16))) + { + fail("R value incorrect"); + } + + if (!sig[1].equals(new BigInteger("6C540B02D9D4852F89DF8CFC99963204F4347704", 16))) + { + fail("S value incorrect"); + } + + signer.init(false, kp.getPublic()); + + if (!signer.verifySignature(msg, sig[0], sig[1])) + { + fail("signature not verified"); + } + + } + + private void dsa2Test2() + { + byte[] seed = Hex.decode("5AFCC1EFFC079A9CCA6ECA86D6E3CC3B18642D9BE1CC6207C84002A9"); + + DSAParametersGenerator pGen = new DSAParametersGenerator(new SHA224Digest()); + + pGen.init(new DSAParameterGenerationParameters(2048, 224, 80, new DSATestSecureRandom(seed))); + + DSAParameters params = pGen.generateParameters(); + + DSAValidationParameters pv = params.getValidationParameters(); + + if (pv.getCounter() != 21) + { + fail("counter incorrect"); + } + + if (!Arrays.areEqual(seed, pv.getSeed())) + { + fail("seed incorrect"); + } + + if (!params.getQ().equals(new BigInteger("90EAF4D1AF0708B1B612FF35E0A2997EB9E9D263C9CE659528945C0D", 16))) + { + fail("Q incorrect"); + } + + if (!params.getP().equals(new BigInteger( + "C196BA05AC29E1F9C3C72D56DFFC6154" + + "A033F1477AC88EC37F09BE6C5BB95F51C296DD20D1A28A06" + + "7CCC4D4316A4BD1DCA55ED1066D438C35AEBAABF57E7DAE4" + + "28782A95ECA1C143DB701FD48533A3C18F0FE23557EA7AE6" + + "19ECACC7E0B51652A8776D02A425567DED36EABD90CA33A1" + + "E8D988F0BBB92D02D1D20290113BB562CE1FC856EEB7CDD9" + + "2D33EEA6F410859B179E7E789A8F75F645FAE2E136D252BF" + + "FAFF89528945C1ABE705A38DBC2D364AADE99BE0D0AAD82E" + + "5320121496DC65B3930E38047294FF877831A16D5228418D" + + "E8AB275D7D75651CEFED65F78AFC3EA7FE4D79B35F62A040" + + "2A1117599ADAC7B269A59F353CF450E6982D3B1702D9CA83", 16))) + { + fail("P incorrect"); + } + + if (!params.getG().equals(new BigInteger( + "A59A749A11242C58C894E9E5A91804E8"+ + "FA0AC64B56288F8D47D51B1EDC4D65444FECA0111D78F35F"+ + "C9FDD4CB1F1B79A3BA9CBEE83A3F811012503C8117F98E50"+ + "48B089E387AF6949BF8784EBD9EF45876F2E6A5A495BE64B"+ + "6E770409494B7FEE1DBB1E4B2BC2A53D4F893D418B715959"+ + "2E4FFFDF6969E91D770DAEBD0B5CB14C00AD68EC7DC1E574"+ + "5EA55C706C4A1C5C88964E34D09DEB753AD418C1AD0F4FDF"+ + "D049A955E5D78491C0B7A2F1575A008CCD727AB376DB6E69"+ + "5515B05BD412F5B8C2F4C77EE10DA48ABD53F5DD498927EE"+ + "7B692BBBCDA2FB23A516C5B4533D73980B2A3B60E384ED20"+ + "0AE21B40D273651AD6060C13D97FD69AA13C5611A51B9085", 16))) + { + fail("G incorrect"); + } + + DSAKeyPairGenerator kpGen = new DSAKeyPairGenerator(); + + kpGen.init(new DSAKeyGenerationParameters(new FixedSecureRandom(Hex.decode("00D0F09ED3E2568F6CADF9224117DA2AEC5A4300E009DE1366023E17")), params)); + + AsymmetricCipherKeyPair kp = kpGen.generateKeyPair(); + + DSAPublicKeyParameters pub = (DSAPublicKeyParameters)kp.getPublic(); + DSAPrivateKeyParameters priv = (DSAPrivateKeyParameters)kp.getPrivate(); + + if (!pub.getY().equals(new BigInteger( + "70035C9A3B225B258F16741F3941FBF0" + + "6F3D056CD7BD864604CBB5EE9DD85304EE8E8E4ABD5E9032" + + "11DDF25CE149075510ACE166970AFDC7DF552B7244F342FA" + + "02F7A621405B754909D757F97290E1FE5036E904CF593446" + + "0C046D95659821E1597ED9F2B1F0E20863A6BBD0CE74DACB" + + "A5D8C68A90B29C2157CDEDB82EC12B81EE3068F9BF5F7F34" + + "6ECA41ED174CCCD7D154FA4F42F80FFE1BF46AE9D8125DEB" + + "5B4BA08A72BDD86596DBEDDC9550FDD650C58F5AE5133509" + + "A702F79A31ECB490F7A3C5581631F7C5BE4FF7F9E9F27FA3" + + "90E47347AD1183509FED6FCF198BA9A71AB3335B4F38BE8D" + + "15496A00B6DC2263E20A5F6B662320A3A1EC033AA61E3B68", 16))) + { + fail("Y value incorrect"); + } + + if (!priv.getX().equals( + new BigInteger("00D0F09ED3E2568F6CADF9224117DA2AEC5A4300E009DE1366023E17", 16))) + { + fail("X value incorrect"); + } + + DSASigner signer = new DSASigner(); + + signer.init(true, new ParametersWithRandom(kp.getPrivate(), new FixedSecureRandom(Hex.decode("735959CC4463B8B440E407EECA8A473BF6A6D1FE657546F67D401F05")))); + + byte[] msg = Hex.decode("23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7"); + + BigInteger[] sig = signer.generateSignature(msg); + + if (!sig[0].equals(new BigInteger("4400138D05F9639CAF54A583CAAF25D2B76D0C3EAD752CE17DBC85FE", 16))) + { + fail("R value incorrect"); + } + + if (!sig[1].equals(new BigInteger("874D4F12CB13B61732D398445698CFA9D92381D938AA57EE2C9327B3", 16))) + { + fail("S value incorrect"); + } + + signer.init(false, kp.getPublic()); + + if (!signer.verifySignature(msg, sig[0], sig[1])) + { + fail("signature not verified"); + } + } + + private void dsa2Test3() + { + byte[] seed = Hex.decode("4783081972865EA95D43318AB2EAF9C61A2FC7BBF1B772A09017BDF5A58F4FF0"); + + DSAParametersGenerator pGen = new DSAParametersGenerator(new SHA256Digest()); + + pGen.init(new DSAParameterGenerationParameters(2048, 256, 80, new DSATestSecureRandom(seed))); + + DSAParameters params = pGen.generateParameters(); + + DSAValidationParameters pv = params.getValidationParameters(); + + if (pv.getCounter() != 12) + { + fail("counter incorrect"); + } + + if (!Arrays.areEqual(seed, pv.getSeed())) + { + fail("seed incorrect"); + } + + if (!params.getQ().equals(new BigInteger("C24ED361870B61E0D367F008F99F8A1F75525889C89DB1B673C45AF5867CB467", 16))) + { + fail("Q incorrect"); + } + + if (!params.getP().equals(new BigInteger( + "F56C2A7D366E3EBDEAA1891FD2A0D099" + + "436438A673FED4D75F594959CFFEBCA7BE0FC72E4FE67D91" + + "D801CBA0693AC4ED9E411B41D19E2FD1699C4390AD27D94C" + + "69C0B143F1DC88932CFE2310C886412047BD9B1C7A67F8A2" + + "5909132627F51A0C866877E672E555342BDF9355347DBD43" + + "B47156B2C20BAD9D2B071BC2FDCF9757F75C168C5D9FC431" + + "31BE162A0756D1BDEC2CA0EB0E3B018A8B38D3EF2487782A" + + "EB9FBF99D8B30499C55E4F61E5C7DCEE2A2BB55BD7F75FCD" + + "F00E48F2E8356BDB59D86114028F67B8E07B127744778AFF" + + "1CF1399A4D679D92FDE7D941C5C85C5D7BFF91BA69F9489D" + + "531D1EBFA727CFDA651390F8021719FA9F7216CEB177BD75", 16))) + { + fail("P incorrect"); + } + + if (!params.getG().equals(new BigInteger( + "8DC6CC814CAE4A1C05A3E186A6FE27EA" + + "BA8CDB133FDCE14A963A92E809790CBA096EAA26140550C1" + + "29FA2B98C16E84236AA33BF919CD6F587E048C52666576DB" + + "6E925C6CBE9B9EC5C16020F9A44C9F1C8F7A8E611C1F6EC2" + + "513EA6AA0B8D0F72FED73CA37DF240DB57BBB27431D61869" + + "7B9E771B0B301D5DF05955425061A30DC6D33BB6D2A32BD0" + + "A75A0A71D2184F506372ABF84A56AEEEA8EB693BF29A6403" + + "45FA1298A16E85421B2208D00068A5A42915F82CF0B858C8" + + "FA39D43D704B6927E0B2F916304E86FB6A1B487F07D8139E" + + "428BB096C6D67A76EC0B8D4EF274B8A2CF556D279AD267CC" + + "EF5AF477AFED029F485B5597739F5D0240F67C2D948A6279", 16))) + { + fail("G incorrect"); + } + + DSAKeyPairGenerator kpGen = new DSAKeyPairGenerator(); + + kpGen.init(new DSAKeyGenerationParameters(new FixedSecureRandom(Hex.decode("0CAF2EF547EC49C4F3A6FE6DF4223A174D01F2C115D49A6F73437C29A2A8458C")), params)); + + AsymmetricCipherKeyPair kp = kpGen.generateKeyPair(); + + DSAPublicKeyParameters pub = (DSAPublicKeyParameters)kp.getPublic(); + DSAPrivateKeyParameters priv = (DSAPrivateKeyParameters)kp.getPrivate(); + + if (!pub.getY().equals(new BigInteger( + "2828003D7C747199143C370FDD07A286" + + "1524514ACC57F63F80C38C2087C6B795B62DE1C224BF8D1D" + + "1424E60CE3F5AE3F76C754A2464AF292286D873A7A30B7EA" + + "CBBC75AAFDE7191D9157598CDB0B60E0C5AA3F6EBE425500" + + "C611957DBF5ED35490714A42811FDCDEB19AF2AB30BEADFF" + + "2907931CEE7F3B55532CFFAEB371F84F01347630EB227A41" + + "9B1F3F558BC8A509D64A765D8987D493B007C4412C297CAF" + + "41566E26FAEE475137EC781A0DC088A26C8804A98C23140E" + + "7C936281864B99571EE95C416AA38CEEBB41FDBFF1EB1D1D" + + "C97B63CE1355257627C8B0FD840DDB20ED35BE92F08C49AE" + + "A5613957D7E5C7A6D5A5834B4CB069E0831753ECF65BA02B", 16))) + { + fail("Y value incorrect"); + } + + if (!priv.getX().equals( + new BigInteger("0CAF2EF547EC49C4F3A6FE6DF4223A174D01F2C115D49A6F73437C29A2A8458C", 16))) + { + fail("X value incorrect"); + } + + DSASigner signer = new DSASigner(); + + signer.init(true, new ParametersWithRandom(kp.getPrivate(), new FixedSecureRandom(Hex.decode("0CAF2EF547EC49C4F3A6FE6DF4223A174D01F2C115D49A6F73437C29A2A8458C")))); + + byte[] msg = Hex.decode("BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD"); + + BigInteger[] sig = signer.generateSignature(msg); + + if (!sig[0].equals(new BigInteger("315C875DCD4850E948B8AC42824E9483A32D5BA5ABE0681B9B9448D444F2BE3C", 16))) + { + fail("R value incorrect"); + } + + if (!sig[1].equals(new BigInteger("89718D12E54A8D9ED066E4A55F7ED5A2229CD23B9A3CEE78F83ED6AA61F6BCB9", 16))) + { + fail("S value incorrect"); + } + + signer.init(false, kp.getPublic()); + + if (!signer.verifySignature(msg, sig[0], sig[1])) + { + fail("signature not verified"); + } + } + + private void dsa2Test4() + { + byte[] seed = Hex.decode("193AFCA7C1E77B3C1ECC618C81322E47B8B8B997C9C83515C59CC446C2D9BD47"); + + DSAParametersGenerator pGen = new DSAParametersGenerator(new SHA256Digest()); + + pGen.init(new DSAParameterGenerationParameters(3072, 256, 80, new DSATestSecureRandom(seed))); + + DSAParameters params = pGen.generateParameters(); + + DSAValidationParameters pv = params.getValidationParameters(); + + if (pv.getCounter() != 20) + { + fail("counter incorrect"); + } + + if (!Arrays.areEqual(seed, pv.getSeed())) + { + fail("seed incorrect"); + } + + if (!params.getQ().equals(new BigInteger("CFA0478A54717B08CE64805B76E5B14249A77A4838469DF7F7DC987EFCCFB11D", 16))) + { + fail("Q incorrect"); + } + + if (!params.getP().equals(new BigInteger( + "90066455B5CFC38F9CAA4A48B4281F292C260FEEF01FD610" + + "37E56258A7795A1C7AD46076982CE6BB956936C6AB4DCFE0" + + "5E6784586940CA544B9B2140E1EB523F009D20A7E7880E4E" + + "5BFA690F1B9004A27811CD9904AF70420EEFD6EA11EF7DA1" + + "29F58835FF56B89FAA637BC9AC2EFAAB903402229F491D8D" + + "3485261CD068699B6BA58A1DDBBEF6DB51E8FE34E8A78E54" + + "2D7BA351C21EA8D8F1D29F5D5D15939487E27F4416B0CA63" + + "2C59EFD1B1EB66511A5A0FBF615B766C5862D0BD8A3FE7A0" + + "E0DA0FB2FE1FCB19E8F9996A8EA0FCCDE538175238FC8B0E" + + "E6F29AF7F642773EBE8CD5402415A01451A840476B2FCEB0" + + "E388D30D4B376C37FE401C2A2C2F941DAD179C540C1C8CE0" + + "30D460C4D983BE9AB0B20F69144C1AE13F9383EA1C08504F" + + "B0BF321503EFE43488310DD8DC77EC5B8349B8BFE97C2C56" + + "0EA878DE87C11E3D597F1FEA742D73EEC7F37BE43949EF1A" + + "0D15C3F3E3FC0A8335617055AC91328EC22B50FC15B941D3" + + "D1624CD88BC25F3E941FDDC6200689581BFEC416B4B2CB73", 16))) + { + fail("P incorrect"); + } + + if (!params.getG().equals(new BigInteger( + "5E5CBA992E0A680D885EB903AEA78E4A45A469103D448EDE" + + "3B7ACCC54D521E37F84A4BDD5B06B0970CC2D2BBB715F7B8" + + "2846F9A0C393914C792E6A923E2117AB805276A975AADB52" + + "61D91673EA9AAFFEECBFA6183DFCB5D3B7332AA19275AFA1" + + "F8EC0B60FB6F66CC23AE4870791D5982AAD1AA9485FD8F4A" + + "60126FEB2CF05DB8A7F0F09B3397F3937F2E90B9E5B9C9B6" + + "EFEF642BC48351C46FB171B9BFA9EF17A961CE96C7E7A7CC" + + "3D3D03DFAD1078BA21DA425198F07D2481622BCE45969D9C" + + "4D6063D72AB7A0F08B2F49A7CC6AF335E08C4720E31476B6" + + "7299E231F8BD90B39AC3AE3BE0C6B6CACEF8289A2E2873D5" + + "8E51E029CAFBD55E6841489AB66B5B4B9BA6E2F784660896" + + "AFF387D92844CCB8B69475496DE19DA2E58259B090489AC8" + + "E62363CDF82CFD8EF2A427ABCD65750B506F56DDE3B98856" + + "7A88126B914D7828E2B63A6D7ED0747EC59E0E0A23CE7D8A" + + "74C1D2C2A7AFB6A29799620F00E11C33787F7DED3B30E1A2" + + "2D09F1FBDA1ABBBFBF25CAE05A13F812E34563F99410E73B", 16))) + { + fail("G incorrect"); + } + + DSAKeyPairGenerator kpGen = new DSAKeyPairGenerator(); + + kpGen.init(new DSAKeyGenerationParameters(new FixedSecureRandom(Hex.decode("3ABC1587297CE7B9EA1AD6651CF2BC4D7F92ED25CABC8553F567D1B40EBB8764")), params)); + + AsymmetricCipherKeyPair kp = kpGen.generateKeyPair(); + + DSAPublicKeyParameters pub = (DSAPublicKeyParameters)kp.getPublic(); + DSAPrivateKeyParameters priv = (DSAPrivateKeyParameters)kp.getPrivate(); + + if (!pub.getY().equals(new BigInteger( + "8B891C8692D3DE875879390F2698B26FBECCA6B075535DCE" + + "6B0C862577F9FA0DEF6074E7A7624121224A595896ABD4CD" + + "A56B2CEFB942E025D2A4282FFAA98A48CDB47E1A6FCB5CFB" + + "393EF35AF9DF913102BB303C2B5C36C3F8FC04ED7B8B69FE" + + "FE0CF3E1FC05CFA713B3435B2656E913BA8874AEA9F93600" + + "6AEB448BCD005D18EC3562A33D04CF25C8D3D69844343442" + + "FA3DB7DE618C5E2DA064573E61E6D5581BFB694A23AC87FD" + + "5B52D62E954E1376DB8DDB524FFC0D469DF978792EE44173" + + "8E5DB05A7DC43E94C11A2E7A4FBE383071FA36D2A7EC8A93" + + "88FE1C4F79888A99D3B6105697C2556B79BB4D7E781CEBB3" + + "D4866AD825A5E830846072289FDBC941FA679CA82F5F78B7" + + "461B2404DB883D215F4E0676CF5493950AC5591697BFEA8D" + + "1EE6EC016B89BA51CAFB5F9C84C989FA117375E94578F28B" + + "E0B34CE0545DA46266FD77F62D8F2CEE92AB77012AFEBC11" + + "008985A821CD2D978C7E6FE7499D1AAF8DE632C21BB48CA5" + + "CBF9F31098FD3FD3854C49A65D9201744AACE540354974F9", 16))) + { + fail("Y value incorrect"); + } + + if (!priv.getX().equals( + new BigInteger("3ABC1587297CE7B9EA1AD6651CF2BC4D7F92ED25CABC8553F567D1B40EBB8764", 16))) + { + fail("X value incorrect"); + } + + DSASigner signer = new DSASigner(); + + signer.init(true, new ParametersWithRandom(kp.getPrivate(), new FixedSecureRandom(Hex.decode("A6902C1E6E3943C5628061588A8B007BCCEA91DBF12915483F04B24AB0678BEE")))); + + byte[] msg = Hex.decode("BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD"); + + BigInteger[] sig = signer.generateSignature(msg); + + if (!sig[0].equals(new BigInteger("5F184E645A38BE8FB4A6871B6503A9D12924C7ABE04B71410066C2ECA6E3BE3E", 16))) + { + fail("R value incorrect"); + } + + if (!sig[1].equals(new BigInteger("91EB0C7BA3D4B9B60B825C3D9F2CADA8A2C9D7723267B033CBCDCF8803DB9C18", 16))) + { + fail("S value incorrect"); + } + + signer.init(false, kp.getPublic()); + + if (!signer.verifySignature(msg, sig[0], sig[1])) + { + fail("signature not verified"); + } } public static void main( @@ -99,4 +575,28 @@ public class DSATest { runTest(new DSATest()); } + + private class DSATestSecureRandom + extends FixedSecureRandom + { + private boolean first = true; + + public DSATestSecureRandom(byte[] value) + { + super(value); + } + + public void nextBytes(byte[] bytes) + { + if (first) + { + super.nextBytes(bytes); + first = false; + } + else + { + bytes[bytes.length - 1] = 2; + } + } + } } diff --git a/src/test/java/org/bouncycastle/crypto/test/DigestTest.java b/src/test/java/org/bouncycastle/crypto/test/DigestTest.java index 92cdc63f..f66f2a52 100644 --- a/src/test/java/org/bouncycastle/crypto/test/DigestTest.java +++ b/src/test/java/org/bouncycastle/crypto/test/DigestTest.java @@ -1,6 +1,7 @@ package org.bouncycastle.crypto.test; import org.bouncycastle.crypto.Digest; +import org.bouncycastle.util.Memoable; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; @@ -65,6 +66,45 @@ public abstract class DigestTest { fail("failing second clone vector test", results[results.length - 1], new String(Hex.encode(resBuf))); } + + // + // memo test + // + Memoable m = (Memoable)digest; + + digest.update(lastV, 0, lastV.length/2); + + // copy the Digest + Memoable copy1 = m.copy(); + Memoable copy2 = copy1.copy(); + + digest.update(lastV, lastV.length/2, lastV.length - lastV.length/2); + digest.doFinal(resBuf, 0); + + if (!areEqual(lastDigest, resBuf)) + { + fail("failing memo vector test", results[results.length - 1], new String(Hex.encode(resBuf))); + } + + m.reset(copy1); + + digest.update(lastV, lastV.length/2, lastV.length - lastV.length/2); + digest.doFinal(resBuf, 0); + + if (!areEqual(lastDigest, resBuf)) + { + fail("failing memo reset vector test", results[results.length - 1], new String(Hex.encode(resBuf))); + } + + Digest md = (Digest)copy2; + + md.update(lastV, lastV.length/2, lastV.length - lastV.length/2); + md.doFinal(resBuf, 0); + + if (!areEqual(lastDigest, resBuf)) + { + fail("failing memo copy vector test", results[results.length - 1], new String(Hex.encode(resBuf))); + } } private byte[] toByteArray(String input) diff --git a/src/test/java/org/bouncycastle/crypto/test/ECIESKeyEncapsulationTest.java b/src/test/java/org/bouncycastle/crypto/test/ECIESKeyEncapsulationTest.java new file mode 100755 index 00000000..5d0b76ca --- /dev/null +++ b/src/test/java/org/bouncycastle/crypto/test/ECIESKeyEncapsulationTest.java @@ -0,0 +1,138 @@ +package org.bouncycastle.crypto.test; + +import java.security.SecureRandom; + +import org.bouncycastle.asn1.sec.SECNamedCurves; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.digests.SHA1Digest; +import org.bouncycastle.crypto.generators.ECKeyPairGenerator; +import org.bouncycastle.crypto.generators.KDF2BytesGenerator; +import org.bouncycastle.crypto.kems.ECIESKeyEncapsulation; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECKeyGenerationParameters; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.util.test.SimpleTest; + +/** + * Tests for the ECIES Key Encapsulation Mechanism + */ +public class ECIESKeyEncapsulationTest + extends SimpleTest +{ + public String getName() + { + return "ECIESKeyEncapsulation"; + } + + public void performTest() + throws Exception + { + + // Set EC domain parameters and generate key pair + X9ECParameters spec = SECNamedCurves.getByName("secp224r1"); + ECDomainParameters ecDomain = new ECDomainParameters(spec.getCurve(), spec.getG(), spec.getN()); + ECKeyPairGenerator ecGen = new ECKeyPairGenerator(); + + ecGen.init(new ECKeyGenerationParameters(ecDomain, new SecureRandom())); + + AsymmetricCipherKeyPair keys = ecGen.generateKeyPair(); + + // Set ECIES-KEM parameters + ECIESKeyEncapsulation kem; + KDF2BytesGenerator kdf = new KDF2BytesGenerator(new SHA1Digest()); + SecureRandom rnd = new SecureRandom(); + byte[] out = new byte[57]; + KeyParameter key1, key2; + + // Test basic ECIES-KEM + kem = new ECIESKeyEncapsulation(kdf, rnd); + + kem.init(keys.getPublic()); + key1 = (KeyParameter)kem.encrypt(out, 128); + + kem.init(keys.getPrivate()); + key2 = (KeyParameter)kem.decrypt(out, 128); + + if (!areEqual(key1.getKey(), key2.getKey())) + { + fail("failed basic test"); + } + + // Test ECIES-KEM using new cofactor mode + kem = new ECIESKeyEncapsulation(kdf, rnd, true, false, false); + + kem.init(keys.getPublic()); + key1 = (KeyParameter)kem.encrypt(out, 128); + + kem.init(keys.getPrivate()); + key2 = (KeyParameter)kem.decrypt(out, 128); + + if (!areEqual(key1.getKey(), key2.getKey())) + { + fail("failed cofactor test"); + } + + // Test ECIES-KEM using old cofactor mode + kem = new ECIESKeyEncapsulation(kdf, rnd, false, true, false); + + kem.init(keys.getPublic()); + key1 = (KeyParameter)kem.encrypt(out, 128); + + kem.init(keys.getPrivate()); + key2 = (KeyParameter)kem.decrypt(out, 128); + + if (!areEqual(key1.getKey(), key2.getKey())) + { + fail("failed old cofactor test"); + } + + // Test ECIES-KEM using single hash mode + kem = new ECIESKeyEncapsulation(kdf, rnd, false, false, true); + + kem.init(keys.getPublic()); + key1 = (KeyParameter)kem.encrypt(out, 128); + + kem.init(keys.getPrivate()); + key2 = (KeyParameter)kem.decrypt(out, 128); + + if (!areEqual(key1.getKey(), key2.getKey())) + { + fail("failed single hash test"); + } + + // Test ECIES-KEM using new cofactor mode and single hash mode + kem = new ECIESKeyEncapsulation(kdf, rnd, true, false, true); + + kem.init(keys.getPublic()); + key1 = (KeyParameter)kem.encrypt(out, 128); + + kem.init(keys.getPrivate()); + key2 = (KeyParameter)kem.decrypt(out, 128); + + if (!areEqual(key1.getKey(), key2.getKey())) + { + fail("failed cofactor and single hash test"); + } + + // Test ECIES-KEM using old cofactor mode and single hash mode + kem = new ECIESKeyEncapsulation(kdf, rnd, false, true, true); + + kem.init(keys.getPublic()); + key1 = (KeyParameter)kem.encrypt(out, 128); + + kem.init(keys.getPrivate()); + key2 = (KeyParameter)kem.decrypt(out, 128); + + if (!areEqual(key1.getKey(), key2.getKey())) + { + fail("failed old cofactor and single hash test"); + } + } + + public static void main( + String[] args) + { + runTest(new ECIESKeyEncapsulationTest()); + } +} diff --git a/src/test/java/org/bouncycastle/crypto/test/GMacTest.java b/src/test/java/org/bouncycastle/crypto/test/GMacTest.java new file mode 100644 index 00000000..d8196f96 --- /dev/null +++ b/src/test/java/org/bouncycastle/crypto/test/GMacTest.java @@ -0,0 +1,170 @@ +package org.bouncycastle.crypto.test; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.engines.AESFastEngine; +import org.bouncycastle.crypto.macs.GMac; +import org.bouncycastle.crypto.modes.GCMBlockCipher; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; + +/** + * Test vectors for AES-GMAC, extracted from <a + * href="http://csrc.nist.gov/groups/STM/cavp/documents/mac/gcmtestvectors.zip">NIST CAVP GCM test + * vectors</a>. + * + */ +public class GMacTest extends SimpleTest +{ + private static class TestCase + { + private byte[] key; + private byte[] iv; + private byte[] ad; + private byte[] tag; + private String name; + + private TestCase(final String name, final String key, final String iv, final String ad, final String tag) + { + this.name = name; + this.key = Hex.decode(key); + this.iv = Hex.decode(iv); + this.ad = Hex.decode(ad); + this.tag = Hex.decode(tag); + } + + public String getName() + { + return name; + } + + public byte[] getKey() + { + return key; + } + + public byte[] getIv() + { + return iv; + } + + public byte[] getAd() + { + return ad; + } + + public byte[] getTag() + { + return tag; + } + } + + private static TestCase[] TEST_VECTORS = new TestCase[] { + // Count = 0, from each of the PTlen = 0 test vector sequences + new TestCase("128/96/0/128", "11754cd72aec309bf52f7687212e8957", "3c819d9a9bed087615030b65", "", + "250327c674aaf477aef2675748cf6971"), + new TestCase("128/96/0/120", "272f16edb81a7abbea887357a58c1917", "794ec588176c703d3d2a7a07", "", + "b6e6f197168f5049aeda32dafbdaeb"), + new TestCase("128/96/0/112", "81b6844aab6a568c4556a2eb7eae752f", "ce600f59618315a6829bef4d", "", + "89b43e9dbc1b4f597dbbc7655bb5"), + new TestCase("128/96/0/104", "cde2f9a9b1a004165ef9dc981f18651b", "29512c29566c7322e1e33e8e", "", + "2e58ce7dabd107c82759c66a75"), + new TestCase("128/96/0/96", "b01e45cc3088aaba9fa43d81d481823f", "5a2c4a66468713456a4bd5e1", "", + "014280f944f53c681164b2ff"), + + new TestCase("128/96/128/128", "77be63708971c4e240d1cb79e8d77feb", "e0e00f19fed7ba0136a797f3", + "7a43ec1d9c0a5a78a0b16533a6213cab", "209fcc8d3675ed938e9c7166709dd946"), + new TestCase("128/96/128/96", "bea48ae4980d27f357611014d4486625", "32bddb5c3aa998a08556454c", + "8a50b0b8c7654bced884f7f3afda2ead", "8e0f6d8bf05ffebe6f500eb1"), + + new TestCase("128/96/384/128", "99e3e8793e686e571d8285c564f75e2b", "c2dd0ab868da6aa8ad9c0d23", + "b668e42d4e444ca8b23cfdd95a9fedd5178aa521144890b093733cf5cf22526c5917ee476541809ac6867a8c399309fc", + "3f4fba100eaf1f34b0baadaae9995d85"), + new TestCase("128/96/384/96", "c77acd1b0918e87053cb3e51651e7013", "39ff857a81745d10f718ac00", + "407992f82ea23b56875d9a3cb843ceb83fd27cb954f7c5534d58539fe96fb534502a1b38ea4fac134db0a42de4be1137", + "2a5dc173285375dc82835876"), + + new TestCase( + "128/1024/0/128", + "d0f1f4defa1e8c08b4b26d576392027c", + "42b4f01eb9f5a1ea5b1eb73b0fb0baed54f387ecaa0393c7d7dffc6af50146ecc021abf7eb9038d4303d91f8d741a11743166c0860208bcc02c6258fd9511a2fa626f96d60b72fcff773af4e88e7a923506e4916ecbd814651e9f445adef4ad6a6b6c7290cc13b956130eef5b837c939fcac0cbbcc9656cd75b13823ee5acdac", + "", "7ab49b57ddf5f62c427950111c5c4f0d"), + new TestCase( + "128/1024/384/96", + "3cce72d37933394a8cac8a82deada8f0", + "aa2f0d676d705d9733c434e481972d4888129cf7ea55c66511b9c0d25a92a174b1e28aa072f27d4de82302828955aadcb817c4907361869bd657b45ff4a6f323871987fcf9413b0702d46667380cd493ed24331a28b9ce5bbfa82d3a6e7679fcce81254ba64abcad14fd18b22c560a9d2c1cd1d3c42dac44c683edf92aced894", + "5686b458e9c176f4de8428d9ebd8e12f569d1c7595cf49a4b0654ab194409f86c0dd3fdb8eb18033bb4338c70f0b97d1", + "a3a9444b21f330c3df64c8b6"), }; + + public void performTest() + { + for (int i = 0; i < TEST_VECTORS.length; i++) + { + TestCase testCase = TEST_VECTORS[i]; + + Mac mac = new GMac(new GCMBlockCipher(new AESFastEngine()), testCase.getTag().length * 8); + CipherParameters key = new KeyParameter(testCase.getKey()); + mac.init(new ParametersWithIV(key, testCase.getIv())); + + testSingleByte(mac, testCase); + testMultibyte(mac, testCase); + } + + // Invalid mac size + testInvalidMacSize(97); + testInvalidMacSize(136); + testInvalidMacSize(88); + testInvalidMacSize(64); + } + + private void testInvalidMacSize(int size) + { + try + { + GMac mac = new GMac(new GCMBlockCipher(new AESFastEngine()), size); + mac.init(new ParametersWithIV(null, new byte[16])); + fail("Expected failure for illegal mac size " + size); + } catch (IllegalArgumentException e) + { + } + } + + private void testMultibyte(Mac mac, TestCase testCase) + { + mac.update(testCase.getAd(), 0, testCase.getAd().length); + checkMac(mac, testCase); + } + + private void testSingleByte(Mac mac, TestCase testCase) + { + final byte[] ad = testCase.getAd(); + for (int i = 0; i < ad.length; i++) + { + mac.update(ad[i]); + } + checkMac(mac, testCase); + } + + private void checkMac(Mac mac, TestCase testCase) + { + final byte[] generatedMac = new byte[mac.getMacSize()]; + mac.doFinal(generatedMac, 0); + if (!areEqual(testCase.getTag(), generatedMac)) + { + fail("Failed " + testCase.getName() + " - expected " + new String(Hex.encode(testCase.getTag())) + " got " + + new String(Hex.encode(generatedMac))); + } + } + + public String getName() + { + return "GMac"; + } + + public static void main(String[] args) + { + runTest(new GMacTest()); + } +} diff --git a/src/test/java/org/bouncycastle/crypto/test/NonMemoableDigestTest.java b/src/test/java/org/bouncycastle/crypto/test/NonMemoableDigestTest.java new file mode 100644 index 00000000..d332393b --- /dev/null +++ b/src/test/java/org/bouncycastle/crypto/test/NonMemoableDigestTest.java @@ -0,0 +1,112 @@ +package org.bouncycastle.crypto.test; + +import org.bouncycastle.crypto.digests.NonMemoableDigest; +import org.bouncycastle.crypto.digests.SHA1Digest; +import org.bouncycastle.crypto.macs.HMac; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTestResult; +import org.bouncycastle.util.test.Test; +import org.bouncycastle.util.test.TestResult; + +/** + * SHA1 HMac Test, test vectors from RFC 2202 + */ +public class NonMemoableDigestTest + implements Test +{ + final static String[] keys = { + "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "4a656665", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "0102030405060708090a0b0c0d0e0f10111213141516171819", + "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }; + + final static String[] digests = { + "b617318655057264e28bc0b6fb378c8ef146be00", + "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79", + "125d7342b9ac11cd91a39af48aa17b4f63f175d3", + "4c9007f4026250c6bc8414f9bf50c86c2d7235da", + "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04", + "aa4ae5e15272d00e95705637ce8a3b55ed402112", + "e8e99d0f45237d786d6bbaa7965c7808bbff1a91", + "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04", + "aa4ae5e15272d00e95705637ce8a3b55ed402112", + "e8e99d0f45237d786d6bbaa7965c7808bbff1a91" + }; + + final static String[] messages = { + "Hi There", + "what do ya want for nothing?", + "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", + "Test With Truncation", + "Test Using Larger Than Block-Size Key - Hash Key First", + "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data" + }; + + public String getName() + { + return "NonMemoableDigest"; + } + + public TestResult perform() + { + HMac hmac = new HMac(new NonMemoableDigest(new SHA1Digest())); + byte[] resBuf = new byte[hmac.getMacSize()]; + + for (int i = 0; i < messages.length; i++) + { + byte[] m = messages[i].getBytes(); + if (messages[i].startsWith("0x")) + { + m = Hex.decode(messages[i].substring(2)); + } + hmac.init(new KeyParameter(Hex.decode(keys[i]))); + hmac.update(m, 0, m.length); + hmac.doFinal(resBuf, 0); + + if (!Arrays.areEqual(resBuf, Hex.decode(digests[i]))) + { + return new SimpleTestResult(false, getName() + ": Vector " + i + " failed"); + } + } + + // + // test reset + // + int vector = 0; // vector used for test + byte[] m = messages[vector].getBytes(); + if (messages[vector].startsWith("0x")) + { + m = Hex.decode(messages[vector].substring(2)); + } + hmac.init(new KeyParameter(Hex.decode(keys[vector]))); + hmac.update(m, 0, m.length); + hmac.doFinal(resBuf, 0); + hmac.reset(); + hmac.update(m, 0, m.length); + hmac.doFinal(resBuf, 0); + + if (!Arrays.areEqual(resBuf, Hex.decode(digests[vector]))) + { + return new SimpleTestResult(false, getName() + + "Reset with vector " + vector + " failed"); + } + + return new SimpleTestResult(true, getName() + ": Okay"); + } + + public static void main( + String[] args) + { + NonMemoableDigestTest test = new NonMemoableDigestTest(); + TestResult result = test.perform(); + + System.out.println(result); + } +} diff --git a/src/test/java/org/bouncycastle/crypto/test/RSAKeyEncapsulationTest.java b/src/test/java/org/bouncycastle/crypto/test/RSAKeyEncapsulationTest.java new file mode 100755 index 00000000..50e751b7 --- /dev/null +++ b/src/test/java/org/bouncycastle/crypto/test/RSAKeyEncapsulationTest.java @@ -0,0 +1,61 @@ +package org.bouncycastle.crypto.test; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.digests.SHA1Digest; +import org.bouncycastle.crypto.generators.KDF2BytesGenerator; +import org.bouncycastle.crypto.generators.RSAKeyPairGenerator; +import org.bouncycastle.crypto.kems.RSAKeyEncapsulation; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.RSAKeyGenerationParameters; +import org.bouncycastle.util.test.SimpleTest; + +/** + * Tests for the RSA Key Encapsulation Mechanism + */ +public class RSAKeyEncapsulationTest + extends SimpleTest +{ + public String getName() + { + return "RSAKeyEncapsulation"; + } + + public void performTest() + throws Exception + { + // Generate RSA key pair + RSAKeyPairGenerator rsaGen = new RSAKeyPairGenerator(); + rsaGen.init(new RSAKeyGenerationParameters(BigInteger.valueOf(65537), new SecureRandom(), 1024, 5)); + AsymmetricCipherKeyPair keys = rsaGen.generateKeyPair(); + + // Set RSA-KEM parameters + RSAKeyEncapsulation kem; + KDF2BytesGenerator kdf = new KDF2BytesGenerator(new SHA1Digest()); + SecureRandom rnd = new SecureRandom(); + byte[] out = new byte[128]; + KeyParameter key1, key2; + + // Test RSA-KEM + kem = new RSAKeyEncapsulation(kdf, rnd); + + kem.init(keys.getPublic()); + key1 = (KeyParameter)kem.encrypt(out, 128); + + kem.init(keys.getPrivate()); + key2 = (KeyParameter)kem.decrypt(out, 128); + + if (!areEqual(key1.getKey(), key2.getKey())) + { + fail("failed test"); + } + } + + public static void main( + String[] args) + { + runTest(new RSAKeyEncapsulationTest()); + } +} diff --git a/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java b/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java index aea3003d..906eb910 100644 --- a/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java +++ b/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java @@ -96,6 +96,7 @@ public class RegressionTest new CMacTest(), new EAXTest(), new GCMTest(), + new GMacTest(), new HCFamilyTest(), new HCFamilyVecTest(), new ISAACTest(), @@ -113,10 +114,9 @@ public class RegressionTest new DSTU4145Test(), new SipHashTest(), new OCBTest(), - new HashDRBGTest(), - new HMacDRBGTest(), - new CTRDRBGTest(), - new DualECDRBGTest() + new NonMemoableDigestTest(), + new RSAKeyEncapsulationTest(), + new ECIESKeyEncapsulationTest() }; public static void main( diff --git a/src/test/java/org/bouncycastle/crypto/test/WhirlpoolDigestTest.java b/src/test/java/org/bouncycastle/crypto/test/WhirlpoolDigestTest.java index 9f7f5342..542e6e6b 100644 --- a/src/test/java/org/bouncycastle/crypto/test/WhirlpoolDigestTest.java +++ b/src/test/java/org/bouncycastle/crypto/test/WhirlpoolDigestTest.java @@ -1,6 +1,7 @@ package org.bouncycastle.crypto.test; import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.digests.SHA1Digest; import org.bouncycastle.crypto.digests.WhirlpoolDigest; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; @@ -11,44 +12,42 @@ import org.bouncycastle.util.test.SimpleTest; * */ public class WhirlpoolDigestTest - extends SimpleTest + extends DigestTest { - private static String[][] _isoVectors = + private static String[] messages = { - { - "", - "19FA61D75522A4669B44E39C1D2E1726C530232130D407F89AFEE0964997F7A73E83BE698B288FEBCF88E3E03C4F0757EA8964E59B63D93708B138CC42A66EB3" - }, - { - "a", - "8ACA2602792AEC6F11A67206531FB7D7F0DFF59413145E6973C45001D0087B42D11BC645413AEFF63A42391A39145A591A92200D560195E53B478584FDAE231A" - }, - { - "abc", - "4E2448A4C6F486BB16B6562C73B4020BF3043E3A731BCE721AE1B303D97E6D4C7181EEBDB6C57E277D0E34957114CBD6C797FC9D95D8B582D225292076D4EEF5" - }, - { - "message digest", - "378C84A4126E2DC6E56DCC7458377AAC838D00032230F53CE1F5700C0FFB4D3B8421557659EF55C106B4B52AC5A4AAA692ED920052838F3362E86DBD37A8903E" - }, - { - "abcdefghijklmnopqrstuvwxyz", - "F1D754662636FFE92C82EBB9212A484A8D38631EAD4238F5442EE13B8054E41B08BF2A9251C30B6A0B8AAE86177AB4A6F68F673E7207865D5D9819A3DBA4EB3B" - }, - { - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", - "DC37E008CF9EE69BF11F00ED9ABA26901DD7C28CDEC066CC6AF42E40F82F3A1E08EBA26629129D8FB7CB57211B9281A65517CC879D7B962142C65F5A7AF01467" - }, - { - "12345678901234567890123456789012345678901234567890123456789012345678901234567890", - "466EF18BABB0154D25B9D38A6414F5C08784372BCCB204D6549C4AFADB6014294D5BD8DF2A6C44E538CD047B2681A51A2C60481E88C5A20B2C2A80CF3A9A083B" - }, - { - "abcdbcdecdefdefgefghfghighijhijk", - "2A987EA40F917061F5D6F0A0E4644F488A7A5A52DEEE656207C562F988E95C6916BDC8031BC5BE1B7B947639FE050B56939BAAA0ADFF9AE6745B7B181C3BE3FD" - } + "", + "a", + "abc", + "message digest", + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + "12345678901234567890123456789012345678901234567890123456789012345678901234567890", + "abcdbcdecdefdefgefghfghighijhijk" }; - + + private static String[] digests = + { + "19FA61D75522A4669B44E39C1D2E1726C530232130D407F89AFEE0964997F7A73E83BE698B288FEBCF88E3E03C4F0757EA8964E59B63D93708B138CC42A66EB3", + "8ACA2602792AEC6F11A67206531FB7D7F0DFF59413145E6973C45001D0087B42D11BC645413AEFF63A42391A39145A591A92200D560195E53B478584FDAE231A", + "4E2448A4C6F486BB16B6562C73B4020BF3043E3A731BCE721AE1B303D97E6D4C7181EEBDB6C57E277D0E34957114CBD6C797FC9D95D8B582D225292076D4EEF5", + "378C84A4126E2DC6E56DCC7458377AAC838D00032230F53CE1F5700C0FFB4D3B8421557659EF55C106B4B52AC5A4AAA692ED920052838F3362E86DBD37A8903E", + "F1D754662636FFE92C82EBB9212A484A8D38631EAD4238F5442EE13B8054E41B08BF2A9251C30B6A0B8AAE86177AB4A6F68F673E7207865D5D9819A3DBA4EB3B", + "DC37E008CF9EE69BF11F00ED9ABA26901DD7C28CDEC066CC6AF42E40F82F3A1E08EBA26629129D8FB7CB57211B9281A65517CC879D7B962142C65F5A7AF01467", + "466EF18BABB0154D25B9D38A6414F5C08784372BCCB204D6549C4AFADB6014294D5BD8DF2A6C44E538CD047B2681A51A2C60481E88C5A20B2C2A80CF3A9A083B", + "2A987EA40F917061F5D6F0A0E4644F488A7A5A52DEEE656207C562F988E95C6916BDC8031BC5BE1B7B947639FE050B56939BAAA0ADFF9AE6745B7B181C3BE3FD" + }; + + WhirlpoolDigestTest() + { + super(new WhirlpoolDigest(), messages, digests); + } + + protected Digest cloneDigest(Digest digest) + { + return new WhirlpoolDigest((WhirlpoolDigest)digest); + } + private static String _millionAResultVector = "0C99005BEB57EFF50A7CF005560DDF5D29057FD86B20BFD62DECA0F1CCEA4AF51FC15490EDDC47AF32BB2B66C34FF9AD8C6008AD677F77126953B226E4ED8B01"; private static String _thirtyOneZeros = "3E3F188F8FEBBEB17A933FEAF7FE53A4858D80C915AD6A1418F0318E68D49B4E459223CD414E0FBC8A57578FD755D86E827ABEF4070FC1503E25D99E382F72BA"; @@ -60,11 +59,7 @@ public class WhirlpoolDigestTest public void performTest() { - for (int i = 0; i < _isoVectors.length; i++) - { - performStandardVectorTest("ISO vector test ["+i+"]", _isoVectors[i][0], - _isoVectors[i][1]); - } + super.performTest(); byte[] thirtyOneZeros = new byte[31]; performStandardVectorTest("31 zeroes test", @@ -92,12 +87,6 @@ public class WhirlpoolDigestTest } } - private void performStandardVectorTest(String testTitle, String inputBytesAsString, - String resultsAsHex) - { - doPerformTest(testTitle, inputBytesAsString.getBytes(), resultsAsHex); - } - private String createHexOutputFromDigest(byte[] digestBytes) { String resStr; @@ -108,7 +97,6 @@ public class WhirlpoolDigestTest resStr = new String(Hex.encode(resBuf)); return resStr; } - public static void main(String[] args) { diff --git a/src/test/java/org/bouncycastle/jce/provider/test/DESedeTest.java b/src/test/java/org/bouncycastle/jce/provider/test/DESedeTest.java index 4ac77b8e..313f665f 100644 --- a/src/test/java/org/bouncycastle/jce/provider/test/DESedeTest.java +++ b/src/test/java/org/bouncycastle/jce/provider/test/DESedeTest.java @@ -126,6 +126,7 @@ public class DESedeTest } private void wrapTest( + String alg, int id, byte[] kek, byte[] iv, @@ -134,13 +135,13 @@ public class DESedeTest { try { - Cipher wrapper = Cipher.getInstance("DESedeWrap", "BC"); + Cipher wrapper = Cipher.getInstance(alg + "Wrap", "BC"); - wrapper.init(Cipher.WRAP_MODE, new SecretKeySpec(kek, "DESEDE"), new IvParameterSpec(iv)); + wrapper.init(Cipher.WRAP_MODE, new SecretKeySpec(kek, alg), new IvParameterSpec(iv)); try { - byte[] cText = wrapper.wrap(new SecretKeySpec(in, "DESEDE")); + byte[] cText = wrapper.wrap(new SecretKeySpec(in, alg)); if (!equalArray(cText, out)) { fail("failed wrap test " + id + " expected " + new String(Hex.encode(out)) + " got " + new String(Hex.encode(cText))); @@ -151,11 +152,11 @@ public class DESedeTest fail("failed wrap test exception " + e.toString()); } - wrapper.init(Cipher.UNWRAP_MODE, new SecretKeySpec(kek, "DESEDE")); + wrapper.init(Cipher.UNWRAP_MODE, new SecretKeySpec(kek, alg)); try { - Key pText = wrapper.unwrap(out, "DESede", Cipher.SECRET_KEY); + Key pText = wrapper.unwrap(out, alg, Cipher.SECRET_KEY); if (!equalArray(pText.getEncoded(), in)) { fail("failed unwrap test " + id + " expected " + new String(Hex.encode(in)) + " got " + new String(Hex.encode(pText.getEncoded()))); @@ -173,6 +174,7 @@ public class DESedeTest } public void test( + String alg, int strength, byte[] input, byte[] output) @@ -191,19 +193,19 @@ public class DESedeTest try { - keyGen = KeyGenerator.getInstance("DESEDE", "BC"); + keyGen = KeyGenerator.getInstance(alg, "BC"); keyGen.init(strength, rand); key = keyGen.generateKey(); - in = Cipher.getInstance("DESEDE/ECB/PKCS7Padding", "BC"); - out = Cipher.getInstance("DESEDE/ECB/PKCS7Padding", "BC"); + in = Cipher.getInstance(alg + "/ECB/PKCS7Padding", "BC"); + out = Cipher.getInstance(alg + "/ECB/PKCS7Padding", "BC"); out.init(Cipher.ENCRYPT_MODE, key, rand); } catch (Exception e) { - fail("DESEDE failed initialisation - " + e.toString()); + fail(alg + " failed initialisation - " + e.toString()); } try @@ -212,7 +214,7 @@ public class DESedeTest } catch (Exception e) { - fail("DESEDE failed initialisation - " + e.toString()); + fail(alg + " failed initialisation - " + e.toString()); } // @@ -233,7 +235,7 @@ public class DESedeTest } catch (IOException e) { - fail("DESEDE failed encryption - " + e.toString()); + fail(alg + " failed encryption - " + e.toString()); } byte[] bytes; @@ -242,7 +244,7 @@ public class DESedeTest if (!equalArray(bytes, output)) { - fail("DESEDE failed encryption - expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(bytes))); + fail(alg + " failed encryption - expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(bytes))); } // @@ -266,12 +268,12 @@ public class DESedeTest } catch (Exception e) { - fail("DESEDE failed encryption - " + e.toString()); + fail(alg + " failed encryption - " + e.toString()); } if (!equalArray(bytes, input)) { - fail("DESEDE failed decryption - expected " + new String(Hex.encode(input)) + " got " + new String(Hex.encode(bytes))); + fail(alg + " failed decryption - expected " + new String(Hex.encode(input)) + " got " + new String(Hex.encode(bytes))); } // @@ -279,17 +281,17 @@ public class DESedeTest // try { - SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede", "BC"); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(alg, "BC"); DESedeKeySpec keySpec = (DESedeKeySpec)keyFactory.getKeySpec((SecretKey)key, DESedeKeySpec.class); if (!equalArray(key.getEncoded(), keySpec.getKey(), 16)) { - fail("DESEDE KeySpec does not match key."); + fail(alg + " KeySpec does not match key."); } } catch (Exception e) { - fail("DESEDE failed keyspec - " + e.toString()); + fail(alg + " failed keyspec - " + e.toString()); } } @@ -297,7 +299,12 @@ public class DESedeTest { for (int i = 0; i != cipherTests1.length; i += 2) { - test(Integer.parseInt(cipherTests1[i]), input1, Hex.decode(cipherTests1[i + 1])); + test("DESEDE", Integer.parseInt(cipherTests1[i]), input1, Hex.decode(cipherTests1[i + 1])); + } + + for (int i = 0; i != cipherTests1.length; i += 2) + { + test("TDEA", Integer.parseInt(cipherTests1[i]), input1, Hex.decode(cipherTests1[i + 1])); } byte[] kek1 = Hex.decode("255e0d1c07b646dfb3134cc843ba8aa71f025b7c0838251f"); @@ -305,7 +312,8 @@ public class DESedeTest byte[] in1 = Hex.decode("2923bf85e06dd6ae529149f1f1bae9eab3a7da3d860d3e98"); byte[] out1 = Hex.decode("690107618ef092b3b48ca1796b234ae9fa33ebb4159604037db5d6a84eb3aac2768c632775a467d4"); - wrapTest(1, kek1, iv1, in1, out1); + wrapTest("DESEDE", 1, kek1, iv1, in1, out1); + wrapTest("TDEA", 1, kek1, iv1, in1, out1); } public static void main( diff --git a/src/test/java/org/bouncycastle/jce/provider/test/DSATest.java b/src/test/java/org/bouncycastle/jce/provider/test/DSATest.java index 2a81b387..e0478997 100644 --- a/src/test/java/org/bouncycastle/jce/provider/test/DSATest.java +++ b/src/test/java/org/bouncycastle/jce/provider/test/DSATest.java @@ -477,9 +477,6 @@ public class DSATest private void testECDSA239bitBinary(String algorithm, DERObjectIdentifier oid) throws Exception { -// BigInteger r = new BigInteger("21596333210419611985018340039034612628818151486841789642455876922391552"); -// BigInteger s = new BigInteger("197030374000731686738334997654997227052849804072198819102649413465737174"); - byte[] kData = BigIntegers.asUnsignedByteArray(new BigInteger("171278725565216523967285789236956265265265235675811949404040041670216363")); SecureRandom k = new FixedSecureRandom(kData); @@ -828,6 +825,117 @@ public class DSATest } } + private void testDSA2Parameters() + throws Exception + { + byte[] seed = Hex.decode("4783081972865EA95D43318AB2EAF9C61A2FC7BBF1B772A09017BDF5A58F4FF0"); + + AlgorithmParameterGenerator a = AlgorithmParameterGenerator.getInstance("DSA", "BC"); + a.init(2048, new DSATestSecureRandom(seed)); + AlgorithmParameters params = a.generateParameters(); + + DSAParameterSpec dsaP = (DSAParameterSpec)params.getParameterSpec(DSAParameterSpec.class); + + if (!dsaP.getQ().equals(new BigInteger("C24ED361870B61E0D367F008F99F8A1F75525889C89DB1B673C45AF5867CB467", 16))) + { + fail("Q incorrect"); + } + + if (!dsaP.getP().equals(new BigInteger( + "F56C2A7D366E3EBDEAA1891FD2A0D099" + + "436438A673FED4D75F594959CFFEBCA7BE0FC72E4FE67D91" + + "D801CBA0693AC4ED9E411B41D19E2FD1699C4390AD27D94C" + + "69C0B143F1DC88932CFE2310C886412047BD9B1C7A67F8A2" + + "5909132627F51A0C866877E672E555342BDF9355347DBD43" + + "B47156B2C20BAD9D2B071BC2FDCF9757F75C168C5D9FC431" + + "31BE162A0756D1BDEC2CA0EB0E3B018A8B38D3EF2487782A" + + "EB9FBF99D8B30499C55E4F61E5C7DCEE2A2BB55BD7F75FCD" + + "F00E48F2E8356BDB59D86114028F67B8E07B127744778AFF" + + "1CF1399A4D679D92FDE7D941C5C85C5D7BFF91BA69F9489D" + + "531D1EBFA727CFDA651390F8021719FA9F7216CEB177BD75", 16))) + { + fail("P incorrect"); + } + + if (!dsaP.getG().equals(new BigInteger( + "8DC6CC814CAE4A1C05A3E186A6FE27EA" + + "BA8CDB133FDCE14A963A92E809790CBA096EAA26140550C1" + + "29FA2B98C16E84236AA33BF919CD6F587E048C52666576DB" + + "6E925C6CBE9B9EC5C16020F9A44C9F1C8F7A8E611C1F6EC2" + + "513EA6AA0B8D0F72FED73CA37DF240DB57BBB27431D61869" + + "7B9E771B0B301D5DF05955425061A30DC6D33BB6D2A32BD0" + + "A75A0A71D2184F506372ABF84A56AEEEA8EB693BF29A6403" + + "45FA1298A16E85421B2208D00068A5A42915F82CF0B858C8" + + "FA39D43D704B6927E0B2F916304E86FB6A1B487F07D8139E" + + "428BB096C6D67A76EC0B8D4EF274B8A2CF556D279AD267CC" + + "EF5AF477AFED029F485B5597739F5D0240F67C2D948A6279", 16))) + { + fail("G incorrect"); + } + + KeyPairGenerator g = KeyPairGenerator.getInstance("DSA", "BC"); + g.initialize(dsaP, new FixedSecureRandom(Hex.decode("0CAF2EF547EC49C4F3A6FE6DF4223A174D01F2C115D49A6F73437C29A2A8458C"))); + KeyPair p = g.generateKeyPair(); + + DSAPrivateKey sKey = (DSAPrivateKey)p.getPrivate(); + DSAPublicKey vKey = (DSAPublicKey)p.getPublic(); + + if (!vKey.getY().equals(new BigInteger( + "2828003D7C747199143C370FDD07A286" + + "1524514ACC57F63F80C38C2087C6B795B62DE1C224BF8D1D" + + "1424E60CE3F5AE3F76C754A2464AF292286D873A7A30B7EA" + + "CBBC75AAFDE7191D9157598CDB0B60E0C5AA3F6EBE425500" + + "C611957DBF5ED35490714A42811FDCDEB19AF2AB30BEADFF" + + "2907931CEE7F3B55532CFFAEB371F84F01347630EB227A41" + + "9B1F3F558BC8A509D64A765D8987D493B007C4412C297CAF" + + "41566E26FAEE475137EC781A0DC088A26C8804A98C23140E" + + "7C936281864B99571EE95C416AA38CEEBB41FDBFF1EB1D1D" + + "C97B63CE1355257627C8B0FD840DDB20ED35BE92F08C49AE" + + "A5613957D7E5C7A6D5A5834B4CB069E0831753ECF65BA02B", 16))) + { + fail("Y value incorrect"); + } + + if (!sKey.getX().equals( + new BigInteger("0CAF2EF547EC49C4F3A6FE6DF4223A174D01F2C115D49A6F73437C29A2A8458C", 16))) + { + fail("X value incorrect"); + } + + byte[] encodeParams = params.getEncoded(); + + AlgorithmParameters a2 = AlgorithmParameters.getInstance("DSA", "BC"); + a2.init(encodeParams); + + // a and a2 should be equivalent! + byte[] encodeParams_2 = a2.getEncoded(); + + if (!areEqual(encodeParams, encodeParams_2)) + { + fail("encode/decode parameters failed"); + } + + Signature s = Signature.getInstance("DSA", "BC"); + byte[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; + + s.initSign(sKey); + + s.update(data); + + byte[] sigBytes = s.sign(); + + s = Signature.getInstance("DSA", "BC"); + + s.initVerify(vKey); + + s.update(data); + + if (!s.verify(sigBytes)) + { + fail("DSA verification failed"); + } + } + public void performTest() throws Exception { @@ -845,9 +953,12 @@ public class DSATest testECDSA239bitBinary("SHA1withCVC-ECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_1); testECDSA239bitBinary("SHA224withCVC-ECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_224); testECDSA239bitBinary("SHA256withCVC-ECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_256); - + testECDSA239bitBinary("SHA384withCVC-ECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_384); + testECDSA239bitBinary("SHA512withCVC-ECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_512); + testGeneration(); testParameters(); + testDSA2Parameters(); } protected BigInteger[] derDecode( @@ -878,4 +989,28 @@ public class DSATest runTest(new DSATest()); } + + private class DSATestSecureRandom + extends FixedSecureRandom + { + private boolean first = true; + + public DSATestSecureRandom(byte[] value) + { + super(value); + } + + public void nextBytes(byte[] bytes) + { + if (first) + { + super.nextBytes(bytes); + first = false; + } + else + { + bytes[bytes.length - 1] = 2; + } + } + } } diff --git a/src/test/java/org/bouncycastle/jce/provider/test/GMacTest.java b/src/test/java/org/bouncycastle/jce/provider/test/GMacTest.java new file mode 100644 index 00000000..72a4888a --- /dev/null +++ b/src/test/java/org/bouncycastle/jce/provider/test/GMacTest.java @@ -0,0 +1,145 @@ +package org.bouncycastle.jce.provider.test; + +import java.security.NoSuchAlgorithmException; +import java.security.Security; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.Mac; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; +import org.bouncycastle.util.test.TestFailedException; + +public class GMacTest + extends SimpleTest +{ + public String getName() + { + return "GMac"; + } + + public void performTest() + throws Exception + { + checkRegistrations(); + } + + private void checkRegistrations() + throws Exception + { + List<String> missingMacs = new ArrayList<String>(); + List<String> missingKeyGens = new ArrayList<String>(); + + String[] ciphers = new String[] { "AES", "NOEKEON", "Twofish", "CAST6", "SEED", "Serpent", "RC6", "CAMELLIA" }; + String[] macs = new String[] + { + "a52308801b32d4770c701ace9b826f12", + "cf11dacaf6024a78dba76b256e23caab", + "13db7c428e5a7128149b5ec782d07fac", + "d13a33e78e48b274bf7d64bf9aecdb82", + "d05d550054735c6e7e01b6981fc14b4e", + "4a34dfe4f5410afd7c40b1e110377a73", + "d9f597c96b41f641da6c83d4760f543b", + "371ad8cc920c6bda2a26d8f237bd446b" + }; + + for (int i = 0; i < ciphers.length; i++) + { + String cipherName = ciphers[i]; + Cipher cipher; + try + { + cipher = Cipher.getInstance(cipherName, "BC"); + } + catch (Exception e) + { + System.err.println(cipherName + ": " + e.getMessage()); + continue; + } + int blocksize; + try + { + blocksize = cipher.getBlockSize(); + } + catch (Exception e) + { + System.err.println(cipherName + ": " + e.getMessage()); + continue; + } + // GCM is defined over 128 bit block ciphers + if (blocksize == 16) + { + String macName = cipherName + "-GMAC"; + String macNameAlt = cipherName + "GMAC"; + + // Check we have a GMAC registered for each name + checkMac(macName, missingMacs, missingKeyGens, macs[i]); + checkMac(macNameAlt, missingMacs, missingKeyGens, macs[i]); + } + } + if (missingMacs.size() != 0) + { + fail("Did not find GMAC registrations for the following ciphers: " + missingMacs); + } + if (missingKeyGens.size() != 0) + { + Collections.sort(missingKeyGens); + fail("Did not find GMAC KeyGenerator registrations for the following macs: " + missingKeyGens); + } + } + + private void checkMac(String name, List<String> missingMacs, List<String> missingKeyGens, String macOutput) + { + try + { + Mac mac = Mac.getInstance(name); + + mac.init(new SecretKeySpec(new byte[mac.getMacLength()], mac.getAlgorithm()), new IvParameterSpec( + new byte[16])); + mac.update(new byte[128]); + byte[] bytes = mac.doFinal(); + + if (!Arrays.areEqual(bytes, Hex.decode(macOutput))) + { + fail("wrong mac value computed for " + name); + } + + try + { + KeyGenerator kg = KeyGenerator.getInstance(name); + kg.generateKey(); + } + catch (NoSuchAlgorithmException e) + { + missingKeyGens.add(name); + } + } + catch (NoSuchAlgorithmException e) + { + missingMacs.add(name); + } + catch (TestFailedException e) + { + throw e; + } + catch (Exception e) + { + fail("Unexpected error", e); + } + } + + public static void main(String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new GMacTest()); + } +}
\ No newline at end of file diff --git a/src/test/java/org/bouncycastle/jce/provider/test/JceTestUtil.java b/src/test/java/org/bouncycastle/jce/provider/test/JceTestUtil.java new file mode 100644 index 00000000..9c0805a8 --- /dev/null +++ b/src/test/java/org/bouncycastle/jce/provider/test/JceTestUtil.java @@ -0,0 +1,49 @@ +package org.bouncycastle.jce.provider.test; + +import java.security.Security; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +abstract class JceTestUtil +{ + private JceTestUtil() + { + } + + static String[] getRegisteredAlgorithms(String prefix, String[] exclusionPatterns) + { + final BouncyCastleProvider prov = (BouncyCastleProvider)Security.getProvider("BC"); + + List matches = new ArrayList(); + Enumeration algos = prov.keys(); + while (algos.hasMoreElements()) + { + String algo = (String)algos.nextElement(); + if (!algo.startsWith(prefix)) + { + continue; + } + String algoName = algo.substring(prefix.length()); + if (!isExcluded(algoName, exclusionPatterns)) + { + matches.add(algoName); + } + } + return (String[])matches.toArray(new String[matches.size()]); + } + + private static boolean isExcluded(String algoName, String[] exclusionPatterns) + { + for (int i = 0; i < exclusionPatterns.length; i++) + { + if (algoName.contains(exclusionPatterns[i])) + { + return true; + } + } + return false; + } +} diff --git a/src/test/java/org/bouncycastle/jce/provider/test/RegressionTest.java b/src/test/java/org/bouncycastle/jce/provider/test/RegressionTest.java index 52026bdb..85972a05 100644 --- a/src/test/java/org/bouncycastle/jce/provider/test/RegressionTest.java +++ b/src/test/java/org/bouncycastle/jce/provider/test/RegressionTest.java @@ -67,6 +67,7 @@ public class RegressionTest new SigNameTest(), new MQVTest(), new CMacTest(), + new GMacTest(), new DSTU4145Test(), new CRL5Test(), new SipHashTest(), diff --git a/src/test/java/org/bouncycastle/openpgp/test/PGPKeyRingTest.java b/src/test/java/org/bouncycastle/openpgp/test/PGPKeyRingTest.java index 46853110..8e044aff 100644 --- a/src/test/java/org/bouncycastle/openpgp/test/PGPKeyRingTest.java +++ b/src/test/java/org/bouncycastle/openpgp/test/PGPKeyRingTest.java @@ -11,6 +11,8 @@ import java.util.Iterator; import javax.crypto.Cipher; +import org.bouncycastle.bcpg.HashAlgorithmTags; +import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.spec.ElGamalParameterSpec; import org.bouncycastle.openpgp.PGPEncryptedData; @@ -25,8 +27,10 @@ import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; +import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; @@ -1095,6 +1099,48 @@ public class PGPKeyRingTest "iYW7+SWY76AAl+gmWIMm2jbN8bZXFk4jmIxpycHCrtoXX8rUk/0+se8NvbmAdMGK" + "POOoD7oxdRmJSU5hSspOCHrCwCa3"); + + // Key from http://www.angelfire.com/pr/pgpf/pgpoddities.html + private static final char[] v3KeyPass = "test@key.test".toCharArray(); + + private static final byte[] pubv3 = Base64.decode( + "mQENAzroPPgAAAEIANnTx/gHfag7qRMG6cVUnYZJjLcsdF6JSaVs+PUDCZ8l2+Z2" + + "V9tgxByp26bymIlq5qFFeoA5vCiKc8qzYiEVLJVVIIDjw/id2gq/TgmxoLAwiDQM" + + "TUKdCFa6pmR/uaxyrnJxfUA7+Qh0R0OjoCxNlrmyO3eiKstsJGqSUFIQq7GhcHc4" + + "nbV59zHhEWnH7DX7sDa9CgF11WxM3sjWp15iOoP1nixhmchDtQ7foUxLsCF36G/4" + + "ijcbN2NjiCDYMFburN8fXgrQzYHAIIiVFE0J+fbXNfPRmnbhQdaC8rIdiQ3tExBb" + + "N0qWhGPT9M4JOZd1yPdFMb9gbntd8VZkiPd6/3sABRG0FHRlc3QgPHRlc3RAa2V5" + + "LnRlc3Q+iQEVAwUQOug8+PFWZIj3ev97AQH7NQgAo3sH+KcsPtAbyp5U02J9h3Ro" + + "aiKpAYxg3rfUVo/RH6hmCWT/AlPHLPZZC/tKiPkuIm2V3Xqyum530N0sBYxNzgNp" + + "us8mK9QurYj2omKzf1ltN+uNHR8vjB8s7jEd/CDCARu81PqNoVq2b9JRFGpGbAde" + + "7kQ/a0r2/IsJ8fz0iSpCH0geoHt3sBk9MyEem4uG0e2NzlH2wBz4H8l8BNHRHBq0" + + "6tGH4h11ZhH3FiNzJWibT2AvzLCqar2qK+6pohKSvIp8zEP7Y/iQzCvkuOfHsUOH" + + "4Utgg85k09hRDZ3pRRL/4R+Z+/1uXb+n6yKbOmpmi7U7wc9IwZxtTlGXsNIf+Q==" + ); + + private static final byte[] privv3 = Base64.decode( + "lQOgAzroPPgAAAEIANnTx/gHfag7qRMG6cVUnYZJjLcsdF6JSaVs+PUDCZ8l2+Z2" + + "V9tgxByp26bymIlq5qFFeoA5vCiKc8qzYiEVLJVVIIDjw/id2gq/TgmxoLAwiDQM" + + "TUKdCFa6pmR/uaxyrnJxfUA7+Qh0R0OjoCxNlrmyO3eiKstsJGqSUFIQq7GhcHc4" + + "nbV59zHhEWnH7DX7sDa9CgF11WxM3sjWp15iOoP1nixhmchDtQ7foUxLsCF36G/4" + + "ijcbN2NjiCDYMFburN8fXgrQzYHAIIiVFE0J+fbXNfPRmnbhQdaC8rIdiQ3tExBb" + + "N0qWhGPT9M4JOZd1yPdFMb9gbntd8VZkiPd6/3sABREDXB5zk3GNdSkH/+/447Kq" + + "hR9uM+UnZz7wDkzmt+7xbNg9F2pr/tghVCM7D0PO1YjH4DBpU1ZRO+v1t/eBB/Jd" + + "3lJYdlWYHOefJkBi44gNAafZ8ysPOJk6OGOjas/sr+JRFiX9Mgzrs2IDiejmuA98" + + "DLuSuNtzFKbE2/DDdOBEizYUjqPLlCdn5sVEt+0WKWJiAv7YonCGguWS3RKfTaYk" + + "9IE9SbI+qph9JsuyTD22GLv+gTMvwCkC1DVaHIVgzURpdnlyYyz4DBh3pAgg0nh6" + + "gpUTsjnUmrvdh+r8qj3oXH7WBMhs6qKYvU1Go5iV3S1Cu4H/Z/+s6XUFgQShevVe" + + "VCy0QtmWSFeySekEACHLJIdBDa8K4dcM2wvccz587D4PtKvMG5j71raOcgVY+r1k" + + "e6au/fa0ACqLNvn6+vFHG+Rurn8RSKV31YmTpx7J5ixTOsB+wVcwTYbrw8uNlBWc" + + "+IkqPwHrtdK95GIYQykfPW95PRudsOBdxwQW4Ax/WCst3fbjo0SZww0Os+3WBADJ" + + "/Nv0mjikXRmqJIzfuI2yxxX4Wm6vqXJkPF7LGtSMB3VEJ3qPsysoai5TYboxA8C1" + + "4rQjIoQjA+87gxZ44PUVxrxBonITCLXJ3GsvDQ2PNhS6WQ9Cf89vtYW1vLW65Nex" + + "+7AuVRepKhx6Heqdf7S03m6UYliIglrEzgEWM1XrOwP/gLMsme4h0LjLgKfd0LBk" + + "qSMdu21VSl60TMTjxav149AdutzuCVa/yPBM/zLQdlvQoGYg2IbN4+7gDHKURcSx" + + "DgOAzCcEZxdMvRk2kaOI5RRf5gV9e+ErvEMzJ/xT8xWsi+aLOhaDMbwq2LLiK2L+" + + "tXV/Z3H/Ot4u3E7H+6fHPElFYbQUdGVzdCA8dGVzdEBrZXkudGVzdD4=" + ); + public void test1() throws Exception { @@ -1220,7 +1266,7 @@ public class PGPKeyRingTest byte[] bytes = pgpSec.getEncoded(); - pgpSec = new PGPSecretKeyRing(bytes); + pgpSec = new PGPSecretKeyRing(bytes, new JcaKeyFingerprintCalculator()); Iterator it = pgpSec.getSecretKeys(); while (it.hasNext()) @@ -1380,7 +1426,7 @@ public class PGPKeyRingTest byte[] bytes = pgpSec.getEncoded(); - pgpSec = new PGPSecretKeyRing(bytes); + pgpSec = new PGPSecretKeyRing(bytes, new JcaKeyFingerprintCalculator()); Iterator it = pgpSec.getSecretKeys(); while (it.hasNext()) @@ -1501,7 +1547,7 @@ public class PGPKeyRingTest byte[] bytes = pgpSec.getEncoded(); - pgpSec = new PGPSecretKeyRing(bytes); + pgpSec = new PGPSecretKeyRing(bytes, new JcaKeyFingerprintCalculator()); Iterator it = pgpSec.getSecretKeys(); while (it.hasNext()) @@ -1547,7 +1593,7 @@ public class PGPKeyRingTest byte[] bytes = pgpSec.getEncoded(); - pgpSec = new PGPSecretKeyRing(bytes); + pgpSec = new PGPSecretKeyRing(bytes, new JcaKeyFingerprintCalculator()); Iterator it = pgpSec.getSecretKeys(); while (it.hasNext()) @@ -1653,7 +1699,7 @@ public class PGPKeyRingTest byte[] bytes = pgpSec.getEncoded(); - pgpSec = new PGPSecretKeyRing(bytes); + pgpSec = new PGPSecretKeyRing(bytes, new JcaKeyFingerprintCalculator()); Iterator it = pgpSec.getSecretKeys(); while (it.hasNext()) @@ -1830,7 +1876,7 @@ public class PGPKeyRingTest byte[] bytes = pgpSec.getEncoded(); - pgpSec = new PGPSecretKeyRing(bytes); + pgpSec = new PGPSecretKeyRing(bytes, new JcaKeyFingerprintCalculator()); Iterator it = pgpSec.getSecretKeys(); while (it.hasNext()) @@ -1876,7 +1922,7 @@ public class PGPKeyRingTest byte[] bytes = pgpSec.getEncoded(); - pgpSec = new PGPSecretKeyRing(bytes); + pgpSec = new PGPSecretKeyRing(bytes, new JcaKeyFingerprintCalculator()); Iterator it = pgpSec.getSecretKeys(); while (it.hasNext()) @@ -1907,7 +1953,7 @@ public class PGPKeyRingTest public void test10() throws Exception { - PGPSecretKeyRing secretRing = new PGPSecretKeyRing(sec10); + PGPSecretKeyRing secretRing = new PGPSecretKeyRing(sec10, new JcaKeyFingerprintCalculator()); Iterator secretKeys = secretRing.getSecretKeys(); while (secretKeys.hasNext()) @@ -2182,23 +2228,23 @@ public class PGPKeyRingTest private void rewrapTest() throws Exception { - SecureRandom rand = new SecureRandom(); - // Read the secret key rings PGPSecretKeyRingCollection privRings = new PGPSecretKeyRingCollection( new ByteArrayInputStream(rewrapKey)); + char[] newPass = "fred".toCharArray(); Iterator rIt = privRings.getKeyRings(); if (rIt.hasNext()) { - PGPSecretKeyRing pgpPriv = (PGPSecretKeyRing)rIt.next(); + PGPSecretKeyRing pgpPriv= (PGPSecretKeyRing)rIt.next(); Iterator it = pgpPriv.getSecretKeys(); while (it.hasNext()) { PGPSecretKey pgpKey = (PGPSecretKey)it.next(); + long oldKeyID = pgpKey.getKeyID(); // re-encrypt the key with an empty password pgpPriv = PGPSecretKeyRing.removeSecretKey(pgpPriv, pgpKey); @@ -2210,6 +2256,100 @@ public class PGPKeyRingTest // this should succeed PGPPrivateKey privTmp = pgpKey.extractPrivateKey(null); + + if (pgpKey.getKeyID() != oldKeyID) + { + fail("key ID mismatch"); + } + } + + it = pgpPriv.getSecretKeys(); + + while (it.hasNext()) + { + PGPSecretKey pgpKey = (PGPSecretKey)it.next(); + long oldKeyID = pgpKey.getKeyID(); + + // re-encrypt the key with an empty password + pgpPriv = PGPSecretKeyRing.removeSecretKey(pgpPriv, pgpKey); + pgpKey = PGPSecretKey.copyWithNewPassword( + pgpKey, + null, + new JcePBESecretKeyEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5).setProvider("BC").build(newPass)); + pgpPriv = PGPSecretKeyRing.insertSecretKey(pgpPriv, pgpKey); + + // this should succeed + PGPPrivateKey privTmp = pgpKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder().setProvider("BC").build()).setProvider("BC").build(newPass)); + + if (pgpKey.getKeyID() != oldKeyID) + { + fail("key ID mismatch"); + } + } + } + } + + private void rewrapTestV3() + throws Exception + { + // Read the secret key rings + PGPSecretKeyRingCollection privRings = new PGPSecretKeyRingCollection( + new ByteArrayInputStream(privv3)); + char[] newPass = "fred".toCharArray(); + + Iterator rIt = privRings.getKeyRings(); + + if (rIt.hasNext()) + { + PGPSecretKeyRing pgpPriv = (PGPSecretKeyRing)rIt.next(); + + Iterator it = pgpPriv.getSecretKeys(); + + while (it.hasNext()) + { + PGPSecretKey pgpKey = (PGPSecretKey)it.next(); + long oldKeyID = pgpKey.getKeyID(); + + // re-encrypt the key with an empty password + pgpPriv = PGPSecretKeyRing.removeSecretKey(pgpPriv, pgpKey); + + pgpKey = PGPSecretKey.copyWithNewPassword( + pgpKey, + new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder().setProvider("BC").build()).setProvider("BC").build(v3KeyPass), + null); + pgpPriv = PGPSecretKeyRing.insertSecretKey(pgpPriv, pgpKey); + + // this should succeed + PGPPrivateKey privTmp = pgpKey.extractPrivateKey(null); + + if (pgpKey.getKeyID() != oldKeyID) + { + fail("key ID mismatch"); + } + } + + it = pgpPriv.getSecretKeys(); + + while (it.hasNext()) + { + PGPSecretKey pgpKey = (PGPSecretKey)it.next(); + long oldKeyID = pgpKey.getKeyID(); + + // re-encrypt the key with an empty password + pgpPriv = PGPSecretKeyRing.removeSecretKey(pgpPriv, pgpKey); + pgpKey = PGPSecretKey.copyWithNewPassword( + pgpKey, + null, + new JcePBESecretKeyEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5, new JcaPGPDigestCalculatorProviderBuilder().setProvider("BC").build().get(HashAlgorithmTags.MD5)).setProvider("BC").build(newPass)); + pgpPriv = PGPSecretKeyRing.insertSecretKey(pgpPriv, pgpKey); + + // this should succeed + PGPPrivateKey privTmp = pgpKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder().setProvider("BC").build()).setProvider("BC").build(newPass)); + + if (pgpKey.getKeyID() != oldKeyID) + { + fail("key ID mismatch"); + } } } } @@ -2367,6 +2507,7 @@ public class PGPKeyRingTest generateTest(); generateSha1Test(); rewrapTest(); + rewrapTestV3(); testPublicKeyRingWithX509(); testSecretKeyRingWithPersonalCertificate(); insertMasterTest(); diff --git a/src/test/java/org/bouncycastle/openpgp/test/PGPNoPrivateKeyTest.java b/src/test/java/org/bouncycastle/openpgp/test/PGPNoPrivateKeyTest.java new file mode 100644 index 00000000..be69d111 --- /dev/null +++ b/src/test/java/org/bouncycastle/openpgp/test/PGPNoPrivateKeyTest.java @@ -0,0 +1,167 @@ +package org.bouncycastle.openpgp.test; + +import java.security.Security; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPSecretKey; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; +import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; +import org.bouncycastle.util.encoders.Base64; +import org.bouncycastle.util.test.SimpleTest; + +public class PGPNoPrivateKeyTest + extends SimpleTest +{ + String pgpOldPass = "test"; + String pgpNewPass = "newtest"; + String BOUNCY_CASTLE_PROVIDER_NAME = "BC"; + + byte[] pgpPrivateEmpty = Base64.decode( + "lQCVBFGSNGwBBACwABZRIEW/4vDQajcO0FW39yNDcsHBDwPkGT95D7jiVTTRoSs6" + + "ACWRAAwGlz4V62U0+nEgasxpifHnu6jati5zxwS16qNvBcxcqZrdZWdvolzCWWsr" + + "pFd0juhwesrvvUb5dN/xCJKyLPkp6A+uwv35/cxVSOHFvbW7nnronwinYQARAQAB" + + "/gJlAkdOVQG0HlRlc3QgVGVzdGVyc29uIDx0ZXN0QHRlc3QubmV0Poi4BBMBAgAi" + + "BQJRkjRsAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRDSr6Hh9tuk5NfI" + + "A/4iMPF9k2/7KanWksNrBqhKemsyI7hLTxAwv+AA9B0rOO2QoJYe9OjuKn199fNO" + + "JPsAgwy7okvDe3QAUz3WA9GlghM5STYvonFJtl7o4kyjcZ4HO2ZI5Bdc5O9i63QA" + + "rNv40qVp++A3Mf+13z7cftKufj0vOfw6YeayLVXcV4h95J0B/gRRkjSNAQQA2l3d" + + "ZnFFYXYDoNHz1cOX4787CbKdBIfiALFfdbyQ6TzYkCTJTnVCZlQs2aeyrcdTSZUx" + + "N4y9bih4nfJ8uRKyQvLm6O0u6bG16kUDDnnwlsGn3uvTXfUwnSPq8pFY2acde6ZG" + + "N25vezNK1R6C7kU3+puNHqBIRANfHTsBElaD2V0AEQEAAf4CAwIUI0+QlwBVFdNa" + + "S/ppOwSht7Gr19AK4SHe92VWDKnCBPN2W3vhM4NcZSQCV2oiEMI0akLZ26jqCiRl" + + "AvTjLSVDho1rUWbaSxFfKlDQNbxCJKlMQeVfbsWXJMeDkn1AhPru3PBLl6Y1jocd" + + "vIVM7aQugNQlwEuFWgtZeODxcgBfX2lQeEMIv0AtWTAMt6MVT8AgnFqiqC4+14t0" + + "j2CHP2hqCDr5zw9gerAYQ0F03OS34vDm4Y5DmQFjyB05QO2cIN4DZ9gJg8NAQT+P" + + "+bwWR3/i9pTq3InNkoi2uT41OnHsYWgKoEQn62BDxjbvO359crUiq9VvS52v2UXh" + + "b6Z+fF3PoXXsobS1QQwTPXAeA/mlAflTp+HrkckatY7DgWbON1SSn4Z1XcWPKBSY" + + "epS5+90Tj3byZvN7Laj61ZlXVBvU3x7z6MaBZDf4479fklcUnJ13v+P6uGnTI4YE" + + "Q5pPjHn1dDqD2Nl8ZW9ufK9pPYkBPQQYAQIACQUCUZI0jQIbAgCoCRDSr6Hh9tuk" + + "5J0gBBkBAgAGBQJRkjSNAAoJEPIU7wJ5Ws2K0F0D/jHx4jrZq7SCv69/4hictjgz" + + "nNNFSOm20/brHXMBdp6p9mBqt28WU8fgRkxS0mz+1i7VNTv6ZwUXawfTyOVCPR5B" + + "QEC+FA+LvdX0UcJBJpa9tT4koz1JBxmppxxLYdS2A5sslPD5If8QHUaOMEX9O1I+" + + "So3rEh3+DuhQj88FUuG8uJAD/3Xtpf/5nEpghLOZdQ/7QkLCoRZk7fwjChQNFSJU" + + "5xiZbZ/GsSvU1IqAP/NZBmBO0qDm5m7ahXy71O1bMFtaiUaw2Mb7dwqqDvppbjIB" + + "OHdIhSnAorRLcnjm8z51QVMzHmgvKt5/e1q1fzsVzza6DWtYr2X/1VsuouSC1uz1" + + "nPdgnQH+BFGSNJ4BBAC3KliQlchs0rctsXbhA/GEfiO0s9tAgVsfJL1PWUkC+26M" + + "yBbqkVg5RV+J6dyTSeT6cDI8PMu8XFPO6H2WWdovfs7X9K1lxfnNWxQB2L6t2xre" + + "XyFqvTsYEFuGvYmbNyUYvA+daHD0xqX8UrC0J6TYg5ie5I685X8gFKVEtGYG/wAR" + + "AQAB/gIDAuMt34hcdJPX03uBj9LtjcnrMNLyF7PVJv4wBXEt7T9Kp8cYZ80Sxpd2" + + "11LHzjgiPg1kkkImJ9Ie1qbPZjc9tyiGf47m0TIORnKtwNb2YN+sKLpqZ+ienfTs" + + "vc0uyuVGW+8PCt409M9R++0q66sxvb3oKBp2zsr3BbGaISs4OVxY2L8uU3t5j9pi" + + "qKdV2XTiV9OZJ+2f1au1tMwhNPzjVJ4GH53TxewSkshRJTZtw2ouUJkdA/bizfNO" + + "9XYYvV8sW1/ASe1dnOs+ANDGzumzSA00dWPSveURroG+ZtVXVgkakJJtDwdAYutP" + + "kSm28cnsl1OmrBKPonB5N3uDjTlq56vji1d2F5ugAXTTD5PptiML1wEB/TqsRJRX" + + "uY7DLy+8iukOVOyoVw63UMX27YUz61JJZYcB7U28gNeRyBsnTEbjmvteoFsYnaGg" + + "Owgc+1Zx4rQdZEqxZRmfwmiUgHGyI9OpvoVaTIuDIqDd2ZRWiJ8EGAECAAkFAlGS" + + "NJ4CGwwACgkQ0q+h4fbbpOScsgQAmMymSfAmltnHQzKr5k2GvlAqIzl9MqKVm9wA" + + "0Cx3grwzPaiqmfspPIueQ8Phexiy6dwfPrwNoKnJOEjM6/sOcWEmLiIoYi+/oQjU" + + "12zwogOfzT/1hPpG5zs+GBGX4sorCK663PuovwCEoNrWm+7nItfTwdnFavNuj7s4" + + "+b3JLdM="); + + byte[] pgpPrivateFull = Base64.decode( + "lQH+BFGSNGwBBACwABZRIEW/4vDQajcO0FW39yNDcsHBDwPkGT95D7jiVTTRoSs6" + + "ACWRAAwGlz4V62U0+nEgasxpifHnu6jati5zxwS16qNvBcxcqZrdZWdvolzCWWsr" + + "pFd0juhwesrvvUb5dN/xCJKyLPkp6A+uwv35/cxVSOHFvbW7nnronwinYQARAQAB" + + "/gIDAuqTuDp/Chfq0TKnSxmm2ZpDuiHD+NFVnCyNuJpvCQk0PnVwmGMH4xvsAZB2" + + "TOrfh2XHf/n9J4vjxB6p6Zs1kGBgg9hcHoWf+oEf1Tz/PE/c1tUXG2Hz9wlAgstU" + + "my2NpDTYUjQs45p+LaM+WFtLNXzBeqELKlMevs8Xb7n+VHwiTuM3KfXETLCoLz0Q" + + "3GmmpOuNnvXBdza7RsDwke0r66HzwX4Le8cMH9Pe7kSMakx9S1UR/uIsxsZYZOKb" + + "BieGEumxiAnew0Ri5/8wTd5yYC7BWbYvBUgdMQ1gzkzmJcVky8NVfoZKQ0GkdvMo" + + "fMThIVXN1U6+aqzAuUMFCPYQ7fEpfoNLhCnzQPv3RE7Wo2vFMjWBod2J4MSLhBuq" + + "Ut+FYLqYqU21Qe4PEyPmGnkVu7Wd8FGjBF+IKZg+ycPi++h/twloD/h7LEaq907C" + + "4R3rdOzjZnefDfxVWjLLhqKSSuXxtjSSKwMNdbjYVVJ/tB5UZXN0IFRlc3RlcnNv" + + "biA8dGVzdEB0ZXN0Lm5ldD6IuAQTAQIAIgUCUZI0bAIbAwYLCQgHAwIGFQgCCQoL" + + "BBYCAwECHgECF4AACgkQ0q+h4fbbpOTXyAP+IjDxfZNv+ymp1pLDawaoSnprMiO4" + + "S08QML/gAPQdKzjtkKCWHvTo7ip9ffXzTiT7AIMMu6JLw3t0AFM91gPRpYITOUk2" + + "L6JxSbZe6OJMo3GeBztmSOQXXOTvYut0AKzb+NKlafvgNzH/td8+3H7Srn49Lzn8" + + "OmHmsi1V3FeIfeSdAf4EUZI0jQEEANpd3WZxRWF2A6DR89XDl+O/OwmynQSH4gCx" + + "X3W8kOk82JAkyU51QmZULNmnsq3HU0mVMTeMvW4oeJ3yfLkSskLy5ujtLumxtepF" + + "Aw558JbBp97r0131MJ0j6vKRWNmnHXumRjdub3szStUegu5FN/qbjR6gSEQDXx07" + + "ARJWg9ldABEBAAH+AgMCFCNPkJcAVRXTWkv6aTsEobexq9fQCuEh3vdlVgypwgTz" + + "dlt74TODXGUkAldqIhDCNGpC2duo6gokZQL04y0lQ4aNa1Fm2ksRXypQ0DW8QiSp" + + "TEHlX27FlyTHg5J9QIT67tzwS5emNY6HHbyFTO2kLoDUJcBLhVoLWXjg8XIAX19p" + + "UHhDCL9ALVkwDLejFU/AIJxaoqguPteLdI9ghz9oagg6+c8PYHqwGENBdNzkt+Lw" + + "5uGOQ5kBY8gdOUDtnCDeA2fYCYPDQEE/j/m8Fkd/4vaU6tyJzZKItrk+NTpx7GFo" + + "CqBEJ+tgQ8Y27zt+fXK1IqvVb0udr9lF4W+mfnxdz6F17KG0tUEMEz1wHgP5pQH5" + + "U6fh65HJGrWOw4FmzjdUkp+GdV3FjygUmHqUufvdE4928mbzey2o+tWZV1Qb1N8e" + + "8+jGgWQ3+OO/X5JXFJydd7/j+rhp0yOGBEOaT4x59XQ6g9jZfGVvbnyvaT2JAT0E" + + "GAECAAkFAlGSNI0CGwIAqAkQ0q+h4fbbpOSdIAQZAQIABgUCUZI0jQAKCRDyFO8C" + + "eVrNitBdA/4x8eI62au0gr+vf+IYnLY4M5zTRUjpttP26x1zAXaeqfZgardvFlPH" + + "4EZMUtJs/tYu1TU7+mcFF2sH08jlQj0eQUBAvhQPi73V9FHCQSaWvbU+JKM9SQcZ" + + "qaccS2HUtgObLJTw+SH/EB1GjjBF/TtSPkqN6xId/g7oUI/PBVLhvLiQA/917aX/" + + "+ZxKYISzmXUP+0JCwqEWZO38IwoUDRUiVOcYmW2fxrEr1NSKgD/zWQZgTtKg5uZu" + + "2oV8u9TtWzBbWolGsNjG+3cKqg76aW4yATh3SIUpwKK0S3J45vM+dUFTMx5oLyre" + + "f3tatX87Fc82ug1rWK9l/9VbLqLkgtbs9Zz3YJ0B/gRRkjSeAQQAtypYkJXIbNK3" + + "LbF24QPxhH4jtLPbQIFbHyS9T1lJAvtujMgW6pFYOUVfienck0nk+nAyPDzLvFxT" + + "zuh9llnaL37O1/StZcX5zVsUAdi+rdsa3l8har07GBBbhr2JmzclGLwPnWhw9Mal" + + "/FKwtCek2IOYnuSOvOV/IBSlRLRmBv8AEQEAAf4CAwLjLd+IXHST19N7gY/S7Y3J" + + "6zDS8hez1Sb+MAVxLe0/SqfHGGfNEsaXdtdSx844Ij4NZJJCJifSHtamz2Y3Pbco" + + "hn+O5tEyDkZyrcDW9mDfrCi6amfonp307L3NLsrlRlvvDwreNPTPUfvtKuurMb29" + + "6Cgads7K9wWxmiErODlcWNi/LlN7eY/aYqinVdl04lfTmSftn9WrtbTMITT841Se" + + "Bh+d08XsEpLIUSU2bcNqLlCZHQP24s3zTvV2GL1fLFtfwEntXZzrPgDQxs7ps0gN" + + "NHVj0r3lEa6BvmbVV1YJGpCSbQ8HQGLrT5EptvHJ7JdTpqwSj6JweTd7g405auer" + + "44tXdheboAF00w+T6bYjC9cBAf06rESUV7mOwy8vvIrpDlTsqFcOt1DF9u2FM+tS" + + "SWWHAe1NvIDXkcgbJ0xG45r7XqBbGJ2hoDsIHPtWceK0HWRKsWUZn8JolIBxsiPT" + + "qb6FWkyLgyKg3dmUVoifBBgBAgAJBQJRkjSeAhsMAAoJENKvoeH226TknLIEAJjM" + + "pknwJpbZx0Myq+ZNhr5QKiM5fTKilZvcANAsd4K8Mz2oqpn7KTyLnkPD4XsYsunc" + + "Hz68DaCpyThIzOv7DnFhJi4iKGIvv6EI1Nds8KIDn80/9YT6Ruc7PhgRl+LKKwiu" + + "utz7qL8AhKDa1pvu5yLX08HZxWrzbo+7OPm9yS3T"); + + public void performTest() + throws Exception + { + PGPSecretKeyRing pgpSecRing = new PGPSecretKeyRing(pgpPrivateFull, new JcaKeyFingerprintCalculator()); + PGPSecretKey pgpSecKey = pgpSecRing.getSecretKey(); + boolean isFullEmpty = pgpSecKey.isPrivateKeyEmpty(); + + pgpSecRing = new PGPSecretKeyRing(pgpPrivateEmpty, new JcaKeyFingerprintCalculator()); + pgpSecKey = pgpSecRing.getSecretKey(); + boolean isEmptyEmpty = pgpSecKey.isPrivateKeyEmpty(); + + // + // Check isPrivateKeyEmpty() is public + // + + if (isFullEmpty || !isEmptyEmpty) + { + fail("Empty private keys not detected correctly."); + } + + // + // Check copyWithNewPassword doesn't throw an exception for secret + // keys without private keys (PGPException: unknown S2K type: 101). + // + + try + { + PGPSecretKey pgpChangedKey = PGPSecretKey.copyWithNewPassword(pgpSecKey, + new JcePBESecretKeyDecryptorBuilder( + new JcaPGPDigestCalculatorProviderBuilder().setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build()).setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(pgpOldPass.toCharArray()), + new JcePBESecretKeyEncryptorBuilder(pgpSecKey.getKeyEncryptionAlgorithm()).build(pgpNewPass.toCharArray())); + } + catch (PGPException e) + { + if (!e.getMessage().equals("no private key in this SecretKey - public key present only.")) + { + fail("wrong exception."); + } + } + } + + public String getName() + { + return "PGPNoPrivateKeyTest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new PGPNoPrivateKeyTest()); + } +} diff --git a/src/test/java/org/bouncycastle/openpgp/test/PGPRSATest.java b/src/test/java/org/bouncycastle/openpgp/test/PGPRSATest.java index 9b4a753f..a61681c6 100644 --- a/src/test/java/org/bouncycastle/openpgp/test/PGPRSATest.java +++ b/src/test/java/org/bouncycastle/openpgp/test/PGPRSATest.java @@ -550,8 +550,8 @@ public class PGPRSATest private void embeddedJpegTest() throws Exception { - PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(testPubKey); - PGPSecretKeyRing pgpSec = new PGPSecretKeyRing(testPrivKey); + PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(testPubKey, new JcaKeyFingerprintCalculator()); + PGPSecretKeyRing pgpSec = new PGPSecretKeyRing(testPrivKey, new JcaKeyFingerprintCalculator()); PGPPublicKey pubKey = pgpPub.getPublicKey(); @@ -887,7 +887,7 @@ public class PGPRSATest if (!noIDEA()) { - PGPSecretKeyRing pgpPriv = new PGPSecretKeyRing(testPrivKeyV3); + PGPSecretKeyRing pgpPriv = new PGPSecretKeyRing(testPrivKeyV3, new JcaKeyFingerprintCalculator()); PGPPrivateKey pgpPrivKey = pgpPriv.getSecretKey().extractPrivateKey(passP, "BC"); // @@ -907,7 +907,7 @@ public class PGPRSATest // // Read the private key // - PGPSecretKeyRing pgpPriv = new PGPSecretKeyRing(testPrivKey); + PGPSecretKeyRing pgpPriv = new PGPSecretKeyRing(testPrivKey, new JcaKeyFingerprintCalculator()); PGPPrivateKey pgpPrivKey = pgpPriv.getSecretKey().extractPrivateKey(pass, "BC"); // @@ -979,7 +979,7 @@ public class PGPRSATest // // encrypted message - read subkey // - pgpPriv = new PGPSecretKeyRing(subKey); + pgpPriv = new PGPSecretKeyRing(subKey, new JcaKeyFingerprintCalculator()); // // encrypted message diff --git a/src/test/java/org/bouncycastle/openpgp/test/RegressionTest.java b/src/test/java/org/bouncycastle/openpgp/test/RegressionTest.java index 1a01b531..2810cb96 100644 --- a/src/test/java/org/bouncycastle/openpgp/test/RegressionTest.java +++ b/src/test/java/org/bouncycastle/openpgp/test/RegressionTest.java @@ -23,7 +23,8 @@ public class RegressionTest new PGPArmoredTest(), new PGPSignatureTest(), new PGPClearSignedSignatureTest(), - new PGPCompressionTest() + new PGPCompressionTest(), + new PGPNoPrivateKeyTest() }; public static void main( |