diff options
author | David Hook <dgh@cryptoworkshop.com> | 2014-07-22 01:11:01 +0400 |
---|---|---|
committer | David Hook <dgh@cryptoworkshop.com> | 2014-07-22 01:11:01 +0400 |
commit | 970d7ad33269e99ed95d026446dbb60a13b9a290 (patch) | |
tree | 17500ff7346e392dd6c157d0c32a27feb0d0f9ff /core | |
parent | 410abad648137ef6dc3186547c411ef76ae8bbd0 (diff) |
J2ME compatibility updates
Diffstat (limited to 'core')
6 files changed, 940 insertions, 568 deletions
diff --git a/core/src/main/j2me/org/bouncycastle/asn1/ASN1GeneralizedTime.java b/core/src/main/j2me/org/bouncycastle/asn1/ASN1GeneralizedTime.java index ea2cb3f2..87058121 100644 --- a/core/src/main/j2me/org/bouncycastle/asn1/ASN1GeneralizedTime.java +++ b/core/src/main/j2me/org/bouncycastle/asn1/ASN1GeneralizedTime.java @@ -1,27 +1,260 @@ package org.bouncycastle.asn1; +import java.io.IOException; import java.util.Date; +import java.util.TimeZone; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Strings; + +/** + * Generalized time object. + */ public class ASN1GeneralizedTime - extends DERGeneralizedTime + extends ASN1Primitive { - ASN1GeneralizedTime(byte[] bytes) + private byte[] time; + + /** + * return a generalized time from the passed in object + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static ASN1GeneralizedTime getInstance( + Object obj) + { + if (obj == null || obj instanceof ASN1GeneralizedTime) + { + return (ASN1GeneralizedTime)obj; + } + + if (obj instanceof ASN1GeneralizedTime) + { + return new ASN1GeneralizedTime(((ASN1GeneralizedTime)obj).time); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a Generalized Time object 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 ASN1GeneralizedTime getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + ASN1Primitive o = obj.getObject(); + + if (explicit || o instanceof ASN1GeneralizedTime) + { + return getInstance(o); + } + else + { + return new ASN1GeneralizedTime(((ASN1OctetString)o).getOctets()); + } + } + + /** + * The correct format for this is YYYYMMDDHHMMSS[.f]Z, or without the Z + * for local time, or Z|[+|-]HHMM on the end, for difference between local + * time and UTC time. The fractional second amount f must consist of at + * least one number with trailing zeroes removed. + * + * @param time the time string. + * @exception IllegalArgumentException if String is an illegal format. + */ + public ASN1GeneralizedTime( + String time) + { + char last = time.charAt(time.length() - 1); + if (last != 'Z' && !(last >= 0 && last <= '9')) + { + if (time.indexOf('-') < 0 && time.indexOf('+') < 0) + { + throw new IllegalArgumentException("time needs to be in format YYYYMMDDHHMMSS[.f]Z or YYYYMMDDHHMMSS[.f][+-]HHMM"); + } + } + + this.time = Strings.toByteArray(time); + } + + /** + * base constructer from a java.util.date object + */ + public ASN1GeneralizedTime( + Date time) + { + this.time = Strings.toByteArray(DateFormatter.getGeneralizedTimeDateString(time, false)); + } + + protected ASN1GeneralizedTime(Date date, boolean includeMillis) { - super(bytes); + this.time = Strings.toByteArray(DateFormatter.getGeneralizedTimeDateString(date, true)); } - public ASN1GeneralizedTime(Date date) + ASN1GeneralizedTime( + byte[] bytes) { - super(date); + this.time = bytes; } - public ASN1GeneralizedTime(Date date, boolean includeMillis) + /** + * Return the time. + * @return The time string as it appeared in the encoded object. + */ + public String getTimeString() + { + return Strings.fromByteArray(time); + } + + /** + * return the time - always in the form of + * YYYYMMDDhhmmssGMT(+hh:mm|-hh:mm). + * <p> + * Normally in a certificate we would expect "Z" rather than "GMT", + * however adding the "GMT" means we can just use: + * <pre> + * dateF = new SimpleDateFormat("yyyyMMddHHmmssz"); + * </pre> + * To read in the time and get a date which is compatible with our local + * time zone. + */ + public String getTime() { - super(date, includeMillis); + String stime = Strings.fromByteArray(time); + + // + // standardise the format. + // + if (stime.charAt(stime.length() - 1) == 'Z') + { + return stime.substring(0, stime.length() - 1) + "GMT+00:00"; + } + else + { + int signPos = stime.length() - 5; + char sign = stime.charAt(signPos); + if (sign == '-' || sign == '+') + { + return stime.substring(0, signPos) + + "GMT" + + stime.substring(signPos, signPos + 3) + + ":" + + stime.substring(signPos + 3); + } + else + { + signPos = stime.length() - 3; + sign = stime.charAt(signPos); + if (sign == '-' || sign == '+') + { + return stime.substring(0, signPos) + + "GMT" + + stime.substring(signPos) + + ":00"; + } + } + } + return stime + calculateGMTOffset(); } - public ASN1GeneralizedTime(String time) + private String calculateGMTOffset() + { + String sign = "+"; + TimeZone timeZone = TimeZone.getDefault(); + int offset = timeZone.getRawOffset(); + if (offset < 0) + { + sign = "-"; + offset = -offset; + } + int hours = offset / (60 * 60 * 1000); + int minutes = (offset - (hours * 60 * 60 * 1000)) / (60 * 1000); + +// try +// { +// if (timeZone.useDaylightTime() && timeZone.inDaylightTime(this.getDate())) +// { +// hours += sign.equals("+") ? 1 : -1; +// } +// } +// catch (ParseException e) +// { +// // we'll do our best and ignore daylight savings +// } + + return "GMT" + sign + convert(hours) + ":" + convert(minutes); + } + + private String convert(int time) + { + if (time < 10) + { + return "0" + time; + } + + return Integer.toString(time); + } + + public Date getDate() + { + return DateFormatter.fromGeneralizedTimeString(time); + } + + private boolean hasFractionalSeconds() + { + for (int i = 0; i != time.length; i++) + { + if (time[i] == '.') + { + if (i == 14) + { + return true; + } + } + } + return false; + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + { + int length = time.length; + + return 1 + StreamUtil.calculateBodyLength(length) + length; + } + + void encode( + ASN1OutputStream out) + throws IOException + { + out.writeEncoded(BERTags.GENERALIZED_TIME, time); + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof ASN1GeneralizedTime)) + { + return false; + } + + return Arrays.areEqual(time, ((ASN1GeneralizedTime)o).time); + } + + public int hashCode() { - super(time); + return Arrays.hashCode(time); } } diff --git a/core/src/main/j2me/org/bouncycastle/asn1/ASN1UTCTime.java b/core/src/main/j2me/org/bouncycastle/asn1/ASN1UTCTime.java index aac76e10..0d18c6af 100644 --- a/core/src/main/j2me/org/bouncycastle/asn1/ASN1UTCTime.java +++ b/core/src/main/j2me/org/bouncycastle/asn1/ASN1UTCTime.java @@ -1,22 +1,259 @@ package org.bouncycastle.asn1; +import java.io.IOException; import java.util.Date; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Strings; + +/** + * UTC time object. + */ public class ASN1UTCTime - extends DERUTCTime + extends ASN1Primitive { - ASN1UTCTime(byte[] bytes) + private byte[] time; + + /** + * return an UTC Time from the passed in object. + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static ASN1UTCTime getInstance( + Object obj) + { + if (obj == null || obj instanceof ASN1UTCTime) + { + return (ASN1UTCTime)obj; + } + + if (obj instanceof ASN1UTCTime) + { + return new ASN1UTCTime(((ASN1UTCTime)obj).time); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an UTC Time 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 ASN1UTCTime getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + ASN1Object o = obj.getObject(); + + if (explicit || o instanceof ASN1UTCTime) + { + return getInstance(o); + } + else + { + return new ASN1UTCTime(((ASN1OctetString)o).getOctets()); + } + } + + /** + * The correct format for this is YYMMDDHHMMSSZ (it used to be that seconds were + * never encoded. When you're creating one of these objects from scratch, that's + * what you want to use, otherwise we'll try to deal with whatever gets read from + * the input stream... (this is why the input format is different from the getTime() + * method output). + * <p> + * + * @param time the time string. + */ + public ASN1UTCTime( + String time) + { + if (time.charAt(time.length() - 1) != 'Z') + { + // we accept this as a variation + if (time.indexOf('-') < 0 && time.indexOf('+') < 0) + { + throw new IllegalArgumentException("time needs to be in format YYMMDDHHMMSSZ"); + } + } + + this.time = Strings.toByteArray(time); + } + + /** + * base constructor from a java.util.date object + */ + public ASN1UTCTime( + Date time) + { + this.time = Strings.toByteArray(DateFormatter.toUTCDateString(time)); + } + + ASN1UTCTime( + byte[] time) + { + this.time = time; + } + + /** + * return the time as a date based on whatever a 2 digit year will return. For + * standardised processing use getAdjustedDate(). + * + * @return the resulting date + */ + public Date getDate() { - super(bytes); + return DateFormatter.adjustedFromUTCDateString(time); } - public ASN1UTCTime(Date date) + /** + * return the time as an adjusted date + * in the range of 1950 - 2049. + * + * @return a date in the range of 1950 to 2049. + */ + public Date getAdjustedDate() + { + return DateFormatter.adjustedFromUTCDateString(time); + } + + /** + * return the time - always in the form of + * YYMMDDhhmmssGMT(+hh:mm|-hh:mm). + * <p> + * Normally in a certificate we would expect "Z" rather than "GMT", + * however adding the "GMT" means we can just use: + * <pre> + * dateF = new SimpleDateFormat("yyMMddHHmmssz"); + * </pre> + * To read in the time and get a date which is compatible with our local + * time zone. + * <p> + * <b>Note:</b> In some cases, due to the local date processing, this + * may lead to unexpected results. If you want to stick the normal + * convention of 1950 to 2049 use the getAdjustedTime() method. + */ + public String getTime() + { + String stime = Strings.fromByteArray(time); + + // + // standardise the format. + // + if (stime.indexOf('-') < 0 && stime.indexOf('+') < 0) + { + if (stime.length() == 11) + { + return stime.substring(0, 10) + "00GMT+00:00"; + } + else + { + return stime.substring(0, 12) + "GMT+00:00"; + } + } + else + { + int index = stime.indexOf('-'); + if (index < 0) + { + index = stime.indexOf('+'); + } + String d = stime; + + if (index == stime.length() - 3) + { + d += "00"; + } + + if (index == 10) + { + return d.substring(0, 10) + "00GMT" + d.substring(10, 13) + ":" + d.substring(13, 15); + } + else + { + return d.substring(0, 12) + "GMT" + d.substring(12, 15) + ":" + d.substring(15, 17); + } + } + } + + /** + * return a time string as an adjusted date with a 4 digit year. This goes + * in the range of 1950 - 2049. + */ + public String getAdjustedTime() + { + String d = this.getTime(); + + if (d.charAt(0) < '5') + { + return "20" + d; + } + else + { + return "19" + d; + } + } + + /** + * Return the time. + * @return The time string as it appeared in the encoded object. + */ + public String getTimeString() + { + return Strings.fromByteArray(time); + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + { + int length = time.length; + + return 1 + StreamUtil.calculateBodyLength(length) + length; + } + + void encode( + ASN1OutputStream out) + throws IOException + { + out.write(BERTags.UTC_TIME); + + int length = time.length; + + out.writeLength(length); + + for (int i = 0; i != length; i++) + { + out.write((byte)time[i]); + } + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof ASN1UTCTime)) + { + return false; + } + + return Arrays.areEqual(time, ((ASN1UTCTime)o).time); + } + + public int hashCode() { - super(date); + return Arrays.hashCode(time); } - public ASN1UTCTime(String time) + public String toString() { - super(time); + return Strings.fromByteArray(time); } } diff --git a/core/src/main/j2me/org/bouncycastle/asn1/DERGeneralizedTime.java b/core/src/main/j2me/org/bouncycastle/asn1/DERGeneralizedTime.java index 2cb95b60..6162da40 100644 --- a/core/src/main/j2me/org/bouncycastle/asn1/DERGeneralizedTime.java +++ b/core/src/main/j2me/org/bouncycastle/asn1/DERGeneralizedTime.java @@ -1,260 +1,27 @@ package org.bouncycastle.asn1; -import java.io.IOException; import java.util.Date; -import java.util.TimeZone; -import org.bouncycastle.util.Arrays; -import org.bouncycastle.util.Strings; - -/** - * Generalized time object. - */ public class DERGeneralizedTime - extends ASN1Primitive + extends ASN1GeneralizedTime { - private byte[] time; - - /** - * return a generalized time from the passed in object - * - * @exception IllegalArgumentException if the object cannot be converted. - */ - public static ASN1GeneralizedTime getInstance( - Object obj) - { - if (obj == null || obj instanceof ASN1GeneralizedTime) - { - return (ASN1GeneralizedTime)obj; - } - - if (obj instanceof DERGeneralizedTime) - { - return new ASN1GeneralizedTime(((DERGeneralizedTime)obj).time); - } - - throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); - } - - /** - * return a Generalized Time object 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 ASN1GeneralizedTime getInstance( - ASN1TaggedObject obj, - boolean explicit) - { - ASN1Primitive o = obj.getObject(); - - if (explicit || o instanceof DERGeneralizedTime) - { - return getInstance(o); - } - else - { - return new ASN1GeneralizedTime(((ASN1OctetString)o).getOctets()); - } - } - - /** - * The correct format for this is YYYYMMDDHHMMSS[.f]Z, or without the Z - * for local time, or Z|[+|-]HHMM on the end, for difference between local - * time and UTC time. The fractional second amount f must consist of at - * least one number with trailing zeroes removed. - * - * @param time the time string. - * @exception IllegalArgumentException if String is an illegal format. - */ - public DERGeneralizedTime( - String time) - { - char last = time.charAt(time.length() - 1); - if (last != 'Z' && !(last >= 0 && last <= '9')) - { - if (time.indexOf('-') < 0 && time.indexOf('+') < 0) - { - throw new IllegalArgumentException("time needs to be in format YYYYMMDDHHMMSS[.f]Z or YYYYMMDDHHMMSS[.f][+-]HHMM"); - } - } - - this.time = Strings.toByteArray(time); - } - - /** - * base constructer from a java.util.date object - */ - public DERGeneralizedTime( - Date time) - { - this.time = Strings.toByteArray(DateFormatter.getGeneralizedTimeDateString(time, false)); - } - - protected DERGeneralizedTime(Date date, boolean includeMillis) + DERGeneralizedTime(byte[] bytes) { - this.time = Strings.toByteArray(DateFormatter.getGeneralizedTimeDateString(date, true)); + super(bytes); } - DERGeneralizedTime( - byte[] bytes) + public DERGeneralizedTime(Date date) { - this.time = bytes; + super(date); } - /** - * Return the time. - * @return The time string as it appeared in the encoded object. - */ - public String getTimeString() - { - return Strings.fromByteArray(time); - } - - /** - * return the time - always in the form of - * YYYYMMDDhhmmssGMT(+hh:mm|-hh:mm). - * <p> - * Normally in a certificate we would expect "Z" rather than "GMT", - * however adding the "GMT" means we can just use: - * <pre> - * dateF = new SimpleDateFormat("yyyyMMddHHmmssz"); - * </pre> - * To read in the time and get a date which is compatible with our local - * time zone. - */ - public String getTime() + public DERGeneralizedTime(Date date, boolean includeMillis) { - String stime = Strings.fromByteArray(time); - - // - // standardise the format. - // - if (stime.charAt(stime.length() - 1) == 'Z') - { - return stime.substring(0, stime.length() - 1) + "GMT+00:00"; - } - else - { - int signPos = stime.length() - 5; - char sign = stime.charAt(signPos); - if (sign == '-' || sign == '+') - { - return stime.substring(0, signPos) - + "GMT" - + stime.substring(signPos, signPos + 3) - + ":" - + stime.substring(signPos + 3); - } - else - { - signPos = stime.length() - 3; - sign = stime.charAt(signPos); - if (sign == '-' || sign == '+') - { - return stime.substring(0, signPos) - + "GMT" - + stime.substring(signPos) - + ":00"; - } - } - } - return stime + calculateGMTOffset(); + super(date, includeMillis); } - private String calculateGMTOffset() - { - String sign = "+"; - TimeZone timeZone = TimeZone.getDefault(); - int offset = timeZone.getRawOffset(); - if (offset < 0) - { - sign = "-"; - offset = -offset; - } - int hours = offset / (60 * 60 * 1000); - int minutes = (offset - (hours * 60 * 60 * 1000)) / (60 * 1000); - -// try -// { -// if (timeZone.useDaylightTime() && timeZone.inDaylightTime(this.getDate())) -// { -// hours += sign.equals("+") ? 1 : -1; -// } -// } -// catch (ParseException e) -// { -// // we'll do our best and ignore daylight savings -// } - - return "GMT" + sign + convert(hours) + ":" + convert(minutes); - } - - private String convert(int time) - { - if (time < 10) - { - return "0" + time; - } - - return Integer.toString(time); - } - - public Date getDate() - { - return DateFormatter.fromGeneralizedTimeString(time); - } - - private boolean hasFractionalSeconds() - { - for (int i = 0; i != time.length; i++) - { - if (time[i] == '.') - { - if (i == 14) - { - return true; - } - } - } - return false; - } - - boolean isConstructed() - { - return false; - } - - int encodedLength() - { - int length = time.length; - - return 1 + StreamUtil.calculateBodyLength(length) + length; - } - - void encode( - ASN1OutputStream out) - throws IOException - { - out.writeEncoded(BERTags.GENERALIZED_TIME, time); - } - - boolean asn1Equals( - ASN1Primitive o) - { - if (!(o instanceof DERGeneralizedTime)) - { - return false; - } - - return Arrays.areEqual(time, ((DERGeneralizedTime)o).time); - } - - public int hashCode() + public DERGeneralizedTime(String time) { - return Arrays.hashCode(time); + super(time); } } diff --git a/core/src/main/j2me/org/bouncycastle/asn1/DERUTCTime.java b/core/src/main/j2me/org/bouncycastle/asn1/DERUTCTime.java index 3e8010b4..621febff 100644 --- a/core/src/main/j2me/org/bouncycastle/asn1/DERUTCTime.java +++ b/core/src/main/j2me/org/bouncycastle/asn1/DERUTCTime.java @@ -1,259 +1,22 @@ package org.bouncycastle.asn1; -import java.io.IOException; import java.util.Date; -import org.bouncycastle.util.Arrays; -import org.bouncycastle.util.Strings; - -/** - * UTC time object. - */ public class DERUTCTime - extends ASN1Primitive + extends ASN1UTCTime { - private byte[] time; - - /** - * return an UTC Time from the passed in object. - * - * @exception IllegalArgumentException if the object cannot be converted. - */ - public static ASN1UTCTime getInstance( - Object obj) - { - if (obj == null || obj instanceof ASN1UTCTime) - { - return (ASN1UTCTime)obj; - } - - if (obj instanceof DERUTCTime) - { - return new ASN1UTCTime(((DERUTCTime)obj).time); - } - - throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); - } - - /** - * return an UTC Time 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 ASN1UTCTime getInstance( - ASN1TaggedObject obj, - boolean explicit) - { - ASN1Object o = obj.getObject(); - - if (explicit || o instanceof ASN1UTCTime) - { - return getInstance(o); - } - else - { - return new ASN1UTCTime(((ASN1OctetString)o).getOctets()); - } - } - - /** - * The correct format for this is YYMMDDHHMMSSZ (it used to be that seconds were - * never encoded. When you're creating one of these objects from scratch, that's - * what you want to use, otherwise we'll try to deal with whatever gets read from - * the input stream... (this is why the input format is different from the getTime() - * method output). - * <p> - * - * @param time the time string. - */ - public DERUTCTime( - String time) - { - if (time.charAt(time.length() - 1) != 'Z') - { - // we accept this as a variation - if (time.indexOf('-') < 0 && time.indexOf('+') < 0) - { - throw new IllegalArgumentException("time needs to be in format YYMMDDHHMMSSZ"); - } - } - - this.time = Strings.toByteArray(time); - } - - /** - * base constructor from a java.util.date object - */ - public DERUTCTime( - Date time) - { - this.time = Strings.toByteArray(DateFormatter.toUTCDateString(time)); - } - - DERUTCTime( - byte[] time) - { - this.time = time; - } - - /** - * return the time as a date based on whatever a 2 digit year will return. For - * standardised processing use getAdjustedDate(). - * - * @return the resulting date - */ - public Date getDate() + DERUTCTime(byte[] bytes) { - return DateFormatter.adjustedFromUTCDateString(time); + super(bytes); } - /** - * return the time as an adjusted date - * in the range of 1950 - 2049. - * - * @return a date in the range of 1950 to 2049. - */ - public Date getAdjustedDate() - { - return DateFormatter.adjustedFromUTCDateString(time); - } - - /** - * return the time - always in the form of - * YYMMDDhhmmssGMT(+hh:mm|-hh:mm). - * <p> - * Normally in a certificate we would expect "Z" rather than "GMT", - * however adding the "GMT" means we can just use: - * <pre> - * dateF = new SimpleDateFormat("yyMMddHHmmssz"); - * </pre> - * To read in the time and get a date which is compatible with our local - * time zone. - * <p> - * <b>Note:</b> In some cases, due to the local date processing, this - * may lead to unexpected results. If you want to stick the normal - * convention of 1950 to 2049 use the getAdjustedTime() method. - */ - public String getTime() - { - String stime = Strings.fromByteArray(time); - - // - // standardise the format. - // - if (stime.indexOf('-') < 0 && stime.indexOf('+') < 0) - { - if (stime.length() == 11) - { - return stime.substring(0, 10) + "00GMT+00:00"; - } - else - { - return stime.substring(0, 12) + "GMT+00:00"; - } - } - else - { - int index = stime.indexOf('-'); - if (index < 0) - { - index = stime.indexOf('+'); - } - String d = stime; - - if (index == stime.length() - 3) - { - d += "00"; - } - - if (index == 10) - { - return d.substring(0, 10) + "00GMT" + d.substring(10, 13) + ":" + d.substring(13, 15); - } - else - { - return d.substring(0, 12) + "GMT" + d.substring(12, 15) + ":" + d.substring(15, 17); - } - } - } - - /** - * return a time string as an adjusted date with a 4 digit year. This goes - * in the range of 1950 - 2049. - */ - public String getAdjustedTime() - { - String d = this.getTime(); - - if (d.charAt(0) < '5') - { - return "20" + d; - } - else - { - return "19" + d; - } - } - - /** - * Return the time. - * @return The time string as it appeared in the encoded object. - */ - public String getTimeString() - { - return Strings.fromByteArray(time); - } - - boolean isConstructed() - { - return false; - } - - int encodedLength() - { - int length = time.length; - - return 1 + StreamUtil.calculateBodyLength(length) + length; - } - - void encode( - ASN1OutputStream out) - throws IOException - { - out.write(BERTags.UTC_TIME); - - int length = time.length; - - out.writeLength(length); - - for (int i = 0; i != length; i++) - { - out.write((byte)time[i]); - } - } - - boolean asn1Equals( - ASN1Primitive o) - { - if (!(o instanceof DERUTCTime)) - { - return false; - } - - return Arrays.areEqual(time, ((DERUTCTime)o).time); - } - - public int hashCode() + public DERUTCTime(Date date) { - return Arrays.hashCode(time); + super(date); } - public String toString() + public DERUTCTime(String time) { - return Strings.fromByteArray(time); + super(time); } } diff --git a/core/src/main/j2me/org/bouncycastle/crypto/encodings/PKCS1Encoding.java b/core/src/main/j2me/org/bouncycastle/crypto/encodings/PKCS1Encoding.java index e4a8750f..76051c3f 100644 --- a/core/src/main/j2me/org/bouncycastle/crypto/encodings/PKCS1Encoding.java +++ b/core/src/main/j2me/org/bouncycastle/crypto/encodings/PKCS1Encoding.java @@ -1,13 +1,13 @@ package org.bouncycastle.crypto.encodings; +import java.security.SecureRandom; + import org.bouncycastle.crypto.AsymmetricBlockCipher; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.crypto.params.ParametersWithRandom; -import java.security.SecureRandom; - /** * this does your basic PKCS 1 v1.5 padding - whether or not you should be using this * depends on your application - see PKCS1 Version 2 for details. @@ -32,6 +32,8 @@ public class PKCS1Encoding private boolean forEncryption; private boolean forPrivateKey; private boolean useStrictLength; + private int pLen = -1; + private byte[] fallback = null; /** * Basic constructor. @@ -44,11 +46,48 @@ public class PKCS1Encoding this.useStrictLength = useStrict(); } + /** + * Constructor for decryption with a fixed plaintext length. + * + * @param cipher The cipher to use for cryptographic operation. + * @param pLen Length of the expected plaintext. + */ + public PKCS1Encoding( + AsymmetricBlockCipher cipher, + int pLen) + { + this.engine = cipher; + this.useStrictLength = useStrict(); + this.pLen = pLen; + } + + /** + * Constructor for decryption with a fixed plaintext length and a fallback + * value that is returned, if the padding is incorrect. + * + * @param cipher + * The cipher to use for cryptographic operation. + * @param fallback + * The fallback value, we don't to a arraycopy here. + */ + public PKCS1Encoding( + AsymmetricBlockCipher cipher, + byte[] fallback) + { + this.engine = cipher; + this.useStrictLength = useStrict(); + this.fallback = fallback; + this.pLen = fallback.length; + } + + + // // for J2ME compatibility // private boolean useStrict() { + // required if security manager has been installed. String strict = System.getProperty(STRICT_LENGTH_ENABLED_PROPERTY); return strict == null || strict.equals("true"); @@ -174,6 +213,121 @@ public class PKCS1Encoding return engine.processBlock(block, 0, block.length); } + + /** + * Checks if the argument is a correctly PKCS#1.5 encoded Plaintext + * for encryption. + * + * @param encoded The Plaintext. + * @param pLen Expected length of the plaintext. + * @return Either 0, if the encoding is correct, or -1, if it is incorrect. + */ + private static int checkPkcs1Encoding(byte[] encoded, int pLen) { + int correct = 0; + /* + * Check if the first two bytes are 0 2 + */ + correct |= (encoded[0] ^ 2); + + /* + * Now the padding check, check for no 0 byte in the padding + */ + int plen = encoded.length - ( + pLen /* Lenght of the PMS */ + + 1 /* Final 0-byte before PMS */ + ); + + for (int i = 1; i < plen; i++) { + int tmp = encoded[i]; + tmp |= tmp >> 1; + tmp |= tmp >> 2; + tmp |= tmp >> 4; + correct |= (tmp & 1) - 1; + } + + /* + * Make sure the padding ends with a 0 byte. + */ + correct |= encoded[encoded.length - (pLen +1)]; + + /* + * Return 0 or 1, depending on the result. + */ + correct |= correct >> 1; + correct |= correct >> 2; + correct |= correct >> 4; + return ~((correct & 1) - 1); + } + + + /** + * Decode PKCS#1.5 encoding, and return a random value if the padding is not correct. + * + * @param in The encrypted block. + * @param inOff Offset in the encrypted block. + * @param inLen Length of the encrypted block. + * //@param pLen Length of the desired output. + * @return The plaintext without padding, or a random value if the padding was incorrect. + * + * @throws InvalidCipherTextException + */ + private byte[] decodeBlockOrRandom(byte[] in, int inOff, int inLen) + throws InvalidCipherTextException + { + if (!forPrivateKey) + { + throw new InvalidCipherTextException("sorry, this method is only for decryption, not for signing"); + } + + byte[] block = engine.processBlock(in, inOff, inLen); + byte[] random = null; + if (this.fallback == null) + { + random = new byte[this.pLen]; + this.random.nextBytes(random); + } + else + { + random = fallback; + } + + /* + * TODO: This is a potential dangerous side channel. However, you can + * fix this by changing the RSA engine in a way, that it will always + * return blocks of the same length and prepend them with 0 bytes if + * needed. + */ + if (block.length < getOutputBlockSize()) + { + throw new InvalidCipherTextException("block truncated"); + } + + /* + * TODO: Potential side channel. Fix it by making the engine always + * return blocks of the correct length. + */ + if (useStrictLength && block.length != engine.getOutputBlockSize()) + { + throw new InvalidCipherTextException("block incorrect size"); + } + + /* + * Check the padding. + */ + int correct = PKCS1Encoding.checkPkcs1Encoding(block, this.pLen); + + /* + * Now, to a constant time constant memory copy of the decrypted value + * or the random value, depending on the validity of the padding. + */ + byte[] result = new byte[this.pLen]; + for (int i = 0; i < this.pLen; i++) + { + result[i] = (byte)((block[i + (block.length - pLen)] & (~correct)) | (random[i] & correct)); + } + + return result; + } /** * @exception InvalidCipherTextException if the decrypted block is not in PKCS1 format. @@ -184,7 +338,15 @@ public class PKCS1Encoding int inLen) throws InvalidCipherTextException { - byte[] block = engine.processBlock(in, inOff, inLen); + /* + * If the length of the expected plaintext is known, we use a constant-time decryption. + * If the decryption fails, we return a random value. + */ + if (this.pLen != -1) { + return this.decodeBlockOrRandom(in, inOff, inLen); + } + + byte[] block = engine.processBlock(in, inOff, inLen); if (block.length < getOutputBlockSize()) { @@ -192,10 +354,20 @@ public class PKCS1Encoding } byte type = block[0]; - - if (type != 1 && type != 2) + + if (forPrivateKey) { - throw new InvalidCipherTextException("unknown block type"); + if (type != 2) + { + throw new InvalidCipherTextException("unknown block type"); + } + } + else + { + if (type != 1) + { + throw new InvalidCipherTextException("unknown block type"); + } } if (useStrictLength && block.length != engine.getOutputBlockSize()) diff --git a/core/src/main/j2me/org/bouncycastle/math/ec/LongArray.java b/core/src/main/j2me/org/bouncycastle/math/ec/LongArray.java index 4ed4ef4d..b8522c16 100644 --- a/core/src/main/j2me/org/bouncycastle/math/ec/LongArray.java +++ b/core/src/main/j2me/org/bouncycastle/math/ec/LongArray.java @@ -371,6 +371,23 @@ class LongArray } } + public boolean isOne() + { + long[] a = m_ints; + if (a[0] != 1L) + { + return false; + } + for (int i = 1; i < a.length; ++i) + { + if (a[i] != 0L) + { + return false; + } + } + return true; + } + public boolean isZero() { long[] a = m_ints; @@ -822,12 +839,12 @@ class LongArray add(c, cOff, b, 0, bLen); } int k = 1; - while ((a >>>= 1) != 0) + while ((a >>>= 1) != 0L) { if ((a & 1L) != 0L) { long carry = addShiftedUp(c, cOff, b, 0, bLen, k); - if (carry != 0) + if (carry != 0L) { c[cOff + bLen] ^= carry; } @@ -871,8 +888,8 @@ class LongArray if (aLen == 1) { - long a = A.m_ints[0]; - if (a == 1L) + long a0 = A.m_ints[0]; + if (a0 == 1L) { return B; } @@ -880,13 +897,13 @@ class LongArray /* * Fast path for small A, with performance dependent only on the number of set bits */ - long[] c = new long[cLen]; - multiplyWord(a, B.m_ints, bLen, c, 0); + long[] c0 = new long[cLen]; + multiplyWord(a0, B.m_ints, bLen, c0, 0); /* * Reduce the raw answer against the reduction coefficients */ - return reduceResult(c, 0, cLen, m, ks); + return reduceResult(c0, 0, cLen, m, ks); } /* @@ -1003,8 +1020,8 @@ class LongArray if (aLen == 1) { - long a = A.m_ints[0]; - if (a == 1L) + long a0 = A.m_ints[0]; + if (a0 == 1L) { return B; } @@ -1012,13 +1029,13 @@ class LongArray /* * Fast path for small A, with performance dependent only on the number of set bits */ - long[] c = new long[cLen]; - multiplyWord(a, B.m_ints, bLen, c, 0); + long[] c0 = new long[cLen]; + multiplyWord(a0, B.m_ints, bLen, c0, 0); /* * Reduce the raw answer against the reduction coefficients */ - return reduceResult(c, 0, cLen, m, ks); + return reduceResult(c0, 0, cLen, m, ks); } /* @@ -1077,7 +1094,8 @@ class LongArray aVal >>>= 4; int v = (int)aVal & MASK; addBoth(c, cOff, T0, ti[u], T1, ti[v], bMax); - if ((aVal >>>= 4) == 0L) + aVal >>>= 4; + if (aVal == 0L) { break; } @@ -1085,10 +1103,12 @@ class LongArray } } - int cOff = c.length; - while ((cOff -= cLen) != 0) { - addShiftedUp(c, cOff - cLen, c, cOff, cLen, 8); + int cOff = c.length; + while ((cOff -= cLen) != 0) + { + addShiftedUp(c, cOff - cLen, c, cOff, cLen, 8); + } } /* @@ -1132,8 +1152,8 @@ class LongArray if (aLen == 1) { - long a = A.m_ints[0]; - if (a == 1L) + long a0 = A.m_ints[0]; + if (a0 == 1L) { return B; } @@ -1141,13 +1161,13 @@ class LongArray /* * Fast path for small A, with performance dependent only on the number of set bits */ - long[] c = new long[cLen]; - multiplyWord(a, B.m_ints, bLen, c, 0); + long[] c0 = new long[cLen]; + multiplyWord(a0, B.m_ints, bLen, c0, 0); /* * Reduce the raw answer against the reduction coefficients */ - return reduceResult(c, 0, cLen, m, ks); + return reduceResult(c0, 0, cLen, m, ks); } // NOTE: This works, but is slower than width 4 processing @@ -1314,6 +1334,158 @@ class LongArray return reduceResult(c, ci[1], cLen, m, ks); } + public LongArray modReduce(int m, int[] ks) + { + long[] buf = Arrays.clone(m_ints); + int rLen = reduceInPlace(buf, 0, buf.length, m, ks); + return new LongArray(buf, 0, rLen); + } + + public LongArray multiply(LongArray other, int m, int[] ks) + { + /* + * Find out the degree of each argument and handle the zero cases + */ + int aDeg = degree(); + if (aDeg == 0) + { + return this; + } + int bDeg = other.degree(); + if (bDeg == 0) + { + return other; + } + + /* + * Swap if necessary so that A is the smaller argument + */ + LongArray A = this, B = other; + if (aDeg > bDeg) + { + A = other; B = this; + int tmp = aDeg; aDeg = bDeg; bDeg = tmp; + } + + /* + * Establish the word lengths of the arguments and result + */ + int aLen = (aDeg + 63) >>> 6; + int bLen = (bDeg + 63) >>> 6; + int cLen = (aDeg + bDeg + 62) >>> 6; + + if (aLen == 1) + { + long a0 = A.m_ints[0]; + if (a0 == 1L) + { + return B; + } + + /* + * Fast path for small A, with performance dependent only on the number of set bits + */ + long[] c0 = new long[cLen]; + multiplyWord(a0, B.m_ints, bLen, c0, 0); + + /* + * Reduce the raw answer against the reduction coefficients + */ +// return reduceResult(c0, 0, cLen, m, ks); + return new LongArray(c0, 0, cLen); + } + + /* + * Determine if B will get bigger during shifting + */ + int bMax = (bDeg + 7 + 63) >>> 6; + + /* + * Lookup table for the offset of each B in the tables + */ + int[] ti = new int[16]; + + /* + * Precompute table of all 4-bit products of B + */ + long[] T0 = new long[bMax << 4]; + int tOff = bMax; + ti[1] = tOff; + System.arraycopy(B.m_ints, 0, T0, tOff, bLen); + for (int i = 2; i < 16; ++i) + { + ti[i] = (tOff += bMax); + if ((i & 1) == 0) + { + shiftUp(T0, tOff >>> 1, T0, tOff, bMax, 1); + } + else + { + add(T0, bMax, T0, tOff - bMax, T0, tOff, bMax); + } + } + + /* + * Second table with all 4-bit products of B shifted 4 bits + */ + long[] T1 = new long[T0.length]; + shiftUp(T0, 0, T1, 0, T0.length, 4); +// shiftUp(T0, bMax, T1, bMax, tOff, 4); + + long[] a = A.m_ints; + long[] c = new long[cLen << 3]; + + int MASK = 0xF; + + /* + * Lopez-Dahab (Modified) algorithm + */ + + for (int aPos = 0; aPos < aLen; ++aPos) + { + long aVal = a[aPos]; + int cOff = aPos; + for (;;) + { + int u = (int)aVal & MASK; + aVal >>>= 4; + int v = (int)aVal & MASK; + addBoth(c, cOff, T0, ti[u], T1, ti[v], bMax); + aVal >>>= 4; + if (aVal == 0L) + { + break; + } + cOff += cLen; + } + } + + { + int cOff = c.length; + while ((cOff -= cLen) != 0) + { + addShiftedUp(c, cOff - cLen, c, cOff, cLen, 8); + } + } + + /* + * Finally the raw answer is collected, reduce it against the reduction coefficients + */ +// return reduceResult(c, 0, cLen, m, ks); + return new LongArray(c, 0, cLen); + } + + public void reduce(int m, int[] ks) + { + long[] buf = m_ints; + int rLen = reduceInPlace(buf, 0, buf.length, m, ks); + if (rLen < buf.length) + { + m_ints = new long[rLen]; + System.arraycopy(buf, 0, m_ints, 0, rLen); + } + } + private static LongArray reduceResult(long[] buf, int off, int len, int m, int[] ks) { int rLen = reduceInPlace(buf, off, len, m, ks); @@ -1405,13 +1577,13 @@ class LongArray private static void reduceBit(long[] buf, int off, int bit, int m, int[] ks) { flipBit(buf, off, bit); - int base = bit - m; + int n = bit - m; int j = ks.length; while (--j >= 0) { - flipBit(buf, off, ks[j] + base); + flipBit(buf, off, ks[j] + n); } - flipBit(buf, off, base); + flipBit(buf, off, n); } private static void reduceWordWise(long[] buf, int off, int len, int toBit, int m, int[] ks) @@ -1428,12 +1600,14 @@ class LongArray } } - int partial = toBit & 0x3F; - long word = buf[off + toPos] >>> partial; - if (word != 0) { - buf[off + toPos] ^= word << partial; - reduceWord(buf, off, toBit, word, m, ks); + int partial = toBit & 0x3F; + long word = buf[off + toPos] >>> partial; + if (word != 0) + { + buf[off + toPos] ^= word << partial; + reduceWord(buf, off, toBit, word, m, ks); + } } } @@ -1502,37 +1676,59 @@ class LongArray return new LongArray(r, 0, reduceInPlace(r, 0, r.length, m, ks)); } -// private LongArray modSquareN(int n, int m, int[] ks) -// { -// int len = getUsedLength(); -// if (len == 0) -// { -// return this; -// } -// -// int mLen = (m + 63) >>> 6; -// long[] r = new long[mLen << 1]; -// System.arraycopy(m_ints, 0, r, 0, len); -// -// while (--n >= 0) -// { -// squareInPlace(r, len, m, ks); -// len = reduceInPlace(r, 0, r.length, m, ks); -// } -// -// return new LongArray(r, 0, len); -// } -// -// private static void squareInPlace(long[] x, int xLen, int m, int[] ks) -// { -// int pos = xLen << 1; -// while (--xLen >= 0) -// { -// long xVal = x[xLen]; -// x[--pos] = interleave2_32to64((int)(xVal >>> 32)); -// x[--pos] = interleave2_32to64((int)xVal); -// } -// } + public LongArray modSquareN(int n, int m, int[] ks) + { + int len = getUsedLength(); + if (len == 0) + { + return this; + } + + int mLen = (m + 63) >>> 6; + long[] r = new long[mLen << 1]; + System.arraycopy(m_ints, 0, r, 0, len); + + while (--n >= 0) + { + squareInPlace(r, len, m, ks); + len = reduceInPlace(r, 0, r.length, m, ks); + } + + return new LongArray(r, 0, len); + } + + public LongArray square(int m, int[] ks) + { + int len = getUsedLength(); + if (len == 0) + { + return this; + } + + int _2len = len << 1; + long[] r = new long[_2len]; + + int pos = 0; + while (pos < _2len) + { + long mi = m_ints[pos >>> 1]; + r[pos++] = interleave2_32to64((int)mi); + r[pos++] = interleave2_32to64((int)(mi >>> 32)); + } + + return new LongArray(r, 0, r.length); + } + + private static void squareInPlace(long[] x, int xLen, int m, int[] ks) + { + int pos = xLen << 1; + while (--xLen >= 0) + { + long xVal = x[xLen]; + x[--pos] = interleave2_32to64((int)(xVal >>> 32)); + x[--pos] = interleave2_32to64((int)xVal); + } + } private static void interleave(long[] x, int xOff, long[] z, int zOff, int count, int width) { @@ -1856,6 +2052,10 @@ class LongArray * Output: a(z)^(-1) mod f(z) */ int uzDegree = degree(); + if (uzDegree == 0) + { + throw new IllegalStateException(); + } if (uzDegree == 1) { return this; |