Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/humla-spongycastle.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Hook <dgh@cryptoworkshop.com>2013-05-31 11:07:45 +0400
committerDavid Hook <dgh@cryptoworkshop.com>2013-05-31 11:07:45 +0400
commit2b976f5364cfdbc37d3086019d93483c983eb80b (patch)
treecb846af3fd1d43f9c2562a1fb2d06b997ad8f229 /core/src/main/java/org/bouncycastle
parent5f714bd92fbd780d22406f4bc3681be005f6f04a (diff)
initial reshuffle
Diffstat (limited to 'core/src/main/java/org/bouncycastle')
-rw-r--r--core/src/main/java/org/bouncycastle/LICENSE.java63
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java10
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1Boolean.java15
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1Choice.java14
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1Encodable.java6
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1EncodableVector.java36
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1Encoding.java8
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1Enumerated.java22
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1Exception.java25
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1GeneralizedTime.java22
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1Generator.java15
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1InputStream.java466
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1Integer.java22
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1Null.java67
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1Object.java97
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java42
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java146
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1OctetStringParser.java9
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1OutputStream.java194
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1ParsingException.java23
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1Primitive.java69
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java323
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1SequenceParser.java10
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1Set.java460
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1SetParser.java10
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1StreamParser.java247
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1String.java6
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java236
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1TaggedObjectParser.java12
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ASN1UTCTime.java22
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/BERApplicationSpecific.java10
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/BERApplicationSpecificParser.java41
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/BERConstructedOctetString.java144
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/BERFactory.java17
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/BERGenerator.java100
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/BEROctetString.java168
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/BEROctetStringGenerator.java102
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/BEROctetStringParser.java41
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/BEROutputStream.java36
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/BERSequence.java73
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/BERSequenceGenerator.java41
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/BERSequenceParser.java38
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/BERSet.java73
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/BERSetParser.java38
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/BERTaggedObject.java147
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/BERTaggedObjectParser.java66
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/BERTags.java36
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ConstructedOctetStream.java111
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERApplicationSpecific.java276
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERBMPString.java153
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERBitString.java313
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERBoolean.java179
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DEREncodableVector.java18
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DEREnumerated.java158
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERExternal.java294
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERExternalParser.java52
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERFactory.java17
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERGeneralString.java110
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERGeneralizedTime.java350
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERGenerator.java119
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERIA5String.java183
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERInteger.java148
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERNull.java38
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERNumericString.java186
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java425
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DEROctetString.java48
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DEROctetStringParser.java39
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DEROutputStream.java41
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERPrintableString.java213
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERSequence.java98
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERSequenceGenerator.java45
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERSequenceParser.java38
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERSet.java108
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERSetParser.java38
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERT61String.java144
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERT61UTF8String.java151
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERTaggedObject.java118
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERTags.java9
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERUTCTime.java278
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERUTF8String.java132
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERUniversalString.java148
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DERVisibleString.java135
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DLOutputStream.java31
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DLSequence.java98
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DLSet.java101
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DLTaggedObject.java112
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/DefiniteLengthInputStream.java105
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/InMemoryRepresentable.java9
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/IndefiniteLengthInputStream.java111
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/LazyConstructionEnumeration.java43
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/LazyEncodedSequence.java109
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/LimitedInputStream.java32
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/OIDTokenizer.java48
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/StreamUtil.java114
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java51
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/CAKeyUpdAnnContent.java80
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/CMPCertificate.java92
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/CMPObjectIdentifiers.java106
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/CRLAnnContent.java61
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/CertConfirmContent.java54
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/CertOrEncCert.java96
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/CertRepMessage.java123
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/CertResponse.java139
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/CertStatus.java102
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/CertifiedKeyPair.java127
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/Challenge.java120
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/ErrorMsgContent.java121
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/GenMsgContent.java71
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/GenRepContent.java71
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/InfoTypeAndValue.java132
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/KeyRecRepContent.java142
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/OOBCertHash.java117
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/PBMParameter.java117
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/PKIBody.java194
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/PKIConfirmContent.java48
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/PKIFailureInfo.java126
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/PKIFreeText.java115
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/PKIHeader.java260
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/PKIHeaderBuilder.java254
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/PKIMessage.java166
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/PKIMessages.java71
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/PKIStatus.java64
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/PKIStatusInfo.java165
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/POPODecKeyChallContent.java54
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/POPODecKeyRespContent.java55
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/PollRepContent.java119
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/PollReqContent.java80
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/ProtectedPart.java70
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/RevAnnContent.java103
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/RevDetails.java100
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/RevRepContent.java137
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/RevRepContentBuilder.java59
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cmp/RevReqContent.java73
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/Attribute.java100
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/AttributeTable.java254
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/Attributes.java61
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedData.java218
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedDataParser.java157
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedData.java296
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedDataParser.java197
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/CMSAttributes.java13
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/CMSObjectIdentifiers.java28
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/CompressedData.java110
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/CompressedDataParser.java48
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java107
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/ContentInfoParser.java48
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/DigestedData.java121
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfo.java109
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfoParser.java51
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/EncryptedData.java94
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/EnvelopedData.java205
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/EnvelopedDataParser.java118
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/Evidence.java56
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/IssuerAndSerialNumber.java111
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/KEKIdentifier.java139
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/KEKRecipientInfo.java121
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientIdentifier.java103
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientInfo.java153
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/KeyTransRecipientInfo.java114
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/MetaData.java120
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/OriginatorIdentifierOrKey.java165
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/OriginatorInfo.java128
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/OriginatorPublicKey.java100
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/OtherKeyAttribute.java82
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/OtherRecipientInfo.java102
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/OtherRevocationInfoFormat.java98
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/PasswordRecipientInfo.java143
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/RecipientEncryptedKey.java99
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/RecipientIdentifier.java98
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/RecipientInfo.java154
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/RecipientKeyIdentifier.java156
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/SCVPReqRes.java90
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/SignedData.java307
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/SignedDataParser.java139
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/SignerIdentifier.java98
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/SignerInfo.java211
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/Time.java128
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/TimeStampAndCRL.java82
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/TimeStampTokenEvidence.java84
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/TimeStampedData.java121
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/TimeStampedDataParser.java127
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cms/ecc/MQVuserKeyingMaterial.java112
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/crmf/AttributeTypeAndValue.java80
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/crmf/CRMFObjectIdentifiers.java21
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/crmf/CertId.java84
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/crmf/CertReqMessages.java74
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/crmf/CertReqMsg.java145
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/crmf/CertRequest.java97
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/crmf/CertTemplate.java163
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/crmf/CertTemplateBuilder.java152
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/crmf/Controls.java72
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/crmf/EncKeyWithID.java117
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/crmf/EncryptedKey.java81
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/crmf/EncryptedValue.java164
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/crmf/OptionalValidity.java98
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/crmf/PKIArchiveOptions.java116
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/crmf/PKIPublicationInfo.java81
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/crmf/PKMACValue.java104
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/crmf/POPOPrivKey.java104
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/crmf/POPOSigningKey.java127
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/crmf/POPOSigningKeyInput.java134
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/crmf/ProofOfPossession.java108
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/crmf/SinglePubInfo.java72
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/crmf/SubsequentMessage.java29
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java48
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cryptopro/ECGOST3410NamedCurves.java168
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cryptopro/ECGOST3410ParamSetParameters.java99
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cryptopro/GOST28147Parameters.java72
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410NamedParameters.java116
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410ParamSetParameters.java105
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410PublicKeyAlgParameters.java101
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/dvcs/CertEtcToken.java171
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSCertInfo.java302
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSCertInfoBuilder.java151
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSErrorNotice.java96
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSObjectIdentifiers.java36
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSRequest.java107
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSRequestInformation.java271
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSRequestInformationBuilder.java224
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSResponse.java117
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSTime.java111
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/dvcs/Data.java149
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/dvcs/PathProcInput.java180
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/dvcs/ServiceType.java92
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/dvcs/TargetEtcChain.java191
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/eac/BidirectionalMap.java23
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/eac/CVCertificate.java317
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/eac/CVCertificateRequest.java170
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/eac/CertificateBody.java475
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/eac/CertificateHolderAuthorization.java185
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/eac/CertificateHolderReference.java66
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/eac/CertificationAuthorityReference.java15
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java55
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/eac/EACTags.java209
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/eac/ECDSAPublicKey.java341
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/eac/Flags.java96
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/eac/PackedDate.java103
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/eac/PublicKeyDataObject.java35
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/eac/RSAPublicKey.java121
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/eac/UnsignedInteger.java74
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/CommitmentTypeIdentifier.java14
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/CommitmentTypeIndication.java83
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/CommitmentTypeQualifier.java108
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/CompleteRevocationRefs.java65
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/CrlIdentifier.java106
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/CrlListID.java66
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/CrlOcspRef.java106
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/CrlValidatedID.java82
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/ESFAttributes.java22
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/OcspIdentifier.java73
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/OcspListID.java72
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/OcspResponsesID.java83
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/OtherHash.java81
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/OtherHashAlgAndValue.java81
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/OtherRevRefs.java87
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/OtherRevVals.java89
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/RevocationValues.java151
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/SPUserNotice.java99
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/SPuri.java45
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/SigPolicyQualifierInfo.java75
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/SigPolicyQualifiers.java77
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/SignaturePolicyId.java103
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/SignaturePolicyIdentifier.java76
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/SignerAttribute.java123
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/esf/SignerLocation.java162
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ess/ContentHints.java114
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ess/ContentIdentifier.java63
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ess/ESSCertID.java95
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ess/ESSCertIDv2.java155
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ess/OtherCertID.java137
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ess/OtherSigningCertificate.java109
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ess/SigningCertificate.java109
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ess/SigningCertificateV2.java136
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/gnu/GNUObjectIdentifiers.java30
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/iana/IANAObjectIdentifiers.java20
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/icao/CscaMasterList.java114
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/icao/DataGroupHash.java97
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/icao/ICAOObjectIdentifiers.java33
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/icao/LDSSecurityObject.java159
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/icao/LDSVersionInfo.java75
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java180
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/isismtt/ocsp/CertHash.java124
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/isismtt/ocsp/RequestedCertificate.java183
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/isismtt/x509/AdditionalInformationSyntax.java70
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/isismtt/x509/AdmissionSyntax.java280
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/isismtt/x509/Admissions.java189
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/isismtt/x509/DeclarationOfMajority.java164
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/isismtt/x509/MonetaryLimit.java131
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/isismtt/x509/NamingAuthority.java244
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/isismtt/x509/ProcurationSyntax.java240
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/isismtt/x509/ProfessionInfo.java408
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/isismtt/x509/Restriction.java81
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java9
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/microsoft/MicrosoftObjectIdentifiers.java17
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/misc/CAST5CBCParameters.java78
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/misc/IDEACBCPar.java81
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java47
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/misc/NetscapeCertType.java54
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/misc/NetscapeRevocationURL.java18
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/misc/VerisignCzagExtension.java18
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/mozilla/PublicKeyAndChallenge.java63
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/nist/NISTNamedCurves.java99
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java60
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ntt/NTTObjectIdentifiers.java17
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ocsp/BasicOCSPResponse.java112
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ocsp/CertID.java105
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ocsp/CertStatus.java105
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ocsp/CrlID.java111
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ocsp/OCSPObjectIdentifiers.java22
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ocsp/OCSPRequest.java90
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ocsp/OCSPResponse.java90
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ocsp/OCSPResponseStatus.java71
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ocsp/Request.java91
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ocsp/ResponderID.java104
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ocsp/ResponseBytes.java82
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ocsp/ResponseData.java182
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ocsp/RevokedInfo.java92
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ocsp/ServiceLocator.java36
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ocsp/Signature.java111
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ocsp/SingleResponse.java181
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ocsp/TBSRequest.java172
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/oiw/ElGamalParameter.java54
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java31
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/Attribute.java88
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/AuthenticatedSafe.java74
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/CRLBag.java82
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/CertBag.java66
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequest.java91
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java164
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/ContentInfo.java102
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/DHParameter.java104
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedData.java115
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java86
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/EncryptionScheme.java56
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/IssuerAndSerialNumber.java85
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/KeyDerivationFunc.java56
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/MacData.java106
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/PBEParameter.java73
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/PBES2Algorithms.java77
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/PBES2Parameters.java77
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java108
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/PKCS12PBEParams.java69
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java258
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/Pfx.java87
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java164
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/RC2CBCParameter.java93
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java151
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/RSAPrivateKey.java187
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/RSAPrivateKeyStructure.java189
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/RSAPublicKey.java95
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/RSASSAPSSparams.java172
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/SafeBag.java96
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/SignedData.java167
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/pkcs/SignerInfo.java178
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKey.java143
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKeyStructure.java128
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/sec/SECNamedCurves.java1029
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/sec/SECObjectIdentifiers.java50
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/smime/SMIMEAttributes.java10
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/smime/SMIMECapabilities.java115
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/smime/SMIMECapabilitiesAttribute.java16
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/smime/SMIMECapability.java103
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/smime/SMIMECapabilityVector.java50
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/smime/SMIMEEncryptionKeyPreferenceAttribute.java48
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTNamedCurves.java351
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java42
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/tsp/Accuracy.java173
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/tsp/MessageImprint.java78
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/tsp/TSTInfo.java233
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/tsp/TimeStampReq.java179
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/tsp/TimeStampResp.java84
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145BinaryField.java119
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145ECBinary.java144
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145NamedCurves.java94
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145Params.java121
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145PointEncoder.java162
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145PublicKey.java46
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/ua/UAObjectIdentifiers.java16
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java404
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/util/DERDump.java41
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/util/Dump.java22
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x500/AttributeTypeAndValue.java72
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x500/DirectoryString.java125
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x500/RDN.java119
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x500/X500Name.java326
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x500/X500NameBuilder.java87
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x500/X500NameStyle.java79
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x500/style/BCStrictStyle.java36
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java459
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x500/style/IETFUtils.java572
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x500/style/RFC4519Style.java358
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x500/style/X500NameTokenizer.java90
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/AccessDescription.java98
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/AlgorithmIdentifier.java173
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/AttCertIssuer.java91
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/AttCertValidityPeriod.java84
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/Attribute.java93
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java94
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java166
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/AuthorityInformationAccess.java101
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java232
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/BasicConstraints.java164
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/CRLDistPoint.java100
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/CRLNumber.java54
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/CRLReason.java151
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/CertPolicyId.java57
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/Certificate.java131
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java127
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/CertificatePair.java169
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java99
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/DSAParameter.java92
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/DigestInfo.java86
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/DisplayText.java165
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/DistributionPoint.java158
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/DistributionPointName.java138
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java147
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/Extension.java321
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/Extensions.java221
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/ExtensionsGenerator.java94
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/GeneralName.java439
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/GeneralNames.java108
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/GeneralNamesBuilder.java39
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/GeneralSubtree.java218
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/Holder.java245
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/IetfAttrSyntax.java189
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/IssuerSerial.java115
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java274
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/KeyPurposeId.java157
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java102
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/NameConstraints.java118
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/NoticeReference.java170
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/ObjectDigestInfo.java190
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/PolicyInformation.java87
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/PolicyMappings.java107
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierId.java31
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java114
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/PrivateKeyUsagePeriod.java84
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/RSAPublicKeyStructure.java98
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/ReasonFlags.java85
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/RoleSyntax.java237
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/SubjectDirectoryAttributes.java144
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/SubjectKeyIdentifier.java135
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java156
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/TBSCertList.java309
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/TBSCertificate.java192
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/TBSCertificateStructure.java194
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/Target.java138
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/TargetInformation.java120
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/Targets.java121
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/Time.java133
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/UserNotice.java132
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/V1TBSCertificateGenerator.java144
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/V2AttributeCertificateInfoGenerator.java158
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/V2Form.java157
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/V2TBSCertListGenerator.java281
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java212
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/X509AttributeIdentifiers.java29
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/X509CertificateStructure.java129
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java65
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/X509Extension.java248
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/X509Extensions.java489
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/X509ExtensionsGenerator.java117
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/X509Name.java1381
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/X509NameEntryConverter.java113
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/X509NameTokenizer.java91
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java67
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/qualified/BiometricData.java122
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/qualified/ETSIQCObjectIdentifiers.java16
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/qualified/Iso4217CurrencyCode.java93
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/qualified/MonetaryValue.java92
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/qualified/QCStatement.java95
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/qualified/RFC3739QCObjectIdentifiers.java14
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/qualified/SemanticsInformation.java131
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/qualified/TypeOfBiometricData.java90
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/sigi/NameOrPseudonym.java191
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/sigi/PersonalData.java214
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x509/sigi/SigIObjectIdentifiers.java45
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x9/DHDomainParameters.java139
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x9/DHPublicKey.java52
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x9/DHValidationParms.java80
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x9/ECNamedCurveTable.java97
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x9/KeySpecificInfo.java68
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x9/OtherInfo.java96
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x9/X962NamedCurves.java621
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x9/X962Parameters.java86
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x9/X9Curve.java161
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java176
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x9/X9ECParametersHolder.java18
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x9/X9ECPoint.java48
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x9/X9FieldElement.java64
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x9/X9FieldID.java109
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x9/X9IntegerConverter.java47
-rw-r--r--core/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java132
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/AsymmetricBlockCipher.java45
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/AsymmetricCipherKeyPair.java61
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/AsymmetricCipherKeyPairGenerator.java22
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/BasicAgreement.java26
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/BlockCipher.java56
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/BufferedAsymmetricBlockCipher.java171
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java313
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/CipherKeyGenerator.java38
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/CipherParameters.java8
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/Commitment.java42
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/Committer.java24
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/CryptoException.java48
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/DSA.java36
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/DataLengthException.java29
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/DerivationFunction.java17
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/DerivationParameters.java8
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/Digest.java51
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/EphemeralKeyPair.java23
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/ExtendedDigest.java13
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/InvalidCipherTextException.java40
-rwxr-xr-xcore/src/main/java/org/bouncycastle/crypto/KeyEncapsulation.java22
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/KeyEncoder.java8
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/KeyGenerationParameters.java48
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/KeyParser.java12
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/Mac.java71
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/MaxBytesExceededException.java27
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/OutputLengthException.java10
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/PBEParametersGenerator.java171
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/RuntimeCryptoException.java26
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/Signer.java43
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/SignerWithRecovery.java34
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java108
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/StreamCipher.java53
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/Wrapper.java18
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/agreement/DHAgreement.java94
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/agreement/DHBasicAgreement.java71
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/agreement/DHStandardGroups.java206
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java51
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/agreement/ECDHCBasicAgreement.java58
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/agreement/ECMQVBasicAgreement.java91
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEParticipant.java573
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroup.java122
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroups.java113
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound1Payload.java99
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound2Payload.java71
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound3Payload.java52
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEUtil.java508
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKDFParameters.java54
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKEKGenerator.java131
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java74
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Client.java93
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Server.java90
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Util.java91
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6VerifierGenerator.java47
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/commitments/HashCommitter.java81
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/GOST3411Digest.java362
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java142
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java361
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/MD2Digest.java258
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/MD4Digest.java291
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/MD5Digest.java323
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/NonMemoableDigest.java64
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/NullDigest.java48
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/RIPEMD128Digest.java482
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/RIPEMD160Digest.java443
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/RIPEMD256Digest.java497
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/RIPEMD320Digest.java481
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java309
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java311
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java314
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java99
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/SHA3Digest.java547
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java102
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/SHA512tDigest.java205
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/ShortenedDigest.java80
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/TigerDigest.java879
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/WhirlpoolDigest.java409
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/ec/ECDecryptor.java11
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/ec/ECElGamalDecryptor.java48
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java74
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/ec/ECEncryptor.java11
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java74
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java76
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/ec/ECPair.java38
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/ec/ECPairTransform.java10
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/ec/ECUtil.java22
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/encodings/ISO9796d1Encoding.java287
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java357
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java257
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java546
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java875
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/AESLightEngine.java439
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/AESWrapEngine.java16
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/BlowfishEngine.java577
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/CAST5Engine.java831
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/CAST6Engine.java296
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/CamelliaEngine.java684
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/CamelliaLightEngine.java592
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/CamelliaWrapEngine.java15
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java495
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/DESedeEngine.java127
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java348
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/ElGamalEngine.java217
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/GOST28147Engine.java372
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java303
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/Grainv1Engine.java289
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/HC128Engine.java257
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/HC256Engine.java244
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/IDEAEngine.java367
-rwxr-xr-xcore/src/main/java/org/bouncycastle/crypto/engines/IESEngine.java398
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/ISAACEngine.java219
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/NaccacheSternEngine.java437
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/NoekeonEngine.java263
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/NullEngine.java85
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/RC2Engine.java317
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/RC2WrapEngine.java383
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/RC4Engine.java144
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/RC532Engine.java287
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/RC564Engine.java288
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/RC6Engine.java363
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/RFC3211WrapEngine.java175
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java177
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java126
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/RSABlindingEngine.java137
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/RSACoreEngine.java203
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/RSAEngine.java78
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/RijndaelEngine.java725
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/SEEDEngine.java346
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/SEEDWrapEngine.java15
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java321
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/SerpentEngine.java783
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/SkipjackEngine.java260
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/TEAEngine.java179
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/TwofishEngine.java680
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/VMPCEngine.java139
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/VMPCKSA3Engine.java45
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/XTEAEngine.java183
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/examples/DESExample.java419
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/examples/JPAKEExample.java214
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java142
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/DESKeyGenerator.java48
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java56
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java42
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/DHKeyGeneratorHelper.java51
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/DHKeyPairGenerator.java42
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/DHParametersGenerator.java52
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/DHParametersHelper.java73
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java61
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java387
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/DSTU4145KeyPairGenerator.java21
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java53
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/ElGamalKeyPairGenerator.java44
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/ElGamalParametersGenerator.java43
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/EphemeralKeyPairGenerator.java26
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/GOST3410KeyPairGenerator.java57
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/GOST3410ParametersGenerator.java541
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/HKDFBytesGenerator.java161
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/KDF1BytesGenerator.java23
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/KDF2BytesGenerator.java24
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/MGF1BytesGenerator.java114
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/NaccacheSternKeyPairGenerator.java365
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java131
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/PKCS12ParametersGenerator.java220
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/PKCS5S1ParametersGenerator.java119
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java153
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/RSABlindingFactorGenerator.java77
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java147
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/generators/SCrypt.java147
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java244
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java188
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/io/DigestInputStream.java52
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/io/DigestOutputStream.java42
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/io/MacInputStream.java52
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/io/MacOutputStream.java42
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/io/SignerInputStream.java52
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/io/SignerOutputStream.java38
-rwxr-xr-xcore/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java256
-rwxr-xr-xcore/src/main/java/org/bouncycastle/crypto/kems/RSAKeyEncapsulation.java164
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/macs/BlockCipherMac.java174
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java229
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/macs/CFBBlockCipherMac.java388
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/macs/CMac.java238
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/macs/GMac.java114
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/macs/GOST28147Mac.java298
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/macs/HMac.java231
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/macs/ISO9797Alg3Mac.java305
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/macs/OldHMac.java138
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/macs/SipHash.java192
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/macs/VMPCMac.java186
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java126
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java253
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java378
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java258
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java265
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java368
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java574
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java234
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java581
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/modes/OFBBlockCipher.java187
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/modes/OpenPGPCFBBlockCipher.java312
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/modes/PGPCFBBlockCipher.java450
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/modes/PaddedBlockCipher.java253
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java113
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java36
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMMultiplier.java18
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMExponentiator.java7
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMMultiplier.java7
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java260
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java57
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables64kGCMMultiplier.java73
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java90
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/paddings/BlockCipherPadding.java48
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java79
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/paddings/ISO7816d4Padding.java77
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/paddings/PKCS7Padding.java76
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java299
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/paddings/TBCPadding.java89
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/paddings/X923Padding.java80
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/paddings/ZeroBytePadding.java73
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/AEADParameters.java60
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/AsymmetricKeyParameter.java20
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/CCMParameters.java21
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/DESParameters.java107
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/DESedeParameters.java57
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/DHKeyGenerationParameters.java30
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/DHKeyParameters.java54
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/DHParameters.java189
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/DHPrivateKeyParameters.java41
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/DHPublicKeyParameters.java41
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/DHValidationParameters.java50
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/DSAKeyGenerationParameters.java25
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/DSAKeyParameters.java21
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/DSAParameterGenerationParameters.java80
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/DSAParameters.java74
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/DSAPrivateKeyParameters.java23
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/DSAPublicKeyParameters.java23
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/DSAValidationParameters.java65
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java74
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/ECKeyGenerationParameters.java25
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/ECKeyParameters.java21
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/ECPrivateKeyParameters.java22
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java22
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/ElGamalKeyGenerationParameters.java30
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/ElGamalKeyParameters.java47
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/ElGamalParameters.java69
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/ElGamalPrivateKeyParameters.java46
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/ElGamalPublicKeyParameters.java41
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/GOST3410KeyGenerationParameters.java25
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/GOST3410KeyParameters.java21
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/GOST3410Parameters.java74
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/GOST3410PrivateKeyParameters.java23
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/GOST3410PublicKeyParameters.java23
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/GOST3410ValidationParameters.java84
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/HKDFParameters.java123
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/IESParameters.java44
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/IESWithCipherParameters.java30
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/ISO18033KDFParameters.java23
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/KDFParameters.java31
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/KeyParameter.java30
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/MGFParameters.java32
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/MQVPrivateParameters.java43
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/MQVPublicParameters.java28
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/NaccacheSternKeyGenerationParameters.java97
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/NaccacheSternKeyParameters.java53
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/NaccacheSternPrivateKeyParameters.java50
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/ParametersWithIV.java39
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/ParametersWithRandom.java36
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/ParametersWithSBox.java28
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/ParametersWithSalt.java42
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/RC2Parameters.java36
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/RC5Parameters.java35
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/RSABlindingParameters.java35
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/RSAKeyGenerationParameters.java48
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/RSAKeyParameters.java31
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java67
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/parsers/DHIESPublicKeyParser.java31
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/parsers/ECIESPublicKeyParser.java53
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/prng/BasicEntropySourceProvider.java53
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/prng/DRBGProvider.java8
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/prng/DigestRandomGenerator.java123
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/prng/EntropySource.java25
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/prng/EntropySourceProvider.java6
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/prng/FixedSecureRandom.java135
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/prng/RandomGenerator.java38
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/prng/ReversedWindowGenerator.java111
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandom.java74
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java249
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/prng/ThreadedSeedGenerator.java95
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/prng/VMPCRandomGenerator.java127
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/prng/drbg/CTRSP800DRBG.java468
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java267
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/prng/drbg/HMacSP800DRBG.java171
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/prng/drbg/HashSP800DRBG.java269
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/prng/drbg/SP80090DRBG.java25
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/prng/drbg/Utils.java103
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/signers/DSADigestSigner.java163
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java138
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java163
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java169
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java158
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java188
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/signers/GOST3410Signer.java127
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/signers/GenericSigner.java136
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2PSSSigner.java668
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2Signer.java618
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/signers/PSSSigner.java348
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java232
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCipherFactory.java15
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java218
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java96
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsKeyExchange.java164
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java14
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsServer.java304
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSigner.java13
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/AlertDescription.java215
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/AlertLevel.java10
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/AlwaysValidVerifyer.java24
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/BulkCipherAlgorithm.java24
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/ByteQueue.java123
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/Certificate.java153
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/CertificateRequest.java140
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/CertificateVerifyer.java16
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/CipherSuite.java207
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/CipherType.java19
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/ClientAuthenticationType.java12
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/ClientCertificateType.java23
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java117
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/CompressionMethod.java24
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/ConnectionEnd.java14
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/ContentType.java12
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java634
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/DTLSEpoch.java53
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/DTLSHandshakeRetransmit.java9
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/DTLSProtocol.java84
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/DTLSReassembler.java136
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java497
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java432
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/DTLSReplayWindow.java91
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java631
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/DTLSTransport.java81
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/DatagramTransport.java22
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsAgreementCredentials.java79
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsCipherFactory.java163
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java380
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsEncryptionCredentials.java75
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java384
-rwxr-xr-xcore/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSignerCredentials.java81
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/DeferredHash.java128
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/DigestAlgorithm.java23
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/ECBasisType.java11
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/ECCurveType.java28
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/ECPointFormat.java15
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java43
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/ExporterLabel.java31
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java50
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/HandshakeType.java33
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/HashAlgorithm.java16
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/KeyExchangeAlgorithm.java47
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/LegacyTlsAuthentication.java28
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/LegacyTlsClient.java33
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/MACAlgorithm.java24
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/NamedCurve.java59
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/NewSessionTicket.java43
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/PRFAlgorithm.java23
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java114
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/ProtocolVersion.java126
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java356
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/SRPTlsClient.java137
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/SRTPProtectionProfile.java12
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/SSL3Mac.java114
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java57
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/ServerOnlyTlsAuthentication.java10
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/SignatureAlgorithm.java13
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/SignatureAndHashAlgorithm.java98
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/SupplementalDataEntry.java24
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/SupplementalDataType.java12
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsAEADCipher.java197
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsAgreementCredentials.java13
-rwxr-xr-xcore/src/main/java/org/bouncycastle/crypto/tls/TlsAuthentication.java26
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java313
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsCipher.java14
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsCipherFactory.java13
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java76
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsClientContext.java6
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsClientContextImpl.java19
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java732
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsCompression.java10
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsContext.java32
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsCredentials.java6
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java113
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java222
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java100
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsDSASigner.java57
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsDSSSigner.java21
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsECCUtils.java619
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsECDHEKeyExchange.java206
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java250
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsECDSASigner.java21
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsEncryptionCredentials.java11
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsFatalAlert.java21
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java14
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsInputStream.java41
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsKeyExchange.java55
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsMac.java172
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsNullCipher.java127
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsNullCompression.java17
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsOutputStream.java44
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsPSKIdentity.java12
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java210
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java23
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java943
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsProtocolHandler.java23
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsRSAKeyExchange.java255
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsRSASigner.java89
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsRSAUtils.java52
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsRuntimeException.java26
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsSRPKeyExchange.java217
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsSRTPUtils.java89
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsServer.java89
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsServerContext.java6
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsServerContextImpl.java19
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java772
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsSigner.java23
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsSignerCredentials.java10
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsStreamCipher.java126
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java968
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/UDPTransport.java77
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/UseSRTPData.java54
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/tls/UserMappingType.java12
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/util/Pack.java192
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java170
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java51
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java207
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java81
-rw-r--r--core/src/main/java/org/bouncycastle/i18n/ErrorBundle.java120
-rw-r--r--core/src/main/java/org/bouncycastle/i18n/LocaleString.java24
-rw-r--r--core/src/main/java/org/bouncycastle/i18n/LocalizedException.java49
-rw-r--r--core/src/main/java/org/bouncycastle/i18n/LocalizedMessage.java476
-rw-r--r--core/src/main/java/org/bouncycastle/i18n/MessageBundle.java92
-rw-r--r--core/src/main/java/org/bouncycastle/i18n/MissingEntryException.java73
-rw-r--r--core/src/main/java/org/bouncycastle/i18n/TextBundle.java92
-rw-r--r--core/src/main/java/org/bouncycastle/i18n/filter/Filter.java21
-rw-r--r--core/src/main/java/org/bouncycastle/i18n/filter/HTMLFilter.java68
-rw-r--r--core/src/main/java/org/bouncycastle/i18n/filter/SQLFilter.java69
-rw-r--r--core/src/main/java/org/bouncycastle/i18n/filter/TrustedInput.java23
-rw-r--r--core/src/main/java/org/bouncycastle/i18n/filter/UntrustedInput.java44
-rw-r--r--core/src/main/java/org/bouncycastle/i18n/filter/UntrustedUrlInput.java14
-rw-r--r--core/src/main/java/org/bouncycastle/math/ec/ECAlgorithms.java92
-rw-r--r--core/src/main/java/org/bouncycastle/math/ec/ECConstants.java12
-rw-r--r--core/src/main/java/org/bouncycastle/math/ec/ECCurve.java621
-rw-r--r--core/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java1196
-rw-r--r--core/src/main/java/org/bouncycastle/math/ec/ECMultiplier.java19
-rw-r--r--core/src/main/java/org/bouncycastle/math/ec/ECPoint.java593
-rw-r--r--core/src/main/java/org/bouncycastle/math/ec/FpNafMultiplier.java39
-rw-r--r--core/src/main/java/org/bouncycastle/math/ec/IntArray.java518
-rw-r--r--core/src/main/java/org/bouncycastle/math/ec/PreCompInfo.java10
-rw-r--r--core/src/main/java/org/bouncycastle/math/ec/ReferenceMultiplier.java30
-rw-r--r--core/src/main/java/org/bouncycastle/math/ec/SimpleBigDecimal.java253
-rw-r--r--core/src/main/java/org/bouncycastle/math/ec/Tnaf.java844
-rw-r--r--core/src/main/java/org/bouncycastle/math/ec/WNafMultiplier.java240
-rw-r--r--core/src/main/java/org/bouncycastle/math/ec/WNafPreCompInfo.java44
-rw-r--r--core/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java119
-rw-r--r--core/src/main/java/org/bouncycastle/math/ec/WTauNafPreCompInfo.java39
-rw-r--r--core/src/main/java/org/bouncycastle/math/ec/ZTauElement.java37
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/asn1/GMSSPrivateKey.java1312
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/asn1/GMSSPublicKey.java75
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/asn1/McElieceCCA2PrivateKey.java173
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/asn1/McElieceCCA2PublicKey.java96
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/asn1/McEliecePrivateKey.java197
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/asn1/McEliecePublicKey.java97
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/asn1/PQCObjectIdentifiers.java27
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/asn1/ParSet.java140
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/asn1/RainbowPrivateKey.java350
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/asn1/RainbowPublicKey.java175
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/DigestingMessageSigner.java117
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/MessageEncryptor.java30
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/MessageSigner.java32
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSDigestProvider.java8
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSKeyGenerationParameters.java26
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSKeyPairGenerator.java477
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSKeyParameters.java22
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSLeaf.java376
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSParameters.java156
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSPrivateKeyParameters.java1041
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSPublicKeyParameters.java33
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSRootCalc.java596
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSRootSig.java666
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSSigner.java404
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSUtils.java145
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/gmss/Treehash.java525
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/gmss/util/GMSSRandom.java78
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/gmss/util/GMSSUtil.java151
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/gmss/util/WinternitzOTSVerify.java345
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/gmss/util/WinternitzOTSignature.java405
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/Conversions.java236
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2KeyGenerationParameters.java25
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2KeyPairGenerator.java119
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2KeyParameters.java25
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2Parameters.java51
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2Primitives.java86
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2PrivateKeyParameters.java172
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2PublicKeyParameters.java97
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceFujisakiCipher.java218
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceFujisakiDigestCipher.java128
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKeyGenerationParameters.java25
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKeyPairGenerator.java151
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKeyParameters.java25
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKobaraImaiCipher.java319
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKobaraImaiDigestCipher.java128
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePKCSCipher.java224
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePKCSDigestCipher.java128
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceParameters.java181
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePointchevalCipher.java241
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePointchevalDigestCipher.java128
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePrivateKeyParameters.java197
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePublicKeyParameters.java96
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/ntru/IndexGenerator.java239
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionKeyGenerationParameters.java463
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionKeyPairGenerator.java113
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionKeyParameters.java20
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionParameters.java410
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionPrivateKeyParameters.java199
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionPublicKeyParameters.java131
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEngine.java495
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUParameters.java7
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigner.java259
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSignerPrng.java64
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningKeyGenerationParameters.java407
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningKeyPairGenerator.java357
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningParameters.java269
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningPrivateKeyParameters.java385
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningPublicKeyParameters.java132
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/Layer.java322
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowKeyGenerationParameters.java26
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowKeyPairGenerator.java414
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowKeyParameters.java25
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowParameters.java111
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowPrivateKeyParameters.java117
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowPublicKeyParameters.java53
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowSigner.java301
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/util/ComputeInField.java490
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/util/GF2Field.java139
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/util/RainbowUtil.java230
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/BigEndianConversions.java306
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/BigIntUtils.java138
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/ByteUtils.java414
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/CharUtils.java98
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Matrix.java1323
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Polynomial.java2039
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Vector.java539
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mField.java366
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mMatrix.java377
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mVector.java256
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nElement.java186
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nField.java292
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nONBElement.java1154
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nONBField.java546
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomial.java587
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomialElement.java1021
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomialField.java553
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GFElement.java158
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GoppaCode.java310
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/IntUtils.java203
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/IntegerFunctions.java1424
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/LittleEndianConversions.java230
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Matrix.java131
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Permutation.java247
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/PolynomialGF2mSmallM.java1125
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/PolynomialRingGF2.java278
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/PolynomialRingGF2m.java175
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/RandUtils.java25
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Vector.java69
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/ntru/euclid/BigIntEuclidean.java54
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/ntru/euclid/IntEuclidean.java51
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/BigDecimalPolynomial.java258
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/BigIntPolynomial.java394
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/Constants.java12
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/DenseTernaryPolynomial.java142
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/IntegerPolynomial.java1358
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/LongPolynomial2.java255
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/LongPolynomial5.java149
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/ModularResultant.java46
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/Polynomial.java42
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/ProductFormPolynomial.java153
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/Resultant.java28
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/SparseTernaryPolynomial.java320
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/TernaryPolynomial.java25
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/ntru/util/ArrayEncoder.java292
-rw-r--r--core/src/main/java/org/bouncycastle/pqc/math/ntru/util/Util.java158
-rw-r--r--core/src/main/java/org/bouncycastle/util/Arrays.java736
-rw-r--r--core/src/main/java/org/bouncycastle/util/BigIntegers.java123
-rw-r--r--core/src/main/java/org/bouncycastle/util/CollectionStore.java57
-rw-r--r--core/src/main/java/org/bouncycastle/util/IPAddress.java188
-rw-r--r--core/src/main/java/org/bouncycastle/util/Integers.java9
-rw-r--r--core/src/main/java/org/bouncycastle/util/Memoable.java23
-rw-r--r--core/src/main/java/org/bouncycastle/util/MemoableResetException.java22
-rw-r--r--core/src/main/java/org/bouncycastle/util/Selector.java9
-rw-r--r--core/src/main/java/org/bouncycastle/util/Store.java9
-rw-r--r--core/src/main/java/org/bouncycastle/util/StoreException.java18
-rw-r--r--core/src/main/java/org/bouncycastle/util/StreamParser.java10
-rw-r--r--core/src/main/java/org/bouncycastle/util/StreamParsingException.java18
-rw-r--r--core/src/main/java/org/bouncycastle/util/Strings.java303
-rw-r--r--core/src/main/java/org/bouncycastle/util/encoders/Base64.java151
-rw-r--r--core/src/main/java/org/bouncycastle/util/encoders/Base64Encoder.java328
-rw-r--r--core/src/main/java/org/bouncycastle/util/encoders/BufferedDecoder.java96
-rw-r--r--core/src/main/java/org/bouncycastle/util/encoders/BufferedEncoder.java96
-rw-r--r--core/src/main/java/org/bouncycastle/util/encoders/DecoderException.java19
-rw-r--r--core/src/main/java/org/bouncycastle/util/encoders/Encoder.java17
-rw-r--r--core/src/main/java/org/bouncycastle/util/encoders/EncoderException.java19
-rw-r--r--core/src/main/java/org/bouncycastle/util/encoders/Hex.java148
-rw-r--r--core/src/main/java/org/bouncycastle/util/encoders/HexEncoder.java187
-rw-r--r--core/src/main/java/org/bouncycastle/util/encoders/HexTranslator.java87
-rw-r--r--core/src/main/java/org/bouncycastle/util/encoders/Translator.java23
-rw-r--r--core/src/main/java/org/bouncycastle/util/encoders/UrlBase64.java129
-rw-r--r--core/src/main/java/org/bouncycastle/util/encoders/UrlBase64Encoder.java25
-rw-r--r--core/src/main/java/org/bouncycastle/util/io/StreamOverflowException.java12
-rw-r--r--core/src/main/java/org/bouncycastle/util/io/Streams.java87
-rw-r--r--core/src/main/java/org/bouncycastle/util/io/TeeInputStream.java62
-rw-r--r--core/src/main/java/org/bouncycastle/util/io/TeeOutputStream.java52
-rw-r--r--core/src/main/java/org/bouncycastle/util/io/pem/PemGenerationException.java25
-rw-r--r--core/src/main/java/org/bouncycastle/util/io/pem/PemHeader.java66
-rw-r--r--core/src/main/java/org/bouncycastle/util/io/pem/PemObject.java61
-rw-r--r--core/src/main/java/org/bouncycastle/util/io/pem/PemObjectGenerator.java7
-rw-r--r--core/src/main/java/org/bouncycastle/util/io/pem/PemObjectParser.java9
-rw-r--r--core/src/main/java/org/bouncycastle/util/io/pem/PemReader.java84
-rw-r--r--core/src/main/java/org/bouncycastle/util/io/pem/PemWriter.java137
-rw-r--r--core/src/main/java/org/bouncycastle/util/test/FixedSecureRandom.java135
-rw-r--r--core/src/main/java/org/bouncycastle/util/test/NumberParsing.java34
-rw-r--r--core/src/main/java/org/bouncycastle/util/test/SimpleTest.java84
-rw-r--r--core/src/main/java/org/bouncycastle/util/test/SimpleTestResult.java80
-rw-r--r--core/src/main/java/org/bouncycastle/util/test/Test.java8
-rw-r--r--core/src/main/java/org/bouncycastle/util/test/TestFailedException.java18
-rw-r--r--core/src/main/java/org/bouncycastle/util/test/TestResult.java10
-rw-r--r--core/src/main/java/org/bouncycastle/util/test/UncloseableOutputStream.java23
1127 files changed, 172188 insertions, 0 deletions
diff --git a/core/src/main/java/org/bouncycastle/LICENSE.java b/core/src/main/java/org/bouncycastle/LICENSE.java
new file mode 100644
index 00000000..b97d88be
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/LICENSE.java
@@ -0,0 +1,63 @@
+package org.bouncycastle;
+
+/**
+ * The Bouncy Castle License
+ *
+ * Copyright (c) 2000-2012 The Legion Of The Bouncy Castle (http://www.bouncycastle.org)
+ * <p>
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ * <p>
+ * The above copyright notice and this permission notice shall be included in all copies or substantial
+ * portions of the Software.
+ * <p>
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+public class LICENSE
+{
+ public static String licenseText =
+ "Copyright (c) 2000-2012 The Legion Of The Bouncy Castle (http://www.bouncycastle.org) "
+ + System.getProperty("line.separator")
+ + System.getProperty("line.separator")
+ + "Permission is hereby granted, free of charge, to any person obtaining a copy of this software "
+ + System.getProperty("line.separator")
+ + "and associated documentation files (the \"Software\"), to deal in the Software without restriction, "
+ + System.getProperty("line.separator")
+ + "including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, "
+ + System.getProperty("line.separator")
+ + "and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,"
+ + System.getProperty("line.separator")
+ + "subject to the following conditions:"
+ + System.getProperty("line.separator")
+ + System.getProperty("line.separator")
+ + "The above copyright notice and this permission notice shall be included in all copies or substantial"
+ + System.getProperty("line.separator")
+ + "portions of the Software."
+ + System.getProperty("line.separator")
+ + System.getProperty("line.separator")
+ + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,"
+ + System.getProperty("line.separator")
+ + "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR"
+ + System.getProperty("line.separator")
+ + "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE"
+ + System.getProperty("line.separator")
+ + "LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR"
+ + System.getProperty("line.separator")
+ + "OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER"
+ + System.getProperty("line.separator")
+ + "DEALINGS IN THE SOFTWARE.";
+
+ public static void main(
+ String[] args)
+ {
+ System.out.println(licenseText);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java b/core/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java
new file mode 100644
index 00000000..d7216a6c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public interface ASN1ApplicationSpecificParser
+ extends ASN1Encodable, InMemoryRepresentable
+{
+ ASN1Encodable readObject()
+ throws IOException;
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1Boolean.java b/core/src/main/java/org/bouncycastle/asn1/ASN1Boolean.java
new file mode 100644
index 00000000..1360e8b5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1Boolean.java
@@ -0,0 +1,15 @@
+package org.bouncycastle.asn1;
+
+public class ASN1Boolean
+ extends DERBoolean
+{
+ public ASN1Boolean(boolean value)
+ {
+ super(value);
+ }
+
+ ASN1Boolean(byte[] value)
+ {
+ super(value);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1Choice.java b/core/src/main/java/org/bouncycastle/asn1/ASN1Choice.java
new file mode 100644
index 00000000..603131d1
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1Choice.java
@@ -0,0 +1,14 @@
+package org.bouncycastle.asn1;
+
+/**
+ * Marker interface for CHOICE objects - if you implement this in a role your
+ * own object any attempt to tag the object implicitly will convert the tag to
+ * an explicit one as the encoding rules require.
+ * <p>
+ * If you use this interface your class should also implement the getInstance
+ * pattern which takes a tag object and the tagging mode used.
+ */
+public interface ASN1Choice
+{
+ // marker interface
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1Encodable.java b/core/src/main/java/org/bouncycastle/asn1/ASN1Encodable.java
new file mode 100644
index 00000000..f5738bf4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1Encodable.java
@@ -0,0 +1,6 @@
+package org.bouncycastle.asn1;
+
+public interface ASN1Encodable
+{
+ ASN1Primitive toASN1Primitive();
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1EncodableVector.java b/core/src/main/java/org/bouncycastle/asn1/ASN1EncodableVector.java
new file mode 100644
index 00000000..2819a8d5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1EncodableVector.java
@@ -0,0 +1,36 @@
+package org.bouncycastle.asn1;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+public class ASN1EncodableVector
+{
+ Vector v = new Vector();
+
+ public ASN1EncodableVector()
+ {
+ }
+
+ public void add(ASN1Encodable obj)
+ {
+ v.addElement(obj);
+ }
+
+ public void addAll(ASN1EncodableVector other)
+ {
+ for (Enumeration en = other.v.elements(); en.hasMoreElements();)
+ {
+ v.addElement(en.nextElement());
+ }
+ }
+
+ public ASN1Encodable get(int i)
+ {
+ return (ASN1Encodable)v.elementAt(i);
+ }
+
+ public int size()
+ {
+ return v.size();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1Encoding.java b/core/src/main/java/org/bouncycastle/asn1/ASN1Encoding.java
new file mode 100644
index 00000000..821d3b9d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1Encoding.java
@@ -0,0 +1,8 @@
+package org.bouncycastle.asn1;
+
+public interface ASN1Encoding
+{
+ static final String DER = "DER";
+ static final String DL = "DL";
+ static final String BER = "BER";
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1Enumerated.java b/core/src/main/java/org/bouncycastle/asn1/ASN1Enumerated.java
new file mode 100644
index 00000000..d93fd912
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1Enumerated.java
@@ -0,0 +1,22 @@
+package org.bouncycastle.asn1;
+
+import java.math.BigInteger;
+
+public class ASN1Enumerated
+ extends DEREnumerated
+{
+ ASN1Enumerated(byte[] bytes)
+ {
+ super(bytes);
+ }
+
+ public ASN1Enumerated(BigInteger value)
+ {
+ super(value);
+ }
+
+ public ASN1Enumerated(int value)
+ {
+ super(value);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1Exception.java b/core/src/main/java/org/bouncycastle/asn1/ASN1Exception.java
new file mode 100644
index 00000000..dc0ee203
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1Exception.java
@@ -0,0 +1,25 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public class ASN1Exception
+ extends IOException
+{
+ private Throwable cause;
+
+ ASN1Exception(String message)
+ {
+ super(message);
+ }
+
+ ASN1Exception(String message, Throwable cause)
+ {
+ super(message);
+ this.cause = cause;
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1GeneralizedTime.java b/core/src/main/java/org/bouncycastle/asn1/ASN1GeneralizedTime.java
new file mode 100644
index 00000000..0088a536
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1GeneralizedTime.java
@@ -0,0 +1,22 @@
+package org.bouncycastle.asn1;
+
+import java.util.Date;
+
+public class ASN1GeneralizedTime
+ extends DERGeneralizedTime
+{
+ ASN1GeneralizedTime(byte[] bytes)
+ {
+ super(bytes);
+ }
+
+ public ASN1GeneralizedTime(Date time)
+ {
+ super(time);
+ }
+
+ public ASN1GeneralizedTime(String time)
+ {
+ super(time);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1Generator.java b/core/src/main/java/org/bouncycastle/asn1/ASN1Generator.java
new file mode 100644
index 00000000..50cb7054
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1Generator.java
@@ -0,0 +1,15 @@
+package org.bouncycastle.asn1;
+
+import java.io.OutputStream;
+
+public abstract class ASN1Generator
+{
+ protected OutputStream _out;
+
+ public ASN1Generator(OutputStream out)
+ {
+ _out = out;
+ }
+
+ public abstract OutputStream getRawOutputStream();
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1InputStream.java b/core/src/main/java/org/bouncycastle/asn1/ASN1InputStream.java
new file mode 100644
index 00000000..44714334
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1InputStream.java
@@ -0,0 +1,466 @@
+package org.bouncycastle.asn1;
+
+import java.io.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.util.io.Streams;
+
+/**
+ * a general purpose ASN.1 decoder - note: this class differs from the
+ * others in that it returns null after it has read the last object in
+ * the stream. If an ASN.1 NULL is encountered a DER/BER Null object is
+ * returned.
+ */
+public class ASN1InputStream
+ extends FilterInputStream
+ implements BERTags
+{
+ private final int limit;
+ private final boolean lazyEvaluate;
+
+ private final byte[][] tmpBuffers;
+
+ public ASN1InputStream(
+ InputStream is)
+ {
+ this(is, StreamUtil.findLimit(is));
+ }
+
+ /**
+ * Create an ASN1InputStream based on the input byte array. The length of DER objects in
+ * the stream is automatically limited to the length of the input array.
+ *
+ * @param input array containing ASN.1 encoded data.
+ */
+ public ASN1InputStream(
+ byte[] input)
+ {
+ this(new ByteArrayInputStream(input), input.length);
+ }
+
+ /**
+ * Create an ASN1InputStream based on the input byte array. The length of DER objects in
+ * the stream is automatically limited to the length of the input array.
+ *
+ * @param input array containing ASN.1 encoded data.
+ * @param lazyEvaluate true if parsing inside constructed objects can be delayed.
+ */
+ public ASN1InputStream(
+ byte[] input,
+ boolean lazyEvaluate)
+ {
+ this(new ByteArrayInputStream(input), input.length, lazyEvaluate);
+ }
+
+ /**
+ * Create an ASN1InputStream where no DER object will be longer than limit.
+ *
+ * @param input stream containing ASN.1 encoded data.
+ * @param limit maximum size of a DER encoded object.
+ */
+ public ASN1InputStream(
+ InputStream input,
+ int limit)
+ {
+ this(input, limit, false);
+ }
+
+ /**
+ * Create an ASN1InputStream where no DER object will be longer than limit, and constructed
+ * objects such as sequences will be parsed lazily.
+ *
+ * @param input stream containing ASN.1 encoded data.
+ * @param lazyEvaluate true if parsing inside constructed objects can be delayed.
+ */
+ public ASN1InputStream(
+ InputStream input,
+ boolean lazyEvaluate)
+ {
+ this(input, StreamUtil.findLimit(input), lazyEvaluate);
+ }
+
+ /**
+ * Create an ASN1InputStream where no DER object will be longer than limit, and constructed
+ * objects such as sequences will be parsed lazily.
+ *
+ * @param input stream containing ASN.1 encoded data.
+ * @param limit maximum size of a DER encoded object.
+ * @param lazyEvaluate true if parsing inside constructed objects can be delayed.
+ */
+ public ASN1InputStream(
+ InputStream input,
+ int limit,
+ boolean lazyEvaluate)
+ {
+ super(input);
+ this.limit = limit;
+ this.lazyEvaluate = lazyEvaluate;
+ this.tmpBuffers = new byte[11][];
+ }
+
+ int getLimit()
+ {
+ return limit;
+ }
+
+ protected int readLength()
+ throws IOException
+ {
+ return readLength(this, limit);
+ }
+
+ protected void readFully(
+ byte[] bytes)
+ throws IOException
+ {
+ if (Streams.readFully(this, bytes) != bytes.length)
+ {
+ throw new EOFException("EOF encountered in middle of object");
+ }
+ }
+
+ /**
+ * build an object given its tag and the number of bytes to construct it from.
+ */
+ protected ASN1Primitive buildObject(
+ int tag,
+ int tagNo,
+ int length)
+ throws IOException
+ {
+ boolean isConstructed = (tag & CONSTRUCTED) != 0;
+
+ DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(this, length);
+
+ if ((tag & APPLICATION) != 0)
+ {
+ return new DERApplicationSpecific(isConstructed, tagNo, defIn.toByteArray());
+ }
+
+ if ((tag & TAGGED) != 0)
+ {
+ return new ASN1StreamParser(defIn).readTaggedObject(isConstructed, tagNo);
+ }
+
+ if (isConstructed)
+ {
+ // TODO There are other tags that may be constructed (e.g. BIT_STRING)
+ switch (tagNo)
+ {
+ case OCTET_STRING:
+ //
+ // yes, people actually do this...
+ //
+ ASN1EncodableVector v = buildDEREncodableVector(defIn);
+ ASN1OctetString[] strings = new ASN1OctetString[v.size()];
+
+ for (int i = 0; i != strings.length; i++)
+ {
+ strings[i] = (ASN1OctetString)v.get(i);
+ }
+
+ return new BEROctetString(strings);
+ case SEQUENCE:
+ if (lazyEvaluate)
+ {
+ return new LazyEncodedSequence(defIn.toByteArray());
+ }
+ else
+ {
+ return DERFactory.createSequence(buildDEREncodableVector(defIn));
+ }
+ case SET:
+ return DERFactory.createSet(buildDEREncodableVector(defIn));
+ case EXTERNAL:
+ return new DERExternal(buildDEREncodableVector(defIn));
+ default:
+ throw new IOException("unknown tag " + tagNo + " encountered");
+ }
+ }
+
+ return createPrimitiveDERObject(tagNo, defIn, tmpBuffers);
+ }
+
+ ASN1EncodableVector buildEncodableVector()
+ throws IOException
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ ASN1Primitive o;
+
+ while ((o = readObject()) != null)
+ {
+ v.add(o);
+ }
+
+ return v;
+ }
+
+ ASN1EncodableVector buildDEREncodableVector(
+ DefiniteLengthInputStream dIn) throws IOException
+ {
+ return new ASN1InputStream(dIn).buildEncodableVector();
+ }
+
+ public ASN1Primitive readObject()
+ throws IOException
+ {
+ int tag = read();
+ if (tag <= 0)
+ {
+ if (tag == 0)
+ {
+ throw new IOException("unexpected end-of-contents marker");
+ }
+
+ return null;
+ }
+
+ //
+ // calculate tag number
+ //
+ int tagNo = readTagNumber(this, tag);
+
+ boolean isConstructed = (tag & CONSTRUCTED) != 0;
+
+ //
+ // calculate length
+ //
+ int length = readLength();
+
+ if (length < 0) // indefinite length method
+ {
+ if (!isConstructed)
+ {
+ throw new IOException("indefinite length primitive encoding encountered");
+ }
+
+ IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(this, limit);
+ ASN1StreamParser sp = new ASN1StreamParser(indIn, limit);
+
+ if ((tag & APPLICATION) != 0)
+ {
+ return new BERApplicationSpecificParser(tagNo, sp).getLoadedObject();
+ }
+
+ if ((tag & TAGGED) != 0)
+ {
+ return new BERTaggedObjectParser(true, tagNo, sp).getLoadedObject();
+ }
+
+ // TODO There are other tags that may be constructed (e.g. BIT_STRING)
+ switch (tagNo)
+ {
+ case OCTET_STRING:
+ return new BEROctetStringParser(sp).getLoadedObject();
+ case SEQUENCE:
+ return new BERSequenceParser(sp).getLoadedObject();
+ case SET:
+ return new BERSetParser(sp).getLoadedObject();
+ case EXTERNAL:
+ return new DERExternalParser(sp).getLoadedObject();
+ default:
+ throw new IOException("unknown BER object encountered");
+ }
+ }
+ else
+ {
+ try
+ {
+ return buildObject(tag, tagNo, length);
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new ASN1Exception("corrupted stream detected", e);
+ }
+ }
+ }
+
+ static int readTagNumber(InputStream s, int tag)
+ throws IOException
+ {
+ int tagNo = tag & 0x1f;
+
+ //
+ // with tagged object tag number is bottom 5 bits, or stored at the start of the content
+ //
+ if (tagNo == 0x1f)
+ {
+ tagNo = 0;
+
+ int b = s.read();
+
+ // X.690-0207 8.1.2.4.2
+ // "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
+ if ((b & 0x7f) == 0) // Note: -1 will pass
+ {
+ throw new IOException("corrupted stream - invalid high tag number found");
+ }
+
+ while ((b >= 0) && ((b & 0x80) != 0))
+ {
+ tagNo |= (b & 0x7f);
+ tagNo <<= 7;
+ b = s.read();
+ }
+
+ if (b < 0)
+ {
+ throw new EOFException("EOF found inside tag value.");
+ }
+
+ tagNo |= (b & 0x7f);
+ }
+
+ return tagNo;
+ }
+
+ static int readLength(InputStream s, int limit)
+ throws IOException
+ {
+ int length = s.read();
+ if (length < 0)
+ {
+ throw new EOFException("EOF found when length expected");
+ }
+
+ if (length == 0x80)
+ {
+ return -1; // indefinite-length encoding
+ }
+
+ if (length > 127)
+ {
+ int size = length & 0x7f;
+
+ // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here
+ if (size > 4)
+ {
+ throw new IOException("DER length more than 4 bytes: " + size);
+ }
+
+ length = 0;
+ for (int i = 0; i < size; i++)
+ {
+ int next = s.read();
+
+ if (next < 0)
+ {
+ throw new EOFException("EOF found reading length");
+ }
+
+ length = (length << 8) + next;
+ }
+
+ if (length < 0)
+ {
+ throw new IOException("corrupted stream - negative length found");
+ }
+
+ if (length >= limit) // after all we must have read at least 1 byte
+ {
+ throw new IOException("corrupted stream - out of bounds length found");
+ }
+ }
+
+ return length;
+ }
+
+ private static byte[] getBuffer(DefiniteLengthInputStream defIn, byte[][] tmpBuffers)
+ throws IOException
+ {
+ int len = defIn.getRemaining();
+ if (defIn.getRemaining() < tmpBuffers.length)
+ {
+ byte[] buf = tmpBuffers[len];
+
+ if (buf == null)
+ {
+ buf = tmpBuffers[len] = new byte[len];
+ }
+
+ Streams.readFully(defIn, buf);
+
+ return buf;
+ }
+ else
+ {
+ return defIn.toByteArray();
+ }
+ }
+
+ private static char[] getBMPCharBuffer(DefiniteLengthInputStream defIn)
+ throws IOException
+ {
+ int len = defIn.getRemaining() / 2;
+ char[] buf = new char[len];
+ int totalRead = 0;
+ while (totalRead < len)
+ {
+ int ch1 = defIn.read();
+ if (ch1 < 0)
+ {
+ break;
+ }
+ int ch2 = defIn.read();
+ if (ch2 < 0)
+ {
+ break;
+ }
+ buf[totalRead++] = (char)((ch1 << 8) | (ch2 & 0xff));
+ }
+
+ return buf;
+ }
+
+ static ASN1Primitive createPrimitiveDERObject(
+ int tagNo,
+ DefiniteLengthInputStream defIn,
+ byte[][] tmpBuffers)
+ throws IOException
+ {
+ switch (tagNo)
+ {
+ case BIT_STRING:
+ return DERBitString.fromInputStream(defIn.getRemaining(), defIn);
+ case BMP_STRING:
+ return new DERBMPString(getBMPCharBuffer(defIn));
+ case BOOLEAN:
+ return ASN1Boolean.fromOctetString(getBuffer(defIn, tmpBuffers));
+ case ENUMERATED:
+ return ASN1Enumerated.fromOctetString(getBuffer(defIn, tmpBuffers));
+ case GENERALIZED_TIME:
+ return new ASN1GeneralizedTime(defIn.toByteArray());
+ case GENERAL_STRING:
+ return new DERGeneralString(defIn.toByteArray());
+ case IA5_STRING:
+ return new DERIA5String(defIn.toByteArray());
+ case INTEGER:
+ return new ASN1Integer(defIn.toByteArray());
+ case NULL:
+ return DERNull.INSTANCE; // actual content is ignored (enforce 0 length?)
+ case NUMERIC_STRING:
+ return new DERNumericString(defIn.toByteArray());
+ case OBJECT_IDENTIFIER:
+ return ASN1ObjectIdentifier.fromOctetString(getBuffer(defIn, tmpBuffers));
+ case OCTET_STRING:
+ return new DEROctetString(defIn.toByteArray());
+ case PRINTABLE_STRING:
+ return new DERPrintableString(defIn.toByteArray());
+ case T61_STRING:
+ return new DERT61String(defIn.toByteArray());
+ case UNIVERSAL_STRING:
+ return new DERUniversalString(defIn.toByteArray());
+ case UTC_TIME:
+ return new ASN1UTCTime(defIn.toByteArray());
+ case UTF8_STRING:
+ return new DERUTF8String(defIn.toByteArray());
+ case VISIBLE_STRING:
+ return new DERVisibleString(defIn.toByteArray());
+ default:
+ throw new IOException("unknown tag " + tagNo + " encountered");
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1Integer.java b/core/src/main/java/org/bouncycastle/asn1/ASN1Integer.java
new file mode 100644
index 00000000..d60c6a8e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1Integer.java
@@ -0,0 +1,22 @@
+package org.bouncycastle.asn1;
+
+import java.math.BigInteger;
+
+public class ASN1Integer
+ extends DERInteger
+{
+ ASN1Integer(byte[] bytes)
+ {
+ super(bytes);
+ }
+
+ public ASN1Integer(BigInteger value)
+ {
+ super(value);
+ }
+
+ public ASN1Integer(long value)
+ {
+ super(value);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1Null.java b/core/src/main/java/org/bouncycastle/asn1/ASN1Null.java
new file mode 100644
index 00000000..64028696
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1Null.java
@@ -0,0 +1,67 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+/**
+ * A NULL object.
+ */
+public abstract class ASN1Null
+ extends ASN1Primitive
+{
+ /**
+ * @deprecated use DERNull.INSTANCE
+ */
+ public ASN1Null()
+ {
+ }
+
+ public static ASN1Null getInstance(Object o)
+ {
+ if (o instanceof ASN1Null)
+ {
+ return (ASN1Null)o;
+ }
+
+ if (o != null)
+ {
+ try
+ {
+ return ASN1Null.getInstance(ASN1Primitive.fromByteArray((byte[])o));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("failed to construct NULL from byte[]: " + e.getMessage());
+ }
+ catch (ClassCastException e)
+ {
+ throw new IllegalArgumentException("unknown object in getInstance(): " + o.getClass().getName());
+ }
+ }
+
+ return null;
+ }
+
+ public int hashCode()
+ {
+ return -1;
+ }
+
+ boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof ASN1Null))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ abstract void encode(ASN1OutputStream out)
+ throws IOException;
+
+ public String toString()
+ {
+ return "NULL";
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1Object.java b/core/src/main/java/org/bouncycastle/asn1/ASN1Object.java
new file mode 100644
index 00000000..956fb7d6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1Object.java
@@ -0,0 +1,97 @@
+package org.bouncycastle.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+public abstract class ASN1Object
+ implements ASN1Encodable
+{
+ /**
+ * Return the default BER or DER encoding for this object.
+ *
+ * @return BER/DER byte encoded object.
+ * @throws java.io.IOException on encoding error.
+ */
+ public byte[] getEncoded()
+ throws IOException
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ aOut.writeObject(this);
+
+ return bOut.toByteArray();
+ }
+
+ /**
+ * Return either the default for "BER" or a DER encoding if "DER" is specified.
+ *
+ * @param encoding name of encoding to use.
+ * @return byte encoded object.
+ * @throws IOException on encoding error.
+ */
+ public byte[] getEncoded(
+ String encoding)
+ throws IOException
+ {
+ if (encoding.equals(ASN1Encoding.DER))
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+
+ dOut.writeObject(this);
+
+ return bOut.toByteArray();
+ }
+ else if (encoding.equals(ASN1Encoding.DL))
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DLOutputStream dOut = new DLOutputStream(bOut);
+
+ dOut.writeObject(this);
+
+ return bOut.toByteArray();
+ }
+
+ return this.getEncoded();
+ }
+
+ public int hashCode()
+ {
+ return this.toASN1Primitive().hashCode();
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (this == o)
+ {
+ return true;
+ }
+
+ if (!(o instanceof ASN1Encodable))
+ {
+ return false;
+ }
+
+ ASN1Encodable other = (ASN1Encodable)o;
+
+ return this.toASN1Primitive().equals(other.toASN1Primitive());
+ }
+
+ /**
+ * @deprecated use toASN1Primitive()
+ * @return the underlying primitive type.
+ */
+ public ASN1Primitive toASN1Object()
+ {
+ return this.toASN1Primitive();
+ }
+
+ protected static boolean hasEncodedTagValue(Object obj, int tagValue)
+ {
+ return (obj instanceof byte[]) && ((byte[])obj)[0] == tagValue;
+ }
+
+ public abstract ASN1Primitive toASN1Primitive();
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java b/core/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java
new file mode 100644
index 00000000..98f46a6d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java
@@ -0,0 +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);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java b/core/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java
new file mode 100644
index 00000000..703b858c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java
@@ -0,0 +1,146 @@
+package org.bouncycastle.asn1;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+
+public abstract class ASN1OctetString
+ extends ASN1Primitive
+ implements ASN1OctetStringParser
+{
+ byte[] string;
+
+ /**
+ * return an Octet String 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 ASN1OctetString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ ASN1Primitive o = obj.getObject();
+
+ if (explicit || o instanceof ASN1OctetString)
+ {
+ return getInstance(o);
+ }
+ else
+ {
+ return BEROctetString.fromSequence(ASN1Sequence.getInstance(o));
+ }
+ }
+
+ /**
+ * return an Octet String from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static ASN1OctetString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof ASN1OctetString)
+ {
+ return (ASN1OctetString)obj;
+ }
+ else if (obj instanceof byte[])
+ {
+ try
+ {
+ return ASN1OctetString.getInstance(ASN1Primitive.fromByteArray((byte[])obj));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("failed to construct OCTET STRING from byte[]: " + e.getMessage());
+ }
+ }
+ else if (obj instanceof ASN1Encodable)
+ {
+ ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+
+ if (primitive instanceof ASN1OctetString)
+ {
+ return (ASN1OctetString)primitive;
+ }
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * @param string the octets making up the octet string.
+ */
+ public ASN1OctetString(
+ byte[] string)
+ {
+ if (string == null)
+ {
+ throw new NullPointerException("string cannot be null");
+ }
+ this.string = string;
+ }
+
+ public InputStream getOctetStream()
+ {
+ return new ByteArrayInputStream(string);
+ }
+
+ public ASN1OctetStringParser parser()
+ {
+ return this;
+ }
+
+ public byte[] getOctets()
+ {
+ return string;
+ }
+
+ public int hashCode()
+ {
+ return Arrays.hashCode(this.getOctets());
+ }
+
+ boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof ASN1OctetString))
+ {
+ return false;
+ }
+
+ ASN1OctetString other = (ASN1OctetString)o;
+
+ return Arrays.areEqual(string, other.string);
+ }
+
+ public ASN1Primitive getLoadedObject()
+ {
+ return this.toASN1Primitive();
+ }
+
+ ASN1Primitive toDERObject()
+ {
+ return new DEROctetString(string);
+ }
+
+ ASN1Primitive toDLObject()
+ {
+ return new DEROctetString(string);
+ }
+
+ abstract void encode(ASN1OutputStream out)
+ throws IOException;
+
+ public String toString()
+ {
+ return "#"+new String(Hex.encode(string));
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1OctetStringParser.java b/core/src/main/java/org/bouncycastle/asn1/ASN1OctetStringParser.java
new file mode 100644
index 00000000..00423175
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1OctetStringParser.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.asn1;
+
+import java.io.InputStream;
+
+public interface ASN1OctetStringParser
+ extends ASN1Encodable, InMemoryRepresentable
+{
+ public InputStream getOctetStream();
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1OutputStream.java b/core/src/main/java/org/bouncycastle/asn1/ASN1OutputStream.java
new file mode 100644
index 00000000..9a46a78b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1OutputStream.java
@@ -0,0 +1,194 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Stream that produces output based on the default encoding for the passed in objects.
+ */
+public class ASN1OutputStream
+{
+ private OutputStream os;
+
+ public ASN1OutputStream(
+ OutputStream os)
+ {
+ this.os = os;
+ }
+
+ void writeLength(
+ int length)
+ throws IOException
+ {
+ if (length > 127)
+ {
+ int size = 1;
+ int val = length;
+
+ while ((val >>>= 8) != 0)
+ {
+ size++;
+ }
+
+ write((byte)(size | 0x80));
+
+ for (int i = (size - 1) * 8; i >= 0; i -= 8)
+ {
+ write((byte)(length >> i));
+ }
+ }
+ else
+ {
+ write((byte)length);
+ }
+ }
+
+ void write(int b)
+ throws IOException
+ {
+ os.write(b);
+ }
+
+ void write(byte[] bytes)
+ throws IOException
+ {
+ os.write(bytes);
+ }
+
+ void write(byte[] bytes, int off, int len)
+ throws IOException
+ {
+ os.write(bytes, off, len);
+ }
+
+ void writeEncoded(
+ int tag,
+ byte[] bytes)
+ throws IOException
+ {
+ write(tag);
+ writeLength(bytes.length);
+ write(bytes);
+ }
+
+ void writeTag(int flags, int tagNo)
+ throws IOException
+ {
+ if (tagNo < 31)
+ {
+ write(flags | tagNo);
+ }
+ else
+ {
+ write(flags | 0x1f);
+ if (tagNo < 128)
+ {
+ write(tagNo);
+ }
+ else
+ {
+ byte[] stack = new byte[5];
+ int pos = stack.length;
+
+ stack[--pos] = (byte)(tagNo & 0x7F);
+
+ do
+ {
+ tagNo >>= 7;
+ stack[--pos] = (byte)(tagNo & 0x7F | 0x80);
+ }
+ while (tagNo > 127);
+
+ write(stack, pos, stack.length - pos);
+ }
+ }
+ }
+
+ void writeEncoded(int flags, int tagNo, byte[] bytes)
+ throws IOException
+ {
+ writeTag(flags, tagNo);
+ writeLength(bytes.length);
+ write(bytes);
+ }
+
+ protected void writeNull()
+ throws IOException
+ {
+ os.write(BERTags.NULL);
+ os.write(0x00);
+ }
+
+ public void writeObject(
+ ASN1Encodable obj)
+ throws IOException
+ {
+ if (obj != null)
+ {
+ obj.toASN1Primitive().encode(this);
+ }
+ else
+ {
+ throw new IOException("null object detected");
+ }
+ }
+
+ void writeImplicitObject(ASN1Primitive obj)
+ throws IOException
+ {
+ if (obj != null)
+ {
+ obj.encode(new ImplicitOutputStream(os));
+ }
+ else
+ {
+ throw new IOException("null object detected");
+ }
+ }
+
+ public void close()
+ throws IOException
+ {
+ os.close();
+ }
+
+ public void flush()
+ throws IOException
+ {
+ os.flush();
+ }
+
+ ASN1OutputStream getDERSubStream()
+ {
+ return new DEROutputStream(os);
+ }
+
+ ASN1OutputStream getDLSubStream()
+ {
+ return new DLOutputStream(os);
+ }
+
+ private class ImplicitOutputStream
+ extends ASN1OutputStream
+ {
+ private boolean first = true;
+
+ public ImplicitOutputStream(OutputStream os)
+ {
+ super(os);
+ }
+
+ public void write(int b)
+ throws IOException
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ super.write(b);
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1ParsingException.java b/core/src/main/java/org/bouncycastle/asn1/ASN1ParsingException.java
new file mode 100644
index 00000000..995b5e93
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1ParsingException.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.asn1;
+
+public class ASN1ParsingException
+ extends IllegalStateException
+{
+ private Throwable cause;
+
+ public ASN1ParsingException(String message)
+ {
+ super(message);
+ }
+
+ public ASN1ParsingException(String message, Throwable cause)
+ {
+ super(message);
+ this.cause = cause;
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1Primitive.java b/core/src/main/java/org/bouncycastle/asn1/ASN1Primitive.java
new file mode 100644
index 00000000..e6fe1370
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1Primitive.java
@@ -0,0 +1,69 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public abstract class ASN1Primitive
+ extends ASN1Object
+{
+ ASN1Primitive()
+ {
+
+ }
+
+ /**
+ * Create a base ASN.1 object from a byte stream.
+ *
+ * @param data the byte stream to parse.
+ * @return the base ASN.1 object represented by the byte stream.
+ * @exception IOException if there is a problem parsing the data.
+ */
+ public static ASN1Primitive fromByteArray(byte[] data)
+ throws IOException
+ {
+ ASN1InputStream aIn = new ASN1InputStream(data);
+
+ try
+ {
+ return aIn.readObject();
+ }
+ catch (ClassCastException e)
+ {
+ throw new IOException("cannot recognise object in stream");
+ }
+ }
+
+ public final boolean equals(Object o)
+ {
+ if (this == o)
+ {
+ return true;
+ }
+
+ return (o instanceof ASN1Encodable) && asn1Equals(((ASN1Encodable)o).toASN1Primitive());
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return this;
+ }
+
+ ASN1Primitive toDERObject()
+ {
+ return this;
+ }
+
+ ASN1Primitive toDLObject()
+ {
+ return this;
+ }
+
+ public abstract int hashCode();
+
+ abstract boolean isConstructed();
+
+ abstract int encodedLength() throws IOException;
+
+ abstract void encode(ASN1OutputStream out) throws IOException;
+
+ abstract boolean asn1Equals(ASN1Primitive o);
+} \ No newline at end of file
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java b/core/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java
new file mode 100644
index 00000000..0507a2bd
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java
@@ -0,0 +1,323 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Vector;
+
+public abstract class ASN1Sequence
+ extends ASN1Primitive
+{
+ protected Vector seq = new Vector();
+
+ /**
+ * return an ASN1Sequence from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static ASN1Sequence getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof ASN1Sequence)
+ {
+ return (ASN1Sequence)obj;
+ }
+ else if (obj instanceof ASN1SequenceParser)
+ {
+ return ASN1Sequence.getInstance(((ASN1SequenceParser)obj).toASN1Primitive());
+ }
+ else if (obj instanceof byte[])
+ {
+ try
+ {
+ return ASN1Sequence.getInstance(fromByteArray((byte[])obj));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("failed to construct sequence from byte[]: " + e.getMessage());
+ }
+ }
+ else if (obj instanceof ASN1Encodable)
+ {
+ ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+
+ if (primitive instanceof ASN1Sequence)
+ {
+ return (ASN1Sequence)primitive;
+ }
+ }
+
+ throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * Return an ASN1 sequence from a tagged object. There is a special
+ * case here, if an object appears to have been explicitly tagged on
+ * reading but we were expecting it to be implicitly tagged in the
+ * normal course of events it indicates that we lost the surrounding
+ * sequence - so we need to add it back (this will happen if the tagged
+ * object is a sequence that contains other sequences). If you are
+ * dealing with implicitly tagged sequences you really <b>should</b>
+ * be using this method.
+ *
+ * @param obj the tagged object.
+ * @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 ASN1Sequence getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ if (explicit)
+ {
+ if (!obj.isExplicit())
+ {
+ throw new IllegalArgumentException("object implicit - explicit expected.");
+ }
+
+ return ASN1Sequence.getInstance(obj.getObject().toASN1Primitive());
+ }
+ else
+ {
+ //
+ // constructed object which appears to be explicitly tagged
+ // when it should be implicit means we have to add the
+ // surrounding sequence.
+ //
+ if (obj.isExplicit())
+ {
+ if (obj instanceof BERTaggedObject)
+ {
+ return new BERSequence(obj.getObject());
+ }
+ else
+ {
+ return new DLSequence(obj.getObject());
+ }
+ }
+ else
+ {
+ if (obj.getObject() instanceof ASN1Sequence)
+ {
+ return (ASN1Sequence)obj.getObject();
+ }
+ }
+ }
+
+ throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * create an empty sequence
+ */
+ protected ASN1Sequence()
+ {
+ }
+
+ /**
+ * create a sequence containing one object
+ */
+ protected ASN1Sequence(
+ ASN1Encodable obj)
+ {
+ seq.addElement(obj);
+ }
+
+ /**
+ * create a sequence containing a vector of objects.
+ */
+ protected ASN1Sequence(
+ ASN1EncodableVector v)
+ {
+ for (int i = 0; i != v.size(); i++)
+ {
+ seq.addElement(v.get(i));
+ }
+ }
+
+ /**
+ * create a sequence containing a vector of objects.
+ */
+ protected ASN1Sequence(
+ ASN1Encodable[] array)
+ {
+ for (int i = 0; i != array.length; i++)
+ {
+ seq.addElement(array[i]);
+ }
+ }
+
+ public ASN1Encodable[] toArray()
+ {
+ ASN1Encodable[] values = new ASN1Encodable[this.size()];
+
+ for (int i = 0; i != this.size(); i++)
+ {
+ values[i] = this.getObjectAt(i);
+ }
+
+ return values;
+ }
+
+ public Enumeration getObjects()
+ {
+ return seq.elements();
+ }
+
+ public ASN1SequenceParser parser()
+ {
+ final ASN1Sequence outer = this;
+
+ return new ASN1SequenceParser()
+ {
+ private final int max = size();
+
+ private int index;
+
+ public ASN1Encodable readObject() throws IOException
+ {
+ if (index == max)
+ {
+ return null;
+ }
+
+ ASN1Encodable obj = getObjectAt(index++);
+ if (obj instanceof ASN1Sequence)
+ {
+ return ((ASN1Sequence)obj).parser();
+ }
+ if (obj instanceof ASN1Set)
+ {
+ return ((ASN1Set)obj).parser();
+ }
+
+ return obj;
+ }
+
+ public ASN1Primitive getLoadedObject()
+ {
+ return outer;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return outer;
+ }
+ };
+ }
+
+ /**
+ * return the object at the sequence position indicated by index.
+ *
+ * @param index the sequence number (starting at zero) of the object
+ * @return the object at the sequence position indicated by index.
+ */
+ public ASN1Encodable getObjectAt(
+ int index)
+ {
+ return (ASN1Encodable)seq.elementAt(index);
+ }
+
+ /**
+ * return the number of objects in this sequence.
+ *
+ * @return the number of objects in this sequence.
+ */
+ public int size()
+ {
+ return seq.size();
+ }
+
+ public int hashCode()
+ {
+ Enumeration e = this.getObjects();
+ int hashCode = size();
+
+ while (e.hasMoreElements())
+ {
+ Object o = getNext(e);
+ hashCode *= 17;
+
+ hashCode ^= o.hashCode();
+ }
+
+ return hashCode;
+ }
+
+ boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof ASN1Sequence))
+ {
+ return false;
+ }
+
+ ASN1Sequence other = (ASN1Sequence)o;
+
+ if (this.size() != other.size())
+ {
+ return false;
+ }
+
+ Enumeration s1 = this.getObjects();
+ Enumeration s2 = other.getObjects();
+
+ while (s1.hasMoreElements())
+ {
+ ASN1Encodable obj1 = getNext(s1);
+ ASN1Encodable obj2 = getNext(s2);
+
+ ASN1Primitive o1 = obj1.toASN1Primitive();
+ ASN1Primitive o2 = obj2.toASN1Primitive();
+
+ if (o1 == o2 || o1.equals(o2))
+ {
+ continue;
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ private ASN1Encodable getNext(Enumeration e)
+ {
+ ASN1Encodable encObj = (ASN1Encodable)e.nextElement();
+
+ return encObj;
+ }
+
+ ASN1Primitive toDERObject()
+ {
+ ASN1Sequence derSeq = new DERSequence();
+
+ derSeq.seq = this.seq;
+
+ return derSeq;
+ }
+
+ ASN1Primitive toDLObject()
+ {
+ ASN1Sequence dlSeq = new DLSequence();
+
+ dlSeq.seq = this.seq;
+
+ return dlSeq;
+ }
+
+ boolean isConstructed()
+ {
+ return true;
+ }
+
+ abstract void encode(ASN1OutputStream out)
+ throws IOException;
+
+ public String toString()
+ {
+ return seq.toString();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1SequenceParser.java b/core/src/main/java/org/bouncycastle/asn1/ASN1SequenceParser.java
new file mode 100644
index 00000000..441f1502
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1SequenceParser.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public interface ASN1SequenceParser
+ extends ASN1Encodable, InMemoryRepresentable
+{
+ ASN1Encodable readObject()
+ throws IOException;
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1Set.java b/core/src/main/java/org/bouncycastle/asn1/ASN1Set.java
new file mode 100644
index 00000000..f1ac6c7d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1Set.java
@@ -0,0 +1,460 @@
+package org.bouncycastle.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Vector;
+
+abstract public class ASN1Set
+ extends ASN1Primitive
+{
+ private Vector set = new Vector();
+ private boolean isSorted = false;
+
+ /**
+ * return an ASN1Set from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static ASN1Set getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof ASN1Set)
+ {
+ return (ASN1Set)obj;
+ }
+ else if (obj instanceof ASN1SetParser)
+ {
+ return ASN1Set.getInstance(((ASN1SetParser)obj).toASN1Primitive());
+ }
+ else if (obj instanceof byte[])
+ {
+ try
+ {
+ return ASN1Set.getInstance(ASN1Primitive.fromByteArray((byte[])obj));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("failed to construct set from byte[]: " + e.getMessage());
+ }
+ }
+ else if (obj instanceof ASN1Encodable)
+ {
+ ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+
+ if (primitive instanceof ASN1Set)
+ {
+ return (ASN1Set)primitive;
+ }
+ }
+
+ throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * Return an ASN1 set from a tagged object. There is a special
+ * case here, if an object appears to have been explicitly tagged on
+ * reading but we were expecting it to be implicitly tagged in the
+ * normal course of events it indicates that we lost the surrounding
+ * set - so we need to add it back (this will happen if the tagged
+ * object is a sequence that contains other sequences). If you are
+ * dealing with implicitly tagged sets you really <b>should</b>
+ * be using this method.
+ *
+ * @param obj the tagged object.
+ * @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 ASN1Set getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ if (explicit)
+ {
+ if (!obj.isExplicit())
+ {
+ throw new IllegalArgumentException("object implicit - explicit expected.");
+ }
+
+ return (ASN1Set)obj.getObject();
+ }
+ else
+ {
+ //
+ // constructed object which appears to be explicitly tagged
+ // and it's really implicit means we have to add the
+ // surrounding set.
+ //
+ if (obj.isExplicit())
+ {
+ if (obj instanceof BERTaggedObject)
+ {
+ return new BERSet(obj.getObject());
+ }
+ else
+ {
+ return new DLSet(obj.getObject());
+ }
+ }
+ else
+ {
+ if (obj.getObject() instanceof ASN1Set)
+ {
+ return (ASN1Set)obj.getObject();
+ }
+
+ //
+ // in this case the parser returns a sequence, convert it
+ // into a set.
+ //
+ if (obj.getObject() instanceof ASN1Sequence)
+ {
+ ASN1Sequence s = (ASN1Sequence)obj.getObject();
+
+ if (obj instanceof BERTaggedObject)
+ {
+ return new BERSet(s.toArray());
+ }
+ else
+ {
+ return new DLSet(s.toArray());
+ }
+ }
+ }
+ }
+
+ throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
+ }
+
+ protected ASN1Set()
+ {
+ }
+
+ /**
+ * create a sequence containing one object
+ */
+ protected ASN1Set(
+ ASN1Encodable obj)
+ {
+ set.addElement(obj);
+ }
+
+ /**
+ * create a sequence containing a vector of objects.
+ */
+ protected ASN1Set(
+ ASN1EncodableVector v,
+ boolean doSort)
+ {
+ for (int i = 0; i != v.size(); i++)
+ {
+ set.addElement(v.get(i));
+ }
+
+ if (doSort)
+ {
+ this.sort();
+ }
+ }
+
+ /**
+ * create a sequence containing a vector of objects.
+ */
+ protected ASN1Set(
+ ASN1Encodable[] array,
+ boolean doSort)
+ {
+ for (int i = 0; i != array.length; i++)
+ {
+ set.addElement(array[i]);
+ }
+
+ if (doSort)
+ {
+ this.sort();
+ }
+ }
+
+ public Enumeration getObjects()
+ {
+ return set.elements();
+ }
+
+ /**
+ * return the object at the set position indicated by index.
+ *
+ * @param index the set number (starting at zero) of the object
+ * @return the object at the set position indicated by index.
+ */
+ public ASN1Encodable getObjectAt(
+ int index)
+ {
+ return (ASN1Encodable)set.elementAt(index);
+ }
+
+ /**
+ * return the number of objects in this set.
+ *
+ * @return the number of objects in this set.
+ */
+ public int size()
+ {
+ return set.size();
+ }
+
+ public ASN1Encodable[] toArray()
+ {
+ ASN1Encodable[] values = new ASN1Encodable[this.size()];
+
+ for (int i = 0; i != this.size(); i++)
+ {
+ values[i] = this.getObjectAt(i);
+ }
+
+ return values;
+ }
+
+ public ASN1SetParser parser()
+ {
+ final ASN1Set outer = this;
+
+ return new ASN1SetParser()
+ {
+ private final int max = size();
+
+ private int index;
+
+ public ASN1Encodable readObject() throws IOException
+ {
+ if (index == max)
+ {
+ return null;
+ }
+
+ ASN1Encodable obj = getObjectAt(index++);
+ if (obj instanceof ASN1Sequence)
+ {
+ return ((ASN1Sequence)obj).parser();
+ }
+ if (obj instanceof ASN1Set)
+ {
+ return ((ASN1Set)obj).parser();
+ }
+
+ return obj;
+ }
+
+ public ASN1Primitive getLoadedObject()
+ {
+ return outer;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return outer;
+ }
+ };
+ }
+
+ public int hashCode()
+ {
+ Enumeration e = this.getObjects();
+ int hashCode = size();
+
+ while (e.hasMoreElements())
+ {
+ Object o = getNext(e);
+ hashCode *= 17;
+
+ hashCode ^= o.hashCode();
+ }
+
+ return hashCode;
+ }
+
+ ASN1Primitive toDERObject()
+ {
+ if (isSorted)
+ {
+ ASN1Set derSet = new DERSet();
+
+ derSet.set = this.set;
+
+ return derSet;
+ }
+ else
+ {
+ Vector v = new Vector();
+
+ for (int i = 0; i != set.size(); i++)
+ {
+ v.addElement(set.elementAt(i));
+ }
+
+ ASN1Set derSet = new DERSet();
+
+ derSet.set = v;
+
+ derSet.sort();
+
+ return derSet;
+ }
+ }
+
+ ASN1Primitive toDLObject()
+ {
+ ASN1Set derSet = new DLSet();
+
+ derSet.set = this.set;
+
+ return derSet;
+ }
+
+ boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof ASN1Set))
+ {
+ return false;
+ }
+
+ ASN1Set other = (ASN1Set)o;
+
+ if (this.size() != other.size())
+ {
+ return false;
+ }
+
+ Enumeration s1 = this.getObjects();
+ Enumeration s2 = other.getObjects();
+
+ while (s1.hasMoreElements())
+ {
+ ASN1Encodable obj1 = getNext(s1);
+ ASN1Encodable obj2 = getNext(s2);
+
+ ASN1Primitive o1 = obj1.toASN1Primitive();
+ ASN1Primitive o2 = obj2.toASN1Primitive();
+
+ if (o1 == o2 || o1.equals(o2))
+ {
+ continue;
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ private ASN1Encodable getNext(Enumeration e)
+ {
+ ASN1Encodable encObj = (ASN1Encodable)e.nextElement();
+
+ // unfortunately null was allowed as a substitute for DER null
+ if (encObj == null)
+ {
+ return DERNull.INSTANCE;
+ }
+
+ return encObj;
+ }
+
+ /**
+ * return true if a <= b (arrays are assumed padded with zeros).
+ */
+ private boolean lessThanOrEqual(
+ byte[] a,
+ byte[] b)
+ {
+ int len = Math.min(a.length, b.length);
+ for (int i = 0; i != len; ++i)
+ {
+ if (a[i] != b[i])
+ {
+ return (a[i] & 0xff) < (b[i] & 0xff);
+ }
+ }
+ return len == a.length;
+ }
+
+ private byte[] getEncoded(
+ ASN1Encodable obj)
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ try
+ {
+ aOut.writeObject(obj);
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("cannot encode object added to SET");
+ }
+
+ return bOut.toByteArray();
+ }
+
+ protected void sort()
+ {
+ if (!isSorted)
+ {
+ isSorted = true;
+ if (set.size() > 1)
+ {
+ boolean swapped = true;
+ int lastSwap = set.size() - 1;
+
+ while (swapped)
+ {
+ int index = 0;
+ int swapIndex = 0;
+ byte[] a = getEncoded((ASN1Encodable)set.elementAt(0));
+
+ swapped = false;
+
+ while (index != lastSwap)
+ {
+ byte[] b = getEncoded((ASN1Encodable)set.elementAt(index + 1));
+
+ if (lessThanOrEqual(a, b))
+ {
+ a = b;
+ }
+ else
+ {
+ Object o = set.elementAt(index);
+
+ set.setElementAt(set.elementAt(index + 1), index);
+ set.setElementAt(o, index + 1);
+
+ swapped = true;
+ swapIndex = index;
+ }
+
+ index++;
+ }
+
+ lastSwap = swapIndex;
+ }
+ }
+ }
+ }
+
+ boolean isConstructed()
+ {
+ return true;
+ }
+
+ abstract void encode(ASN1OutputStream out)
+ throws IOException;
+
+ public String toString()
+ {
+ return set.toString();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1SetParser.java b/core/src/main/java/org/bouncycastle/asn1/ASN1SetParser.java
new file mode 100644
index 00000000..e025535c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1SetParser.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public interface ASN1SetParser
+ extends ASN1Encodable, InMemoryRepresentable
+{
+ public ASN1Encodable readObject()
+ throws IOException;
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1StreamParser.java b/core/src/main/java/org/bouncycastle/asn1/ASN1StreamParser.java
new file mode 100644
index 00000000..420fa347
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1StreamParser.java
@@ -0,0 +1,247 @@
+package org.bouncycastle.asn1;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ASN1StreamParser
+{
+ private final InputStream _in;
+ private final int _limit;
+ private final byte[][] tmpBuffers;
+
+ public ASN1StreamParser(
+ InputStream in)
+ {
+ this(in, StreamUtil.findLimit(in));
+ }
+
+ public ASN1StreamParser(
+ InputStream in,
+ int limit)
+ {
+ this._in = in;
+ this._limit = limit;
+
+ this.tmpBuffers = new byte[11][];
+ }
+
+ public ASN1StreamParser(
+ byte[] encoding)
+ {
+ this(new ByteArrayInputStream(encoding), encoding.length);
+ }
+
+ ASN1Encodable readIndef(int tagValue) throws IOException
+ {
+ // Note: INDEF => CONSTRUCTED
+
+ // TODO There are other tags that may be constructed (e.g. BIT_STRING)
+ switch (tagValue)
+ {
+ case BERTags.EXTERNAL:
+ return new DERExternalParser(this);
+ case BERTags.OCTET_STRING:
+ return new BEROctetStringParser(this);
+ case BERTags.SEQUENCE:
+ return new BERSequenceParser(this);
+ case BERTags.SET:
+ return new BERSetParser(this);
+ default:
+ throw new ASN1Exception("unknown BER object encountered: 0x" + Integer.toHexString(tagValue));
+ }
+ }
+
+ ASN1Encodable readImplicit(boolean constructed, int tag) throws IOException
+ {
+ if (_in instanceof IndefiniteLengthInputStream)
+ {
+ if (!constructed)
+ {
+ throw new IOException("indefinite length primitive encoding encountered");
+ }
+
+ return readIndef(tag);
+ }
+
+ if (constructed)
+ {
+ switch (tag)
+ {
+ case BERTags.SET:
+ return new DERSetParser(this);
+ case BERTags.SEQUENCE:
+ return new DERSequenceParser(this);
+ case BERTags.OCTET_STRING:
+ return new BEROctetStringParser(this);
+ }
+ }
+ else
+ {
+ switch (tag)
+ {
+ case BERTags.SET:
+ throw new ASN1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)");
+ case BERTags.SEQUENCE:
+ throw new ASN1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)");
+ case BERTags.OCTET_STRING:
+ return new DEROctetStringParser((DefiniteLengthInputStream)_in);
+ }
+ }
+
+ // TODO ASN1Exception
+ throw new RuntimeException("implicit tagging not implemented");
+ }
+
+ ASN1Primitive readTaggedObject(boolean constructed, int tag) throws IOException
+ {
+ if (!constructed)
+ {
+ // Note: !CONSTRUCTED => IMPLICIT
+ DefiniteLengthInputStream defIn = (DefiniteLengthInputStream)_in;
+ return new DERTaggedObject(false, tag, new DEROctetString(defIn.toByteArray()));
+ }
+
+ ASN1EncodableVector v = readVector();
+
+ if (_in instanceof IndefiniteLengthInputStream)
+ {
+ return v.size() == 1
+ ? new BERTaggedObject(true, tag, v.get(0))
+ : new BERTaggedObject(false, tag, BERFactory.createSequence(v));
+ }
+
+ return v.size() == 1
+ ? new DERTaggedObject(true, tag, v.get(0))
+ : new DERTaggedObject(false, tag, DERFactory.createSequence(v));
+ }
+
+ public ASN1Encodable readObject()
+ throws IOException
+ {
+ int tag = _in.read();
+ if (tag == -1)
+ {
+ return null;
+ }
+
+ //
+ // turn of looking for "00" while we resolve the tag
+ //
+ set00Check(false);
+
+ //
+ // calculate tag number
+ //
+ int tagNo = ASN1InputStream.readTagNumber(_in, tag);
+
+ boolean isConstructed = (tag & BERTags.CONSTRUCTED) != 0;
+
+ //
+ // calculate length
+ //
+ int length = ASN1InputStream.readLength(_in, _limit);
+
+ if (length < 0) // indefinite length method
+ {
+ if (!isConstructed)
+ {
+ throw new IOException("indefinite length primitive encoding encountered");
+ }
+
+ IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in, _limit);
+ ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit);
+
+ if ((tag & BERTags.APPLICATION) != 0)
+ {
+ return new BERApplicationSpecificParser(tagNo, sp);
+ }
+
+ if ((tag & BERTags.TAGGED) != 0)
+ {
+ return new BERTaggedObjectParser(true, tagNo, sp);
+ }
+
+ return sp.readIndef(tagNo);
+ }
+ else
+ {
+ DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length);
+
+ if ((tag & BERTags.APPLICATION) != 0)
+ {
+ return new DERApplicationSpecific(isConstructed, tagNo, defIn.toByteArray());
+ }
+
+ if ((tag & BERTags.TAGGED) != 0)
+ {
+ return new BERTaggedObjectParser(isConstructed, tagNo, new ASN1StreamParser(defIn));
+ }
+
+ if (isConstructed)
+ {
+ // TODO There are other tags that may be constructed (e.g. BIT_STRING)
+ switch (tagNo)
+ {
+ case BERTags.OCTET_STRING:
+ //
+ // yes, people actually do this...
+ //
+ return new BEROctetStringParser(new ASN1StreamParser(defIn));
+ case BERTags.SEQUENCE:
+ return new DERSequenceParser(new ASN1StreamParser(defIn));
+ case BERTags.SET:
+ return new DERSetParser(new ASN1StreamParser(defIn));
+ case BERTags.EXTERNAL:
+ return new DERExternalParser(new ASN1StreamParser(defIn));
+ default:
+ throw new IOException("unknown tag " + tagNo + " encountered");
+ }
+ }
+
+ // Some primitive encodings can be handled by parsers too...
+ switch (tagNo)
+ {
+ case BERTags.OCTET_STRING:
+ return new DEROctetStringParser(defIn);
+ }
+
+ try
+ {
+ return ASN1InputStream.createPrimitiveDERObject(tagNo, defIn, tmpBuffers);
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new ASN1Exception("corrupted stream detected", e);
+ }
+ }
+ }
+
+ private void set00Check(boolean enabled)
+ {
+ if (_in instanceof IndefiniteLengthInputStream)
+ {
+ ((IndefiniteLengthInputStream)_in).setEofOn00(enabled);
+ }
+ }
+
+ ASN1EncodableVector readVector() throws IOException
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ ASN1Encodable obj;
+ while ((obj = readObject()) != null)
+ {
+ if (obj instanceof InMemoryRepresentable)
+ {
+ v.add(((InMemoryRepresentable)obj).getLoadedObject());
+ }
+ else
+ {
+ v.add(obj.toASN1Primitive());
+ }
+ }
+
+ return v;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1String.java b/core/src/main/java/org/bouncycastle/asn1/ASN1String.java
new file mode 100644
index 00000000..fde4e239
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1String.java
@@ -0,0 +1,6 @@
+package org.bouncycastle.asn1;
+
+public interface ASN1String
+{
+ public String getString();
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java b/core/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java
new file mode 100644
index 00000000..fb1e2442
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java
@@ -0,0 +1,236 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+/**
+ * ASN.1 TaggedObject - in ASN.1 notation this is any object preceded by
+ * a [n] where n is some number - these are assumed to follow the construction
+ * rules (as with sequences).
+ */
+public abstract class ASN1TaggedObject
+ extends ASN1Primitive
+ implements ASN1TaggedObjectParser
+{
+ int tagNo;
+ boolean empty = false;
+ boolean explicit = true;
+ ASN1Encodable obj = null;
+
+ static public ASN1TaggedObject getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ if (explicit)
+ {
+ return (ASN1TaggedObject)obj.getObject();
+ }
+
+ throw new IllegalArgumentException("implicitly tagged tagged object");
+ }
+
+ static public ASN1TaggedObject getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof ASN1TaggedObject)
+ {
+ return (ASN1TaggedObject)obj;
+ }
+ else if (obj instanceof byte[])
+ {
+ try
+ {
+ return ASN1TaggedObject.getInstance(fromByteArray((byte[])obj));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("failed to construct tagged object from byte[]: " + e.getMessage());
+ }
+ }
+
+ throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * Create a tagged object with the style given by the value of explicit.
+ * <p>
+ * If the object implements ASN1Choice the tag style will always be changed
+ * to explicit in accordance with the ASN.1 encoding rules.
+ * </p>
+ * @param explicit true if the object is explicitly tagged.
+ * @param tagNo the tag number for this object.
+ * @param obj the tagged object.
+ */
+ public ASN1TaggedObject(
+ boolean explicit,
+ int tagNo,
+ ASN1Encodable obj)
+ {
+ if (obj instanceof ASN1Choice)
+ {
+ this.explicit = true;
+ }
+ else
+ {
+ this.explicit = explicit;
+ }
+
+ this.tagNo = tagNo;
+
+ if (this.explicit)
+ {
+ this.obj = obj;
+ }
+ else
+ {
+ ASN1Primitive prim = obj.toASN1Primitive();
+
+ if (prim instanceof ASN1Set)
+ {
+ ASN1Set s = null;
+ }
+
+ this.obj = obj;
+ }
+ }
+
+ boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof ASN1TaggedObject))
+ {
+ return false;
+ }
+
+ ASN1TaggedObject other = (ASN1TaggedObject)o;
+
+ if (tagNo != other.tagNo || empty != other.empty || explicit != other.explicit)
+ {
+ return false;
+ }
+
+ if(obj == null)
+ {
+ if (other.obj != null)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (!(obj.toASN1Primitive().equals(other.obj.toASN1Primitive())))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public int hashCode()
+ {
+ int code = tagNo;
+
+ // TODO: actually this is wrong - the problem is that a re-encoded
+ // object may end up with a different hashCode due to implicit
+ // tagging. As implicit tagging is ambiguous if a sequence is involved
+ // it seems the only correct method for both equals and hashCode is to
+ // compare the encodings...
+ if (obj != null)
+ {
+ code ^= obj.hashCode();
+ }
+
+ return code;
+ }
+
+ public int getTagNo()
+ {
+ return tagNo;
+ }
+
+ /**
+ * return whether or not the object may be explicitly tagged.
+ * <p>
+ * Note: if the object has been read from an input stream, the only
+ * time you can be sure if isExplicit is returning the true state of
+ * affairs is if it returns false. An implicitly tagged object may appear
+ * to be explicitly tagged, so you need to understand the context under
+ * which the reading was done as well, see getObject below.
+ */
+ public boolean isExplicit()
+ {
+ return explicit;
+ }
+
+ public boolean isEmpty()
+ {
+ return empty;
+ }
+
+ /**
+ * return whatever was following the tag.
+ * <p>
+ * Note: tagged objects are generally context dependent if you're
+ * trying to extract a tagged object you should be going via the
+ * appropriate getInstance method.
+ */
+ public ASN1Primitive getObject()
+ {
+ if (obj != null)
+ {
+ return obj.toASN1Primitive();
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the object held in this tagged object as a parser assuming it has
+ * the type of the passed in tag. If the object doesn't have a parser
+ * associated with it, the base object is returned.
+ */
+ public ASN1Encodable getObjectParser(
+ int tag,
+ boolean isExplicit)
+ {
+ switch (tag)
+ {
+ case BERTags.SET:
+ return ASN1Set.getInstance(this, isExplicit).parser();
+ case BERTags.SEQUENCE:
+ return ASN1Sequence.getInstance(this, isExplicit).parser();
+ case BERTags.OCTET_STRING:
+ return ASN1OctetString.getInstance(this, isExplicit).parser();
+ }
+
+ if (isExplicit)
+ {
+ return getObject();
+ }
+
+ throw new RuntimeException("implicit tagging not implemented for tag: " + tag);
+ }
+
+ public ASN1Primitive getLoadedObject()
+ {
+ return this.toASN1Primitive();
+ }
+
+ ASN1Primitive toDERObject()
+ {
+ return new DERTaggedObject(explicit, tagNo, obj);
+ }
+
+ ASN1Primitive toDLObject()
+ {
+ return new DLTaggedObject(explicit, tagNo, obj);
+ }
+
+ abstract void encode(ASN1OutputStream out)
+ throws IOException;
+
+ public String toString()
+ {
+ return "[" + tagNo + "]" + obj;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1TaggedObjectParser.java b/core/src/main/java/org/bouncycastle/asn1/ASN1TaggedObjectParser.java
new file mode 100644
index 00000000..a681dc94
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1TaggedObjectParser.java
@@ -0,0 +1,12 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public interface ASN1TaggedObjectParser
+ extends ASN1Encodable, InMemoryRepresentable
+{
+ public int getTagNo();
+
+ public ASN1Encodable getObjectParser(int tag, boolean isExplicit)
+ throws IOException;
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1UTCTime.java b/core/src/main/java/org/bouncycastle/asn1/ASN1UTCTime.java
new file mode 100644
index 00000000..d3816f22
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ASN1UTCTime.java
@@ -0,0 +1,22 @@
+package org.bouncycastle.asn1;
+
+import java.util.Date;
+
+public class ASN1UTCTime
+ extends DERUTCTime
+{
+ ASN1UTCTime(byte[] bytes)
+ {
+ super(bytes);
+ }
+
+ public ASN1UTCTime(Date time)
+ {
+ super(time);
+ }
+
+ public ASN1UTCTime(String time)
+ {
+ super(time);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/BERApplicationSpecific.java b/core/src/main/java/org/bouncycastle/asn1/BERApplicationSpecific.java
new file mode 100644
index 00000000..8bc8a4eb
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/BERApplicationSpecific.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.asn1;
+
+public class BERApplicationSpecific
+ extends DERApplicationSpecific
+{
+ public BERApplicationSpecific(int tagNo, ASN1EncodableVector vec)
+ {
+ super(tagNo, vec);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/BERApplicationSpecificParser.java b/core/src/main/java/org/bouncycastle/asn1/BERApplicationSpecificParser.java
new file mode 100644
index 00000000..63bd9f3d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/BERApplicationSpecificParser.java
@@ -0,0 +1,41 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public class BERApplicationSpecificParser
+ implements ASN1ApplicationSpecificParser
+{
+ private final int tag;
+ private final ASN1StreamParser parser;
+
+ BERApplicationSpecificParser(int tag, ASN1StreamParser parser)
+ {
+ this.tag = tag;
+ this.parser = parser;
+ }
+
+ public ASN1Encodable readObject()
+ throws IOException
+ {
+ return parser.readObject();
+ }
+
+ public ASN1Primitive getLoadedObject()
+ throws IOException
+ {
+ return new BERApplicationSpecific(tag, parser.readVector());
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ try
+ {
+ return getLoadedObject();
+ }
+ catch (IOException e)
+ {
+ throw new ASN1ParsingException(e.getMessage(), e);
+ }
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/BERConstructedOctetString.java b/core/src/main/java/org/bouncycastle/asn1/BERConstructedOctetString.java
new file mode 100644
index 00000000..cad6e42a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/BERConstructedOctetString.java
@@ -0,0 +1,144 @@
+package org.bouncycastle.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * @deprecated use BEROctetString
+ */
+public class BERConstructedOctetString
+ extends BEROctetString
+{
+ private static final int MAX_LENGTH = 1000;
+
+ /**
+ * convert a vector of octet strings into a single byte string
+ */
+ static private byte[] toBytes(
+ Vector octs)
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ for (int i = 0; i != octs.size(); i++)
+ {
+ try
+ {
+ DEROctetString o = (DEROctetString)octs.elementAt(i);
+
+ bOut.write(o.getOctets());
+ }
+ catch (ClassCastException e)
+ {
+ throw new IllegalArgumentException(octs.elementAt(i).getClass().getName() + " found in input should only contain DEROctetString");
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("exception converting octets " + e.toString());
+ }
+ }
+
+ return bOut.toByteArray();
+ }
+
+ private Vector octs;
+
+ /**
+ * @param string the octets making up the octet string.
+ */
+ public BERConstructedOctetString(
+ byte[] string)
+ {
+ super(string);
+ }
+
+ public BERConstructedOctetString(
+ Vector octs)
+ {
+ super(toBytes(octs));
+
+ this.octs = octs;
+ }
+
+ public BERConstructedOctetString(
+ ASN1Primitive obj)
+ {
+ super(toByteArray(obj));
+ }
+
+ private static byte[] toByteArray(ASN1Primitive obj)
+ {
+ try
+ {
+ return obj.getEncoded();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("Unable to encode object");
+ }
+ }
+
+ public BERConstructedOctetString(
+ ASN1Encodable obj)
+ {
+ this(obj.toASN1Primitive());
+ }
+
+ public byte[] getOctets()
+ {
+ return string;
+ }
+
+ /**
+ * return the DER octets that make up this string.
+ */
+ public Enumeration getObjects()
+ {
+ if (octs == null)
+ {
+ return generateOcts().elements();
+ }
+
+ return octs.elements();
+ }
+
+ private Vector generateOcts()
+ {
+ Vector vec = new Vector();
+ for (int i = 0; i < string.length; i += MAX_LENGTH)
+ {
+ int end;
+
+ if (i + MAX_LENGTH > string.length)
+ {
+ end = string.length;
+ }
+ else
+ {
+ end = i + MAX_LENGTH;
+ }
+
+ byte[] nStr = new byte[end - i];
+
+ System.arraycopy(string, i, nStr, 0, nStr.length);
+
+ vec.addElement(new DEROctetString(nStr));
+ }
+
+ return vec;
+ }
+
+ public static BEROctetString fromSequence(ASN1Sequence seq)
+ {
+ Vector v = new Vector();
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ v.addElement(e.nextElement());
+ }
+
+ return new BERConstructedOctetString(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/BERFactory.java b/core/src/main/java/org/bouncycastle/asn1/BERFactory.java
new file mode 100644
index 00000000..023be0b6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/BERFactory.java
@@ -0,0 +1,17 @@
+package org.bouncycastle.asn1;
+
+class BERFactory
+{
+ static final BERSequence EMPTY_SEQUENCE = new BERSequence();
+ static final BERSet EMPTY_SET = new BERSet();
+
+ static BERSequence createSequence(ASN1EncodableVector v)
+ {
+ return v.size() < 1 ? EMPTY_SEQUENCE : new BERSequence(v);
+ }
+
+ static BERSet createSet(ASN1EncodableVector v)
+ {
+ return v.size() < 1 ? EMPTY_SET : new BERSet(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/BERGenerator.java b/core/src/main/java/org/bouncycastle/asn1/BERGenerator.java
new file mode 100644
index 00000000..ef7f9a38
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/BERGenerator.java
@@ -0,0 +1,100 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class BERGenerator
+ extends ASN1Generator
+{
+ private boolean _tagged = false;
+ private boolean _isExplicit;
+ private int _tagNo;
+
+ protected BERGenerator(
+ OutputStream out)
+ {
+ super(out);
+ }
+
+ public BERGenerator(
+ OutputStream out,
+ int tagNo,
+ boolean isExplicit)
+ {
+ super(out);
+
+ _tagged = true;
+ _isExplicit = isExplicit;
+ _tagNo = tagNo;
+ }
+
+ public OutputStream getRawOutputStream()
+ {
+ return _out;
+ }
+
+ private void writeHdr(
+ int tag)
+ throws IOException
+ {
+ _out.write(tag);
+ _out.write(0x80);
+ }
+
+ protected void writeBERHeader(
+ int tag)
+ throws IOException
+ {
+ if (_tagged)
+ {
+ int tagNum = _tagNo | BERTags.TAGGED;
+
+ if (_isExplicit)
+ {
+ writeHdr(tagNum | BERTags.CONSTRUCTED);
+ writeHdr(tag);
+ }
+ else
+ {
+ if ((tag & BERTags.CONSTRUCTED) != 0)
+ {
+ writeHdr(tagNum | BERTags.CONSTRUCTED);
+ }
+ else
+ {
+ writeHdr(tagNum);
+ }
+ }
+ }
+ else
+ {
+ writeHdr(tag);
+ }
+ }
+
+ protected void writeBERBody(
+ InputStream contentStream)
+ throws IOException
+ {
+ int ch;
+
+ while ((ch = contentStream.read()) >= 0)
+ {
+ _out.write(ch);
+ }
+ }
+
+ protected void writeBEREnd()
+ throws IOException
+ {
+ _out.write(0x00);
+ _out.write(0x00);
+
+ if (_tagged && _isExplicit) // write extra end for tag header
+ {
+ _out.write(0x00);
+ _out.write(0x00);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/BEROctetString.java b/core/src/main/java/org/bouncycastle/asn1/BEROctetString.java
new file mode 100644
index 00000000..bc1ed448
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/BEROctetString.java
@@ -0,0 +1,168 @@
+package org.bouncycastle.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Vector;
+
+public class BEROctetString
+ extends ASN1OctetString
+{
+ private static final int MAX_LENGTH = 1000;
+
+ private ASN1OctetString[] octs;
+
+ /**
+ * convert a vector of octet strings into a single byte string
+ */
+ static private byte[] toBytes(
+ ASN1OctetString[] octs)
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ for (int i = 0; i != octs.length; i++)
+ {
+ try
+ {
+ DEROctetString o = (DEROctetString)octs[i];
+
+ bOut.write(o.getOctets());
+ }
+ catch (ClassCastException e)
+ {
+ throw new IllegalArgumentException(octs[i].getClass().getName() + " found in input should only contain DEROctetString");
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("exception converting octets " + e.toString());
+ }
+ }
+
+ return bOut.toByteArray();
+ }
+
+ /**
+ * @param string the octets making up the octet string.
+ */
+ public BEROctetString(
+ byte[] string)
+ {
+ super(string);
+ }
+
+ public BEROctetString(
+ ASN1OctetString[] octs)
+ {
+ super(toBytes(octs));
+
+ this.octs = octs;
+ }
+
+ public byte[] getOctets()
+ {
+ return string;
+ }
+
+ /**
+ * return the DER octets that make up this string.
+ */
+ public Enumeration getObjects()
+ {
+ if (octs == null)
+ {
+ return generateOcts().elements();
+ }
+
+ return new Enumeration()
+ {
+ int counter = 0;
+
+ public boolean hasMoreElements()
+ {
+ return counter < octs.length;
+ }
+
+ public Object nextElement()
+ {
+ return octs[counter++];
+ }
+ };
+ }
+
+ private Vector generateOcts()
+ {
+ Vector vec = new Vector();
+ for (int i = 0; i < string.length; i += MAX_LENGTH)
+ {
+ int end;
+
+ if (i + MAX_LENGTH > string.length)
+ {
+ end = string.length;
+ }
+ else
+ {
+ end = i + MAX_LENGTH;
+ }
+
+ byte[] nStr = new byte[end - i];
+
+ System.arraycopy(string, i, nStr, 0, nStr.length);
+
+ vec.addElement(new DEROctetString(nStr));
+ }
+
+ return vec;
+ }
+
+ boolean isConstructed()
+ {
+ return true;
+ }
+
+ int encodedLength()
+ throws IOException
+ {
+ int length = 0;
+ for (Enumeration e = getObjects(); e.hasMoreElements();)
+ {
+ length += ((ASN1Encodable)e.nextElement()).toASN1Primitive().encodedLength();
+ }
+
+ return 2 + length + 2;
+ }
+
+ public void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.write(BERTags.CONSTRUCTED | BERTags.OCTET_STRING);
+
+ out.write(0x80);
+
+ //
+ // write out the octet array
+ //
+ for (Enumeration e = getObjects(); e.hasMoreElements();)
+ {
+ out.writeObject((ASN1Encodable)e.nextElement());
+ }
+
+ out.write(0x00);
+ out.write(0x00);
+ }
+
+ static BEROctetString fromSequence(ASN1Sequence seq)
+ {
+ ASN1OctetString[] v = new ASN1OctetString[seq.size()];
+ Enumeration e = seq.getObjects();
+ int index = 0;
+
+ while (e.hasMoreElements())
+ {
+ v[index++] = (ASN1OctetString)e.nextElement();
+ }
+
+ return new BEROctetString(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/BEROctetStringGenerator.java b/core/src/main/java/org/bouncycastle/asn1/BEROctetStringGenerator.java
new file mode 100644
index 00000000..b8df94af
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/BEROctetStringGenerator.java
@@ -0,0 +1,102 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class BEROctetStringGenerator
+ extends BERGenerator
+{
+ public BEROctetStringGenerator(OutputStream out)
+ throws IOException
+ {
+ super(out);
+
+ writeBERHeader(BERTags.CONSTRUCTED | BERTags.OCTET_STRING);
+ }
+
+ public BEROctetStringGenerator(
+ OutputStream out,
+ int tagNo,
+ boolean isExplicit)
+ throws IOException
+ {
+ super(out, tagNo, isExplicit);
+
+ writeBERHeader(BERTags.CONSTRUCTED | BERTags.OCTET_STRING);
+ }
+
+ public OutputStream getOctetOutputStream()
+ {
+ return getOctetOutputStream(new byte[1000]); // limit for CER encoding.
+ }
+
+ public OutputStream getOctetOutputStream(
+ byte[] buf)
+ {
+ return new BufferedBEROctetStream(buf);
+ }
+
+ private class BufferedBEROctetStream
+ extends OutputStream
+ {
+ private byte[] _buf;
+ private int _off;
+ private DEROutputStream _derOut;
+
+ BufferedBEROctetStream(
+ byte[] buf)
+ {
+ _buf = buf;
+ _off = 0;
+ _derOut = new DEROutputStream(_out);
+ }
+
+ public void write(
+ int b)
+ throws IOException
+ {
+ _buf[_off++] = (byte)b;
+
+ if (_off == _buf.length)
+ {
+ DEROctetString.encode(_derOut, _buf);
+ _off = 0;
+ }
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException
+ {
+ while (len > 0)
+ {
+ int numToCopy = Math.min(len, _buf.length - _off);
+ System.arraycopy(b, off, _buf, _off, numToCopy);
+
+ _off += numToCopy;
+ if (_off < _buf.length)
+ {
+ break;
+ }
+
+ DEROctetString.encode(_derOut, _buf);
+ _off = 0;
+
+ off += numToCopy;
+ len -= numToCopy;
+ }
+ }
+
+ public void close()
+ throws IOException
+ {
+ if (_off != 0)
+ {
+ byte[] bytes = new byte[_off];
+ System.arraycopy(_buf, 0, bytes, 0, _off);
+
+ DEROctetString.encode(_derOut, bytes);
+ }
+
+ writeBEREnd();
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/BEROctetStringParser.java b/core/src/main/java/org/bouncycastle/asn1/BEROctetStringParser.java
new file mode 100644
index 00000000..1c7132e5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/BEROctetStringParser.java
@@ -0,0 +1,41 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.util.io.Streams;
+
+public class BEROctetStringParser
+ implements ASN1OctetStringParser
+{
+ private ASN1StreamParser _parser;
+
+ BEROctetStringParser(
+ ASN1StreamParser parser)
+ {
+ _parser = parser;
+ }
+
+ public InputStream getOctetStream()
+ {
+ return new ConstructedOctetStream(_parser);
+ }
+
+ public ASN1Primitive getLoadedObject()
+ throws IOException
+ {
+ return new BEROctetString(Streams.readAll(getOctetStream()));
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ try
+ {
+ return getLoadedObject();
+ }
+ catch (IOException e)
+ {
+ throw new ASN1ParsingException("IOException converting stream to byte array: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/BEROutputStream.java b/core/src/main/java/org/bouncycastle/asn1/BEROutputStream.java
new file mode 100644
index 00000000..7117d4fb
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/BEROutputStream.java
@@ -0,0 +1,36 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class BEROutputStream
+ extends DEROutputStream
+{
+ public BEROutputStream(
+ OutputStream os)
+ {
+ super(os);
+ }
+
+ public void writeObject(
+ Object obj)
+ throws IOException
+ {
+ if (obj == null)
+ {
+ writeNull();
+ }
+ else if (obj instanceof ASN1Primitive)
+ {
+ ((ASN1Primitive)obj).encode(this);
+ }
+ else if (obj instanceof ASN1Encodable)
+ {
+ ((ASN1Encodable)obj).toASN1Primitive().encode(this);
+ }
+ else
+ {
+ throw new IOException("object not BEREncodable");
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/BERSequence.java b/core/src/main/java/org/bouncycastle/asn1/BERSequence.java
new file mode 100644
index 00000000..aa44950f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/BERSequence.java
@@ -0,0 +1,73 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+public class BERSequence
+ extends ASN1Sequence
+{
+ /**
+ * create an empty sequence
+ */
+ public BERSequence()
+ {
+ }
+
+ /**
+ * create a sequence containing one object
+ */
+ public BERSequence(
+ ASN1Encodable obj)
+ {
+ super(obj);
+ }
+
+ /**
+ * create a sequence containing a vector of objects.
+ */
+ public BERSequence(
+ ASN1EncodableVector v)
+ {
+ super(v);
+ }
+
+ /**
+ * create a sequence containing an array of objects.
+ */
+ public BERSequence(
+ ASN1Encodable[] array)
+ {
+ super(array);
+ }
+
+ int encodedLength()
+ throws IOException
+ {
+ int length = 0;
+ for (Enumeration e = getObjects(); e.hasMoreElements();)
+ {
+ length += ((ASN1Encodable)e.nextElement()).toASN1Primitive().encodedLength();
+ }
+
+ return 2 + length + 2;
+ }
+
+ /*
+ */
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.write(BERTags.SEQUENCE | BERTags.CONSTRUCTED);
+ out.write(0x80);
+
+ Enumeration e = getObjects();
+ while (e.hasMoreElements())
+ {
+ out.writeObject((ASN1Encodable)e.nextElement());
+ }
+
+ out.write(0x00);
+ out.write(0x00);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/BERSequenceGenerator.java b/core/src/main/java/org/bouncycastle/asn1/BERSequenceGenerator.java
new file mode 100644
index 00000000..6e275651
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/BERSequenceGenerator.java
@@ -0,0 +1,41 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class BERSequenceGenerator
+ extends BERGenerator
+{
+ public BERSequenceGenerator(
+ OutputStream out)
+ throws IOException
+ {
+ super(out);
+
+ writeBERHeader(BERTags.CONSTRUCTED | BERTags.SEQUENCE);
+ }
+
+ public BERSequenceGenerator(
+ OutputStream out,
+ int tagNo,
+ boolean isExplicit)
+ throws IOException
+ {
+ super(out, tagNo, isExplicit);
+
+ writeBERHeader(BERTags.CONSTRUCTED | BERTags.SEQUENCE);
+ }
+
+ public void addObject(
+ ASN1Encodable object)
+ throws IOException
+ {
+ object.toASN1Primitive().encode(new BEROutputStream(_out));
+ }
+
+ public void close()
+ throws IOException
+ {
+ writeBEREnd();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/BERSequenceParser.java b/core/src/main/java/org/bouncycastle/asn1/BERSequenceParser.java
new file mode 100644
index 00000000..d5d43959
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/BERSequenceParser.java
@@ -0,0 +1,38 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public class BERSequenceParser
+ implements ASN1SequenceParser
+{
+ private ASN1StreamParser _parser;
+
+ BERSequenceParser(ASN1StreamParser parser)
+ {
+ this._parser = parser;
+ }
+
+ public ASN1Encodable readObject()
+ throws IOException
+ {
+ return _parser.readObject();
+ }
+
+ public ASN1Primitive getLoadedObject()
+ throws IOException
+ {
+ return new BERSequence(_parser.readVector());
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ try
+ {
+ return getLoadedObject();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/BERSet.java b/core/src/main/java/org/bouncycastle/asn1/BERSet.java
new file mode 100644
index 00000000..064d7786
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/BERSet.java
@@ -0,0 +1,73 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+public class BERSet
+ extends ASN1Set
+{
+ /**
+ * create an empty sequence
+ */
+ public BERSet()
+ {
+ }
+
+ /**
+ * @param obj - a single object that makes up the set.
+ */
+ public BERSet(
+ ASN1Encodable obj)
+ {
+ super(obj);
+ }
+
+ /**
+ * @param v - a vector of objects making up the set.
+ */
+ public BERSet(
+ ASN1EncodableVector v)
+ {
+ super(v, false);
+ }
+
+ /**
+ * create a set from an array of objects.
+ */
+ public BERSet(
+ ASN1Encodable[] a)
+ {
+ super(a, false);
+ }
+
+ int encodedLength()
+ throws IOException
+ {
+ int length = 0;
+ for (Enumeration e = getObjects(); e.hasMoreElements();)
+ {
+ length += ((ASN1Encodable)e.nextElement()).toASN1Primitive().encodedLength();
+ }
+
+ return 2 + length + 2;
+ }
+
+ /*
+ */
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.write(BERTags.SET | BERTags.CONSTRUCTED);
+ out.write(0x80);
+
+ Enumeration e = getObjects();
+ while (e.hasMoreElements())
+ {
+ out.writeObject((ASN1Encodable)e.nextElement());
+ }
+
+ out.write(0x00);
+ out.write(0x00);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/BERSetParser.java b/core/src/main/java/org/bouncycastle/asn1/BERSetParser.java
new file mode 100644
index 00000000..5a30f3cf
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/BERSetParser.java
@@ -0,0 +1,38 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public class BERSetParser
+ implements ASN1SetParser
+{
+ private ASN1StreamParser _parser;
+
+ BERSetParser(ASN1StreamParser parser)
+ {
+ this._parser = parser;
+ }
+
+ public ASN1Encodable readObject()
+ throws IOException
+ {
+ return _parser.readObject();
+ }
+
+ public ASN1Primitive getLoadedObject()
+ throws IOException
+ {
+ return new BERSet(_parser.readVector());
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ try
+ {
+ return getLoadedObject();
+ }
+ catch (IOException e)
+ {
+ throw new ASN1ParsingException(e.getMessage(), e);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/BERTaggedObject.java b/core/src/main/java/org/bouncycastle/asn1/BERTaggedObject.java
new file mode 100644
index 00000000..1af0a433
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/BERTaggedObject.java
@@ -0,0 +1,147 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+/**
+ * BER TaggedObject - in ASN.1 notation this is any object preceded by
+ * a [n] where n is some number - these are assumed to follow the construction
+ * rules (as with sequences).
+ */
+public class BERTaggedObject
+ extends ASN1TaggedObject
+{
+ /**
+ * @param tagNo the tag number for this object.
+ * @param obj the tagged object.
+ */
+ public BERTaggedObject(
+ int tagNo,
+ ASN1Encodable obj)
+ {
+ super(true, tagNo, obj);
+ }
+
+ /**
+ * @param explicit true if an explicitly tagged object.
+ * @param tagNo the tag number for this object.
+ * @param obj the tagged object.
+ */
+ public BERTaggedObject(
+ boolean explicit,
+ int tagNo,
+ ASN1Encodable obj)
+ {
+ super(explicit, tagNo, obj);
+ }
+
+ /**
+ * create an implicitly tagged object that contains a zero
+ * length sequence.
+ */
+ public BERTaggedObject(
+ int tagNo)
+ {
+ super(false, tagNo, new BERSequence());
+ }
+
+ boolean isConstructed()
+ {
+ if (!empty)
+ {
+ if (explicit)
+ {
+ return true;
+ }
+ else
+ {
+ ASN1Primitive primitive = obj.toASN1Primitive().toDERObject();
+
+ return primitive.isConstructed();
+ }
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+ int encodedLength()
+ throws IOException
+ {
+ if (!empty)
+ {
+ ASN1Primitive primitive = obj.toASN1Primitive();
+ int length = primitive.encodedLength();
+
+ if (explicit)
+ {
+ return StreamUtil.calculateTagLength(tagNo) + StreamUtil.calculateBodyLength(length) + length;
+ }
+ else
+ {
+ // header length already in calculation
+ length = length - 1;
+
+ return StreamUtil.calculateTagLength(tagNo) + length;
+ }
+ }
+ else
+ {
+ return StreamUtil.calculateTagLength(tagNo) + 1;
+ }
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.writeTag(BERTags.CONSTRUCTED | BERTags.TAGGED, tagNo);
+ out.write(0x80);
+
+ if (!empty)
+ {
+ if (!explicit)
+ {
+ Enumeration e;
+ if (obj instanceof ASN1OctetString)
+ {
+ if (obj instanceof BEROctetString)
+ {
+ e = ((BEROctetString)obj).getObjects();
+ }
+ else
+ {
+ ASN1OctetString octs = (ASN1OctetString)obj;
+ BEROctetString berO = new BEROctetString(octs.getOctets());
+ e = berO.getObjects();
+ }
+ }
+ else if (obj instanceof ASN1Sequence)
+ {
+ e = ((ASN1Sequence)obj).getObjects();
+ }
+ else if (obj instanceof ASN1Set)
+ {
+ e = ((ASN1Set)obj).getObjects();
+ }
+ else
+ {
+ throw new RuntimeException("not implemented: " + obj.getClass().getName());
+ }
+
+ while (e.hasMoreElements())
+ {
+ out.writeObject((ASN1Encodable)e.nextElement());
+ }
+ }
+ else
+ {
+ out.writeObject(obj);
+ }
+ }
+
+ out.write(0x00);
+ out.write(0x00);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/BERTaggedObjectParser.java b/core/src/main/java/org/bouncycastle/asn1/BERTaggedObjectParser.java
new file mode 100644
index 00000000..7cd334a4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/BERTaggedObjectParser.java
@@ -0,0 +1,66 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public class BERTaggedObjectParser
+ implements ASN1TaggedObjectParser
+{
+ private boolean _constructed;
+ private int _tagNumber;
+ private ASN1StreamParser _parser;
+
+ BERTaggedObjectParser(
+ boolean constructed,
+ int tagNumber,
+ ASN1StreamParser parser)
+ {
+ _constructed = constructed;
+ _tagNumber = tagNumber;
+ _parser = parser;
+ }
+
+ public boolean isConstructed()
+ {
+ return _constructed;
+ }
+
+ public int getTagNo()
+ {
+ return _tagNumber;
+ }
+
+ public ASN1Encodable getObjectParser(
+ int tag,
+ boolean isExplicit)
+ throws IOException
+ {
+ if (isExplicit)
+ {
+ if (!_constructed)
+ {
+ throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
+ }
+ return _parser.readObject();
+ }
+
+ return _parser.readImplicit(_constructed, tag);
+ }
+
+ public ASN1Primitive getLoadedObject()
+ throws IOException
+ {
+ return _parser.readTaggedObject(_constructed, _tagNumber);
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ try
+ {
+ return this.getLoadedObject();
+ }
+ catch (IOException e)
+ {
+ throw new ASN1ParsingException(e.getMessage());
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/BERTags.java b/core/src/main/java/org/bouncycastle/asn1/BERTags.java
new file mode 100644
index 00000000..7281a6a8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/BERTags.java
@@ -0,0 +1,36 @@
+package org.bouncycastle.asn1;
+
+public interface BERTags
+{
+ public static final int BOOLEAN = 0x01;
+ public static final int INTEGER = 0x02;
+ public static final int BIT_STRING = 0x03;
+ public static final int OCTET_STRING = 0x04;
+ public static final int NULL = 0x05;
+ public static final int OBJECT_IDENTIFIER = 0x06;
+ public static final int EXTERNAL = 0x08;
+ public static final int ENUMERATED = 0x0a;
+ public static final int SEQUENCE = 0x10;
+ public static final int SEQUENCE_OF = 0x10; // for completeness
+ public static final int SET = 0x11;
+ public static final int SET_OF = 0x11; // for completeness
+
+
+ public static final int NUMERIC_STRING = 0x12;
+ public static final int PRINTABLE_STRING = 0x13;
+ public static final int T61_STRING = 0x14;
+ public static final int VIDEOTEX_STRING = 0x15;
+ public static final int IA5_STRING = 0x16;
+ public static final int UTC_TIME = 0x17;
+ public static final int GENERALIZED_TIME = 0x18;
+ public static final int GRAPHIC_STRING = 0x19;
+ public static final int VISIBLE_STRING = 0x1a;
+ public static final int GENERAL_STRING = 0x1b;
+ public static final int UNIVERSAL_STRING = 0x1c;
+ public static final int BMP_STRING = 0x1e;
+ public static final int UTF8_STRING = 0x0c;
+
+ public static final int CONSTRUCTED = 0x20;
+ public static final int APPLICATION = 0x40;
+ public static final int TAGGED = 0x80;
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ConstructedOctetStream.java b/core/src/main/java/org/bouncycastle/asn1/ConstructedOctetStream.java
new file mode 100644
index 00000000..f247b11f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ConstructedOctetStream.java
@@ -0,0 +1,111 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+class ConstructedOctetStream
+ extends InputStream
+{
+ private final ASN1StreamParser _parser;
+
+ private boolean _first = true;
+ private InputStream _currentStream;
+
+ ConstructedOctetStream(
+ ASN1StreamParser parser)
+ {
+ _parser = parser;
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException
+ {
+ if (_currentStream == null)
+ {
+ if (!_first)
+ {
+ return -1;
+ }
+
+ ASN1OctetStringParser s = (ASN1OctetStringParser)_parser.readObject();
+
+ if (s == null)
+ {
+ return -1;
+ }
+
+ _first = false;
+ _currentStream = s.getOctetStream();
+ }
+
+ int totalRead = 0;
+
+ for (;;)
+ {
+ int numRead = _currentStream.read(b, off + totalRead, len - totalRead);
+
+ if (numRead >= 0)
+ {
+ totalRead += numRead;
+
+ if (totalRead == len)
+ {
+ return totalRead;
+ }
+ }
+ else
+ {
+ ASN1OctetStringParser aos = (ASN1OctetStringParser)_parser.readObject();
+
+ if (aos == null)
+ {
+ _currentStream = null;
+ return totalRead < 1 ? -1 : totalRead;
+ }
+
+ _currentStream = aos.getOctetStream();
+ }
+ }
+ }
+
+ public int read()
+ throws IOException
+ {
+ if (_currentStream == null)
+ {
+ if (!_first)
+ {
+ return -1;
+ }
+
+ ASN1OctetStringParser s = (ASN1OctetStringParser)_parser.readObject();
+
+ if (s == null)
+ {
+ return -1;
+ }
+
+ _first = false;
+ _currentStream = s.getOctetStream();
+ }
+
+ for (;;)
+ {
+ int b = _currentStream.read();
+
+ if (b >= 0)
+ {
+ return b;
+ }
+
+ ASN1OctetStringParser s = (ASN1OctetStringParser)_parser.readObject();
+
+ if (s == null)
+ {
+ _currentStream = null;
+ return -1;
+ }
+
+ _currentStream = s.getOctetStream();
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERApplicationSpecific.java b/core/src/main/java/org/bouncycastle/asn1/DERApplicationSpecific.java
new file mode 100644
index 00000000..5b592888
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERApplicationSpecific.java
@@ -0,0 +1,276 @@
+package org.bouncycastle.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Base class for an application specific object
+ */
+public class DERApplicationSpecific
+ extends ASN1Primitive
+{
+ private final boolean isConstructed;
+ private final int tag;
+ private final byte[] octets;
+
+ DERApplicationSpecific(
+ boolean isConstructed,
+ int tag,
+ byte[] octets)
+ {
+ this.isConstructed = isConstructed;
+ this.tag = tag;
+ this.octets = octets;
+ }
+
+ public DERApplicationSpecific(
+ int tag,
+ byte[] octets)
+ {
+ this(false, tag, octets);
+ }
+
+ public DERApplicationSpecific(
+ int tag,
+ ASN1Encodable object)
+ throws IOException
+ {
+ this(true, tag, object);
+ }
+
+ public DERApplicationSpecific(
+ boolean explicit,
+ int tag,
+ ASN1Encodable object)
+ throws IOException
+ {
+ ASN1Primitive primitive = object.toASN1Primitive();
+
+ byte[] data = primitive.getEncoded(ASN1Encoding.DER);
+
+ this.isConstructed = explicit || (primitive instanceof ASN1Set || primitive instanceof ASN1Sequence);
+ this.tag = tag;
+
+ if (explicit)
+ {
+ this.octets = data;
+ }
+ else
+ {
+ int lenBytes = getLengthOfHeader(data);
+ byte[] tmp = new byte[data.length - lenBytes];
+ System.arraycopy(data, lenBytes, tmp, 0, tmp.length);
+ this.octets = tmp;
+ }
+ }
+
+ public DERApplicationSpecific(int tagNo, ASN1EncodableVector vec)
+ {
+ this.tag = tagNo;
+ this.isConstructed = true;
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ for (int i = 0; i != vec.size(); i++)
+ {
+ try
+ {
+ bOut.write(((ASN1Object)vec.get(i)).getEncoded(ASN1Encoding.DER));
+ }
+ catch (IOException e)
+ {
+ throw new ASN1ParsingException("malformed object: " + e, e);
+ }
+ }
+ this.octets = bOut.toByteArray();
+ }
+
+ public static DERApplicationSpecific getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof DERApplicationSpecific)
+ {
+ return (DERApplicationSpecific)obj;
+ }
+ else if (obj instanceof byte[])
+ {
+ try
+ {
+ return DERApplicationSpecific.getInstance(ASN1Primitive.fromByteArray((byte[])obj));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("failed to construct object from byte[]: " + e.getMessage());
+ }
+ }
+ else if (obj instanceof ASN1Encodable)
+ {
+ ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+
+ if (primitive instanceof ASN1Sequence)
+ {
+ return (DERApplicationSpecific)primitive;
+ }
+ }
+
+ throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
+ }
+
+ private int getLengthOfHeader(byte[] data)
+ {
+ int length = data[1] & 0xff; // TODO: assumes 1 byte tag
+
+ if (length == 0x80)
+ {
+ return 2; // indefinite-length encoding
+ }
+
+ if (length > 127)
+ {
+ int size = length & 0x7f;
+
+ // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here
+ if (size > 4)
+ {
+ throw new IllegalStateException("DER length more than 4 bytes: " + size);
+ }
+
+ return size + 2;
+ }
+
+ return 2;
+ }
+
+ public boolean isConstructed()
+ {
+ return isConstructed;
+ }
+
+ public byte[] getContents()
+ {
+ return octets;
+ }
+
+ public int getApplicationTag()
+ {
+ return tag;
+ }
+
+ /**
+ * Return the enclosed object assuming explicit tagging.
+ *
+ * @return the resulting object
+ * @throws IOException if reconstruction fails.
+ */
+ public ASN1Primitive getObject()
+ throws IOException
+ {
+ return new ASN1InputStream(getContents()).readObject();
+ }
+
+ /**
+ * Return the enclosed object assuming implicit tagging.
+ *
+ * @param derTagNo the type tag that should be applied to the object's contents.
+ * @return the resulting object
+ * @throws IOException if reconstruction fails.
+ */
+ public ASN1Primitive getObject(int derTagNo)
+ throws IOException
+ {
+ if (derTagNo >= 0x1f)
+ {
+ throw new IOException("unsupported tag number");
+ }
+
+ byte[] orig = this.getEncoded();
+ byte[] tmp = replaceTagNumber(derTagNo, orig);
+
+ if ((orig[0] & BERTags.CONSTRUCTED) != 0)
+ {
+ tmp[0] |= BERTags.CONSTRUCTED;
+ }
+
+ return new ASN1InputStream(tmp).readObject();
+ }
+
+ int encodedLength()
+ throws IOException
+ {
+ return StreamUtil.calculateTagLength(tag) + StreamUtil.calculateBodyLength(octets.length) + octets.length;
+ }
+
+ /* (non-Javadoc)
+ * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
+ */
+ void encode(ASN1OutputStream out) throws IOException
+ {
+ int classBits = BERTags.APPLICATION;
+ if (isConstructed)
+ {
+ classBits |= BERTags.CONSTRUCTED;
+ }
+
+ out.writeEncoded(classBits, tag, octets);
+ }
+
+ boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof DERApplicationSpecific))
+ {
+ return false;
+ }
+
+ DERApplicationSpecific other = (DERApplicationSpecific)o;
+
+ return isConstructed == other.isConstructed
+ && tag == other.tag
+ && Arrays.areEqual(octets, other.octets);
+ }
+
+ public int hashCode()
+ {
+ return (isConstructed ? 1 : 0) ^ tag ^ Arrays.hashCode(octets);
+ }
+
+ private byte[] replaceTagNumber(int newTag, byte[] input)
+ throws IOException
+ {
+ int tagNo = input[0] & 0x1f;
+ int index = 1;
+ //
+ // with tagged object tag number is bottom 5 bits, or stored at the start of the content
+ //
+ if (tagNo == 0x1f)
+ {
+ tagNo = 0;
+
+ int b = input[index++] & 0xff;
+
+ // X.690-0207 8.1.2.4.2
+ // "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
+ if ((b & 0x7f) == 0) // Note: -1 will pass
+ {
+ throw new ASN1ParsingException("corrupted stream - invalid high tag number found");
+ }
+
+ while ((b >= 0) && ((b & 0x80) != 0))
+ {
+ tagNo |= (b & 0x7f);
+ tagNo <<= 7;
+ b = input[index++] & 0xff;
+ }
+
+ tagNo |= (b & 0x7f);
+ }
+
+ byte[] tmp = new byte[input.length - index + 1];
+
+ System.arraycopy(input, index, tmp, 1, tmp.length - 1);
+
+ tmp[0] = (byte)newTag;
+
+ return tmp;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERBMPString.java b/core/src/main/java/org/bouncycastle/asn1/DERBMPString.java
new file mode 100644
index 00000000..341e46a6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERBMPString.java
@@ -0,0 +1,153 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * DER BMPString object.
+ */
+public class DERBMPString
+ extends ASN1Primitive
+ implements ASN1String
+{
+ private char[] string;
+
+ /**
+ * return a BMP String from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERBMPString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERBMPString)
+ {
+ return (DERBMPString)obj;
+ }
+
+ if (obj instanceof byte[])
+ {
+ try
+ {
+ return (DERBMPString)fromByteArray((byte[])obj);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+ }
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a BMP String 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 DERBMPString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ ASN1Primitive o = obj.getObject();
+
+ if (explicit || o instanceof DERBMPString)
+ {
+ return getInstance(o);
+ }
+ else
+ {
+ return new DERBMPString(ASN1OctetString.getInstance(o).getOctets());
+ }
+ }
+
+ /**
+ * basic constructor - byte encoded string.
+ */
+ DERBMPString(
+ byte[] string)
+ {
+ char[] cs = new char[string.length / 2];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ cs[i] = (char)((string[2 * i] << 8) | (string[2 * i + 1] & 0xff));
+ }
+
+ this.string = cs;
+ }
+
+ DERBMPString(char[] string)
+ {
+ this.string = string;
+ }
+
+ /**
+ * basic constructor
+ */
+ public DERBMPString(
+ String string)
+ {
+ this.string = string.toCharArray();
+ }
+
+ public String getString()
+ {
+ return new String(string);
+ }
+
+ public String toString()
+ {
+ return getString();
+ }
+
+ public int hashCode()
+ {
+ return Arrays.hashCode(string);
+ }
+
+ protected boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof DERBMPString))
+ {
+ return false;
+ }
+
+ DERBMPString s = (DERBMPString)o;
+
+ return Arrays.areEqual(string, s.string);
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ {
+ return 1 + StreamUtil.calculateBodyLength(string.length * 2) + (string.length * 2);
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.write(BERTags.BMP_STRING);
+ out.writeLength(string.length * 2);
+
+ for (int i = 0; i != string.length; i++)
+ {
+ char c = string[i];
+
+ out.write((byte)(c >> 8));
+ out.write((byte)c);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERBitString.java b/core/src/main/java/org/bouncycastle/asn1/DERBitString.java
new file mode 100644
index 00000000..a7b02ec6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERBitString.java
@@ -0,0 +1,313 @@
+package org.bouncycastle.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.io.Streams;
+
+public class DERBitString
+ extends ASN1Primitive
+ implements ASN1String
+{
+ private static final char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+ protected byte[] data;
+ protected int padBits;
+
+ /**
+ * return the correct number of pad bits for a bit string defined in
+ * a 32 bit constant
+ */
+ static protected int getPadBits(
+ int bitString)
+ {
+ int val = 0;
+ for (int i = 3; i >= 0; i--)
+ {
+ //
+ // this may look a little odd, but if it isn't done like this pre jdk1.2
+ // JVM's break!
+ //
+ if (i != 0)
+ {
+ if ((bitString >> (i * 8)) != 0)
+ {
+ val = (bitString >> (i * 8)) & 0xFF;
+ break;
+ }
+ }
+ else
+ {
+ if (bitString != 0)
+ {
+ val = bitString & 0xFF;
+ break;
+ }
+ }
+ }
+
+ if (val == 0)
+ {
+ return 7;
+ }
+
+
+ int bits = 1;
+
+ while (((val <<= 1) & 0xFF) != 0)
+ {
+ bits++;
+ }
+
+ return 8 - bits;
+ }
+
+ /**
+ * return the correct number of bytes for a bit string defined in
+ * a 32 bit constant
+ */
+ static protected byte[] getBytes(int bitString)
+ {
+ int bytes = 4;
+ for (int i = 3; i >= 1; i--)
+ {
+ if ((bitString & (0xFF << (i * 8))) != 0)
+ {
+ break;
+ }
+ bytes--;
+ }
+
+ byte[] result = new byte[bytes];
+ for (int i = 0; i < bytes; i++)
+ {
+ result[i] = (byte) ((bitString >> (i * 8)) & 0xFF);
+ }
+
+ return result;
+ }
+
+ /**
+ * return a Bit String from the passed in object
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERBitString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERBitString)
+ {
+ return (DERBitString)obj;
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a Bit String 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 DERBitString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ ASN1Primitive o = obj.getObject();
+
+ if (explicit || o instanceof DERBitString)
+ {
+ return getInstance(o);
+ }
+ else
+ {
+ return fromOctetString(((ASN1OctetString)o).getOctets());
+ }
+ }
+
+ protected DERBitString(
+ byte data,
+ int padBits)
+ {
+ this.data = new byte[1];
+ this.data[0] = data;
+ this.padBits = padBits;
+ }
+
+ /**
+ * @param data the octets making up the bit string.
+ * @param padBits the number of extra bits at the end of the string.
+ */
+ public DERBitString(
+ byte[] data,
+ int padBits)
+ {
+ this.data = data;
+ this.padBits = padBits;
+ }
+
+ public DERBitString(
+ byte[] data)
+ {
+ this(data, 0);
+ }
+
+ public DERBitString(
+ int value)
+ {
+ this.data = getBytes(value);
+ this.padBits = getPadBits(value);
+ }
+
+ public DERBitString(
+ ASN1Encodable obj)
+ throws IOException
+ {
+ this.data = obj.toASN1Primitive().getEncoded(ASN1Encoding.DER);
+ this.padBits = 0;
+ }
+
+ public byte[] getBytes()
+ {
+ return data;
+ }
+
+ public int getPadBits()
+ {
+ return padBits;
+ }
+
+
+ /**
+ * @return the value of the bit string as an int (truncating if necessary)
+ */
+ public int intValue()
+ {
+ int value = 0;
+
+ for (int i = 0; i != data.length && i != 4; i++)
+ {
+ value |= (data[i] & 0xff) << (8 * i);
+ }
+
+ return value;
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ {
+ return 1 + StreamUtil.calculateBodyLength(data.length + 1) + data.length + 1;
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ byte[] bytes = new byte[getBytes().length + 1];
+
+ bytes[0] = (byte)getPadBits();
+ System.arraycopy(getBytes(), 0, bytes, 1, bytes.length - 1);
+
+ out.writeEncoded(BERTags.BIT_STRING, bytes);
+ }
+
+ public int hashCode()
+ {
+ return padBits ^ Arrays.hashCode(data);
+ }
+
+ protected boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof DERBitString))
+ {
+ return false;
+ }
+
+ DERBitString other = (DERBitString)o;
+
+ return this.padBits == other.padBits
+ && Arrays.areEqual(this.data, other.data);
+ }
+
+ public String getString()
+ {
+ StringBuffer buf = new StringBuffer("#");
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ try
+ {
+ aOut.writeObject(this);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("internal error encoding BitString");
+ }
+
+ byte[] string = bOut.toByteArray();
+
+ for (int i = 0; i != string.length; i++)
+ {
+ buf.append(table[(string[i] >>> 4) & 0xf]);
+ buf.append(table[string[i] & 0xf]);
+ }
+
+ return buf.toString();
+ }
+
+ public String toString()
+ {
+ return getString();
+ }
+
+ static DERBitString fromOctetString(byte[] bytes)
+ {
+ if (bytes.length < 1)
+ {
+ throw new IllegalArgumentException("truncated BIT STRING detected");
+ }
+
+ int padBits = bytes[0];
+ byte[] data = new byte[bytes.length - 1];
+
+ if (data.length != 0)
+ {
+ System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
+ }
+
+ return new DERBitString(data, padBits);
+ }
+
+ static DERBitString fromInputStream(int length, InputStream stream)
+ throws IOException
+ {
+ if (length < 1)
+ {
+ throw new IllegalArgumentException("truncated BIT STRING detected");
+ }
+
+ int padBits = stream.read();
+ byte[] data = new byte[length - 1];
+
+ if (data.length != 0)
+ {
+ if (Streams.readFully(stream, data) != data.length)
+ {
+ throw new EOFException("EOF encountered in middle of BIT STRING");
+ }
+ }
+
+ return new DERBitString(data, padBits);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERBoolean.java b/core/src/main/java/org/bouncycastle/asn1/DERBoolean.java
new file mode 100644
index 00000000..063e5253
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERBoolean.java
@@ -0,0 +1,179 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+
+public class DERBoolean
+ extends ASN1Primitive
+{
+ private static final byte[] TRUE_VALUE = new byte[] { (byte)0xff };
+ private static final byte[] FALSE_VALUE = new byte[] { 0 };
+
+ private byte[] value;
+
+ public static final ASN1Boolean FALSE = new ASN1Boolean(false);
+ public static final ASN1Boolean TRUE = new ASN1Boolean(true);
+
+
+ /**
+ * return a boolean from the passed in object.
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static ASN1Boolean getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof ASN1Boolean)
+ {
+ return (ASN1Boolean)obj;
+ }
+
+ if (obj instanceof DERBoolean)
+ {
+ return ((DERBoolean)obj).isTrue() ? DERBoolean.TRUE : DERBoolean.FALSE;
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a ASN1Boolean from the passed in boolean.
+ */
+ public static ASN1Boolean getInstance(
+ boolean value)
+ {
+ return (value ? TRUE : FALSE);
+ }
+
+ /**
+ * return a ASN1Boolean from the passed in boolean.
+ */
+ public static ASN1Boolean getInstance(
+ int value)
+ {
+ return (value != 0 ? TRUE : FALSE);
+ }
+
+ /**
+ * return a Boolean 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 ASN1Boolean getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ ASN1Primitive o = obj.getObject();
+
+ if (explicit || o instanceof DERBoolean)
+ {
+ return getInstance(o);
+ }
+ else
+ {
+ return ASN1Boolean.fromOctetString(((ASN1OctetString)o).getOctets());
+ }
+ }
+
+ DERBoolean(
+ byte[] value)
+ {
+ if (value.length != 1)
+ {
+ throw new IllegalArgumentException("byte value should have 1 byte in it");
+ }
+
+ if (value[0] == 0)
+ {
+ this.value = FALSE_VALUE;
+ }
+ else if (value[0] == 0xff)
+ {
+ this.value = TRUE_VALUE;
+ }
+ else
+ {
+ this.value = Arrays.clone(value);
+ }
+ }
+
+ /**
+ * @deprecated use getInstance(boolean) method.
+ * @param value
+ */
+ public DERBoolean(
+ boolean value)
+ {
+ this.value = (value) ? TRUE_VALUE : FALSE_VALUE;
+ }
+
+ public boolean isTrue()
+ {
+ return (value[0] != 0);
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ {
+ return 3;
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(BERTags.BOOLEAN, value);
+ }
+
+ protected boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if ((o == null) || !(o instanceof DERBoolean))
+ {
+ return false;
+ }
+
+ return (value[0] == ((DERBoolean)o).value[0]);
+ }
+
+ public int hashCode()
+ {
+ return value[0];
+ }
+
+
+ public String toString()
+ {
+ return (value[0] != 0) ? "TRUE" : "FALSE";
+ }
+
+ static ASN1Boolean fromOctetString(byte[] value)
+ {
+ if (value.length != 1)
+ {
+ throw new IllegalArgumentException("byte value should have 1 byte in it");
+ }
+
+ if (value[0] == 0)
+ {
+ return FALSE;
+ }
+ else if (value[0] == 0xff)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return new ASN1Boolean(value);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DEREncodableVector.java b/core/src/main/java/org/bouncycastle/asn1/DEREncodableVector.java
new file mode 100644
index 00000000..919ff72d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DEREncodableVector.java
@@ -0,0 +1,18 @@
+package org.bouncycastle.asn1;
+
+/**
+ * a general class for building up a vector of DER encodable objects -
+ * this will eventually be superceded by ASN1EncodableVector so you should
+ * use that class in preference.
+ */
+public class DEREncodableVector
+ extends ASN1EncodableVector
+{
+ /**
+ * @deprecated use ASN1EncodableVector instead.
+ */
+ public DEREncodableVector()
+ {
+
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DEREnumerated.java b/core/src/main/java/org/bouncycastle/asn1/DEREnumerated.java
new file mode 100644
index 00000000..2f299ee2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DEREnumerated.java
@@ -0,0 +1,158 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import org.bouncycastle.util.Arrays;
+
+public class DEREnumerated
+ extends ASN1Primitive
+{
+ byte[] bytes;
+
+ /**
+ * return an integer from the passed in object
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static ASN1Enumerated getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof ASN1Enumerated)
+ {
+ return (ASN1Enumerated)obj;
+ }
+
+ if (obj instanceof DEREnumerated)
+ {
+ return new ASN1Enumerated(((DEREnumerated)obj).getValue());
+ }
+
+ if (obj instanceof byte[])
+ {
+ try
+ {
+ return (ASN1Enumerated)fromByteArray((byte[])obj);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+ }
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an Enumerated 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 DEREnumerated getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ ASN1Primitive o = obj.getObject();
+
+ if (explicit || o instanceof DEREnumerated)
+ {
+ return getInstance(o);
+ }
+ else
+ {
+ return fromOctetString(((ASN1OctetString)o).getOctets());
+ }
+ }
+
+ public DEREnumerated(
+ int value)
+ {
+ bytes = BigInteger.valueOf(value).toByteArray();
+ }
+
+ public DEREnumerated(
+ BigInteger value)
+ {
+ bytes = value.toByteArray();
+ }
+
+ public DEREnumerated(
+ byte[] bytes)
+ {
+ this.bytes = bytes;
+ }
+
+ public BigInteger getValue()
+ {
+ return new BigInteger(bytes);
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ {
+ return 1 + StreamUtil.calculateBodyLength(bytes.length) + bytes.length;
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(BERTags.ENUMERATED, bytes);
+ }
+
+ boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof DEREnumerated))
+ {
+ return false;
+ }
+
+ DEREnumerated other = (DEREnumerated)o;
+
+ return Arrays.areEqual(this.bytes, other.bytes);
+ }
+
+ public int hashCode()
+ {
+ return Arrays.hashCode(bytes);
+ }
+
+ private static ASN1Enumerated[] cache = new ASN1Enumerated[12];
+
+ static ASN1Enumerated fromOctetString(byte[] enc)
+ {
+ if (enc.length > 1)
+ {
+ return new ASN1Enumerated(Arrays.clone(enc));
+ }
+
+ if (enc.length == 0)
+ {
+ throw new IllegalArgumentException("ENUMERATED has zero length");
+ }
+ int value = enc[0] & 0xff;
+
+ if (value >= cache.length)
+ {
+ return new ASN1Enumerated(Arrays.clone(enc));
+ }
+
+ ASN1Enumerated possibleMatch = cache[value];
+
+ if (possibleMatch == null)
+ {
+ possibleMatch = cache[value] = new ASN1Enumerated(Arrays.clone(enc));
+ }
+
+ return possibleMatch;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERExternal.java b/core/src/main/java/org/bouncycastle/asn1/DERExternal.java
new file mode 100644
index 00000000..aed1d27f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERExternal.java
@@ -0,0 +1,294 @@
+package org.bouncycastle.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * Class representing the DER-type External
+ */
+public class DERExternal
+ extends ASN1Primitive
+{
+ private ASN1ObjectIdentifier directReference;
+ private ASN1Integer indirectReference;
+ private ASN1Primitive dataValueDescriptor;
+ private int encoding;
+ private ASN1Primitive externalContent;
+
+ public DERExternal(ASN1EncodableVector vector)
+ {
+ int offset = 0;
+
+ ASN1Primitive enc = getObjFromVector(vector, offset);
+ if (enc instanceof ASN1ObjectIdentifier)
+ {
+ directReference = (ASN1ObjectIdentifier)enc;
+ offset++;
+ enc = getObjFromVector(vector, offset);
+ }
+ if (enc instanceof ASN1Integer)
+ {
+ indirectReference = (ASN1Integer) enc;
+ offset++;
+ enc = getObjFromVector(vector, offset);
+ }
+ if (!(enc instanceof DERTaggedObject))
+ {
+ dataValueDescriptor = (ASN1Primitive) enc;
+ offset++;
+ enc = getObjFromVector(vector, offset);
+ }
+
+ if (vector.size() != offset + 1)
+ {
+ throw new IllegalArgumentException("input vector too large");
+ }
+
+ if (!(enc instanceof DERTaggedObject))
+ {
+ throw new IllegalArgumentException("No tagged object found in vector. Structure doesn't seem to be of type External");
+ }
+ DERTaggedObject obj = (DERTaggedObject)enc;
+ setEncoding(obj.getTagNo());
+ externalContent = obj.getObject();
+ }
+
+ private ASN1Primitive getObjFromVector(ASN1EncodableVector v, int index)
+ {
+ if (v.size() <= index)
+ {
+ throw new IllegalArgumentException("too few objects in input vector");
+ }
+
+ return v.get(index).toASN1Primitive();
+ }
+ /**
+ * Creates a new instance of DERExternal
+ * See X.690 for more informations about the meaning of these parameters
+ * @param directReference The direct reference or <code>null</code> if not set.
+ * @param indirectReference The indirect reference or <code>null</code> if not set.
+ * @param dataValueDescriptor The data value descriptor or <code>null</code> if not set.
+ * @param externalData The external data in its encoded form.
+ */
+ public DERExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, DERTaggedObject externalData)
+ {
+ this(directReference, indirectReference, dataValueDescriptor, externalData.getTagNo(), externalData.toASN1Primitive());
+ }
+
+ /**
+ * Creates a new instance of DERExternal.
+ * See X.690 for more informations about the meaning of these parameters
+ * @param directReference The direct reference or <code>null</code> if not set.
+ * @param indirectReference The indirect reference or <code>null</code> if not set.
+ * @param dataValueDescriptor The data value descriptor or <code>null</code> if not set.
+ * @param encoding The encoding to be used for the external data
+ * @param externalData The external data
+ */
+ public DERExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, int encoding, ASN1Primitive externalData)
+ {
+ setDirectReference(directReference);
+ setIndirectReference(indirectReference);
+ setDataValueDescriptor(dataValueDescriptor);
+ setEncoding(encoding);
+ setExternalContent(externalData.toASN1Primitive());
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode()
+ {
+ int ret = 0;
+ if (directReference != null)
+ {
+ ret = directReference.hashCode();
+ }
+ if (indirectReference != null)
+ {
+ ret ^= indirectReference.hashCode();
+ }
+ if (dataValueDescriptor != null)
+ {
+ ret ^= dataValueDescriptor.hashCode();
+ }
+ ret ^= externalContent.hashCode();
+ return ret;
+ }
+
+ boolean isConstructed()
+ {
+ return true;
+ }
+
+ int encodedLength()
+ throws IOException
+ {
+ return this.getEncoded().length;
+ }
+
+ /* (non-Javadoc)
+ * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
+ */
+ void encode(ASN1OutputStream out)
+ throws IOException
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ if (directReference != null)
+ {
+ baos.write(directReference.getEncoded(ASN1Encoding.DER));
+ }
+ if (indirectReference != null)
+ {
+ baos.write(indirectReference.getEncoded(ASN1Encoding.DER));
+ }
+ if (dataValueDescriptor != null)
+ {
+ baos.write(dataValueDescriptor.getEncoded(ASN1Encoding.DER));
+ }
+ DERTaggedObject obj = new DERTaggedObject(true, encoding, externalContent);
+ baos.write(obj.getEncoded(ASN1Encoding.DER));
+ out.writeEncoded(BERTags.CONSTRUCTED, BERTags.EXTERNAL, baos.toByteArray());
+ }
+
+ /* (non-Javadoc)
+ * @see org.bouncycastle.asn1.ASN1Primitive#asn1Equals(org.bouncycastle.asn1.ASN1Primitive)
+ */
+ boolean asn1Equals(ASN1Primitive o)
+ {
+ if (!(o instanceof DERExternal))
+ {
+ return false;
+ }
+ if (this == o)
+ {
+ return true;
+ }
+ DERExternal other = (DERExternal)o;
+ if (directReference != null)
+ {
+ if (other.directReference == null || !other.directReference.equals(directReference))
+ {
+ return false;
+ }
+ }
+ if (indirectReference != null)
+ {
+ if (other.indirectReference == null || !other.indirectReference.equals(indirectReference))
+ {
+ return false;
+ }
+ }
+ if (dataValueDescriptor != null)
+ {
+ if (other.dataValueDescriptor == null || !other.dataValueDescriptor.equals(dataValueDescriptor))
+ {
+ return false;
+ }
+ }
+ return externalContent.equals(other.externalContent);
+ }
+
+ /**
+ * Returns the data value descriptor
+ * @return The descriptor
+ */
+ public ASN1Primitive getDataValueDescriptor()
+ {
+ return dataValueDescriptor;
+ }
+
+ /**
+ * Returns the direct reference of the external element
+ * @return The reference
+ */
+ public ASN1ObjectIdentifier getDirectReference()
+ {
+ return directReference;
+ }
+
+ /**
+ * Returns the encoding of the content. Valid values are
+ * <ul>
+ * <li><code>0</code> single-ASN1-type</li>
+ * <li><code>1</code> OCTET STRING</li>
+ * <li><code>2</code> BIT STRING</li>
+ * </ul>
+ * @return The encoding
+ */
+ public int getEncoding()
+ {
+ return encoding;
+ }
+
+ /**
+ * Returns the content of this element
+ * @return The content
+ */
+ public ASN1Primitive getExternalContent()
+ {
+ return externalContent;
+ }
+
+ /**
+ * Returns the indirect reference of this element
+ * @return The reference
+ */
+ public ASN1Integer getIndirectReference()
+ {
+ return indirectReference;
+ }
+
+ /**
+ * Sets the data value descriptor
+ * @param dataValueDescriptor The descriptor
+ */
+ private void setDataValueDescriptor(ASN1Primitive dataValueDescriptor)
+ {
+ this.dataValueDescriptor = dataValueDescriptor;
+ }
+
+ /**
+ * Sets the direct reference of the external element
+ * @param directReferemce The reference
+ */
+ private void setDirectReference(ASN1ObjectIdentifier directReferemce)
+ {
+ this.directReference = directReferemce;
+ }
+
+ /**
+ * Sets the encoding of the content. Valid values are
+ * <ul>
+ * <li><code>0</code> single-ASN1-type</li>
+ * <li><code>1</code> OCTET STRING</li>
+ * <li><code>2</code> BIT STRING</li>
+ * </ul>
+ * @param encoding The encoding
+ */
+ private void setEncoding(int encoding)
+ {
+ if (encoding < 0 || encoding > 2)
+ {
+ throw new IllegalArgumentException("invalid encoding value: " + encoding);
+ }
+ this.encoding = encoding;
+ }
+
+ /**
+ * Sets the content of this element
+ * @param externalContent The content
+ */
+ private void setExternalContent(ASN1Primitive externalContent)
+ {
+ this.externalContent = externalContent;
+ }
+
+ /**
+ * Sets the indirect reference of this element
+ * @param indirectReference The reference
+ */
+ private void setIndirectReference(ASN1Integer indirectReference)
+ {
+ this.indirectReference = indirectReference;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERExternalParser.java b/core/src/main/java/org/bouncycastle/asn1/DERExternalParser.java
new file mode 100644
index 00000000..b19c84d5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERExternalParser.java
@@ -0,0 +1,52 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public class DERExternalParser
+ implements ASN1Encodable, InMemoryRepresentable
+{
+ private ASN1StreamParser _parser;
+
+ /**
+ *
+ */
+ public DERExternalParser(ASN1StreamParser parser)
+ {
+ this._parser = parser;
+ }
+
+ public ASN1Encodable readObject()
+ throws IOException
+ {
+ return _parser.readObject();
+ }
+
+ public ASN1Primitive getLoadedObject()
+ throws IOException
+ {
+ try
+ {
+ return new DERExternal(_parser.readVector());
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new ASN1Exception(e.getMessage(), e);
+ }
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ try
+ {
+ return getLoadedObject();
+ }
+ catch (IOException ioe)
+ {
+ throw new ASN1ParsingException("unable to get DER object", ioe);
+ }
+ catch (IllegalArgumentException ioe)
+ {
+ throw new ASN1ParsingException("unable to get DER object", ioe);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERFactory.java b/core/src/main/java/org/bouncycastle/asn1/DERFactory.java
new file mode 100644
index 00000000..b829e3bc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERFactory.java
@@ -0,0 +1,17 @@
+package org.bouncycastle.asn1;
+
+class DERFactory
+{
+ static final ASN1Sequence EMPTY_SEQUENCE = new DERSequence();
+ static final ASN1Set EMPTY_SET = new DERSet();
+
+ static ASN1Sequence createSequence(ASN1EncodableVector v)
+ {
+ return v.size() < 1 ? EMPTY_SEQUENCE : new DLSequence(v);
+ }
+
+ static ASN1Set createSet(ASN1EncodableVector v)
+ {
+ return v.size() < 1 ? EMPTY_SET : new DLSet(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERGeneralString.java b/core/src/main/java/org/bouncycastle/asn1/DERGeneralString.java
new file mode 100644
index 00000000..c6354f46
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERGeneralString.java
@@ -0,0 +1,110 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+public class DERGeneralString
+ extends ASN1Primitive
+ implements ASN1String
+{
+ private byte[] string;
+
+ public static DERGeneralString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERGeneralString)
+ {
+ return (DERGeneralString) obj;
+ }
+
+ if (obj instanceof byte[])
+ {
+ try
+ {
+ return (DERGeneralString)fromByteArray((byte[])obj);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+ }
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: "
+ + obj.getClass().getName());
+ }
+
+ public static DERGeneralString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ ASN1Primitive o = obj.getObject();
+
+ if (explicit || o instanceof DERGeneralString)
+ {
+ return getInstance(o);
+ }
+ else
+ {
+ return new DERGeneralString(((ASN1OctetString)o).getOctets());
+ }
+ }
+
+ DERGeneralString(byte[] string)
+ {
+ this.string = string;
+ }
+
+ public DERGeneralString(String string)
+ {
+ this.string = Strings.toByteArray(string);
+ }
+
+ public String getString()
+ {
+ return Strings.fromByteArray(string);
+ }
+
+ public String toString()
+ {
+ return getString();
+ }
+
+ public byte[] getOctets()
+ {
+ return Arrays.clone(string);
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ {
+ return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
+ }
+
+ void encode(ASN1OutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(BERTags.GENERAL_STRING, string);
+ }
+
+ public int hashCode()
+ {
+ return Arrays.hashCode(string);
+ }
+
+ boolean asn1Equals(ASN1Primitive o)
+ {
+ if (!(o instanceof DERGeneralString))
+ {
+ return false;
+ }
+ DERGeneralString s = (DERGeneralString)o;
+
+ return Arrays.areEqual(string, s.string);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERGeneralizedTime.java b/core/src/main/java/org/bouncycastle/asn1/DERGeneralizedTime.java
new file mode 100644
index 00000000..43e46731
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERGeneralizedTime.java
@@ -0,0 +1,350 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.SimpleTimeZone;
+import java.util.TimeZone;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+/**
+ * Generalized time object.
+ */
+public class DERGeneralizedTime
+ extends ASN1Primitive
+{
+ 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);
+ }
+
+ if (obj instanceof byte[])
+ {
+ try
+ {
+ return (ASN1GeneralizedTime)fromByteArray((byte[])obj);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+ }
+ }
+
+ 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)
+ {
+ this.time = Strings.toByteArray(time);
+ try
+ {
+ this.getDate();
+ }
+ catch (ParseException e)
+ {
+ throw new IllegalArgumentException("invalid date string: " + e.getMessage());
+ }
+ }
+
+ /**
+ * base constructor from a java.util.date object
+ */
+ public DERGeneralizedTime(
+ Date time)
+ {
+ SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
+
+ dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
+
+ this.time = Strings.toByteArray(dateF.format(time));
+ }
+
+ DERGeneralizedTime(
+ byte[] bytes)
+ {
+ this.time = bytes;
+ }
+
+ /**
+ * 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()
+ {
+ 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();
+ }
+
+ 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()
+ throws ParseException
+ {
+ SimpleDateFormat dateF;
+ String stime = Strings.fromByteArray(time);
+ String d = stime;
+
+ if (stime.endsWith("Z"))
+ {
+ if (hasFractionalSeconds())
+ {
+ dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'");
+ }
+ else
+ {
+ dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
+ }
+
+ dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
+ }
+ else if (stime.indexOf('-') > 0 || stime.indexOf('+') > 0)
+ {
+ d = this.getTime();
+ if (hasFractionalSeconds())
+ {
+ dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSSz");
+ }
+ else
+ {
+ dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
+ }
+
+ dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
+ }
+ else
+ {
+ if (hasFractionalSeconds())
+ {
+ dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS");
+ }
+ else
+ {
+ dateF = new SimpleDateFormat("yyyyMMddHHmmss");
+ }
+
+ dateF.setTimeZone(new SimpleTimeZone(0, TimeZone.getDefault().getID()));
+ }
+
+ if (hasFractionalSeconds())
+ {
+ // java misinterprets extra digits as being milliseconds...
+ String frac = d.substring(14);
+ int index;
+ for (index = 1; index < frac.length(); index++)
+ {
+ char ch = frac.charAt(index);
+ if (!('0' <= ch && ch <= '9'))
+ {
+ break;
+ }
+ }
+
+ if (index - 1 > 3)
+ {
+ frac = frac.substring(0, 4) + frac.substring(index);
+ d = d.substring(0, 14) + frac;
+ }
+ else if (index - 1 == 1)
+ {
+ frac = frac.substring(0, index) + "00" + frac.substring(index);
+ d = d.substring(0, 14) + frac;
+ }
+ else if (index - 1 == 2)
+ {
+ frac = frac.substring(0, index) + "0" + frac.substring(index);
+ d = d.substring(0, 14) + frac;
+ }
+ }
+
+ return dateF.parse(d);
+ }
+
+ 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()
+ {
+ return Arrays.hashCode(time);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERGenerator.java b/core/src/main/java/org/bouncycastle/asn1/DERGenerator.java
new file mode 100644
index 00000000..7451ad40
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERGenerator.java
@@ -0,0 +1,119 @@
+package org.bouncycastle.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.bouncycastle.util.io.Streams;
+
+public abstract class DERGenerator
+ extends ASN1Generator
+{
+ private boolean _tagged = false;
+ private boolean _isExplicit;
+ private int _tagNo;
+
+ protected DERGenerator(
+ OutputStream out)
+ {
+ super(out);
+ }
+
+ public DERGenerator(
+ OutputStream out,
+ int tagNo,
+ boolean isExplicit)
+ {
+ super(out);
+
+ _tagged = true;
+ _isExplicit = isExplicit;
+ _tagNo = tagNo;
+ }
+
+ private void writeLength(
+ OutputStream out,
+ int length)
+ throws IOException
+ {
+ if (length > 127)
+ {
+ int size = 1;
+ int val = length;
+
+ while ((val >>>= 8) != 0)
+ {
+ size++;
+ }
+
+ out.write((byte)(size | 0x80));
+
+ for (int i = (size - 1) * 8; i >= 0; i -= 8)
+ {
+ out.write((byte)(length >> i));
+ }
+ }
+ else
+ {
+ out.write((byte)length);
+ }
+ }
+
+ void writeDEREncoded(
+ OutputStream out,
+ int tag,
+ byte[] bytes)
+ throws IOException
+ {
+ out.write(tag);
+ writeLength(out, bytes.length);
+ out.write(bytes);
+ }
+
+ void writeDEREncoded(
+ int tag,
+ byte[] bytes)
+ throws IOException
+ {
+ if (_tagged)
+ {
+ int tagNum = _tagNo | BERTags.TAGGED;
+
+ if (_isExplicit)
+ {
+ int newTag = _tagNo | BERTags.CONSTRUCTED | BERTags.TAGGED;
+
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ writeDEREncoded(bOut, tag, bytes);
+
+ writeDEREncoded(_out, newTag, bOut.toByteArray());
+ }
+ else
+ {
+ if ((tag & BERTags.CONSTRUCTED) != 0)
+ {
+ writeDEREncoded(_out, tagNum | BERTags.CONSTRUCTED, bytes);
+ }
+ else
+ {
+ writeDEREncoded(_out, tagNum, bytes);
+ }
+ }
+ }
+ else
+ {
+ writeDEREncoded(_out, tag, bytes);
+ }
+ }
+
+ void writeDEREncoded(
+ OutputStream out,
+ int tag,
+ InputStream in)
+ throws IOException
+ {
+ writeDEREncoded(out, tag, Streams.readAll(in));
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERIA5String.java b/core/src/main/java/org/bouncycastle/asn1/DERIA5String.java
new file mode 100644
index 00000000..631672ef
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERIA5String.java
@@ -0,0 +1,183 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+/**
+ * DER IA5String object - this is an ascii string.
+ */
+public class DERIA5String
+ extends ASN1Primitive
+ implements ASN1String
+{
+ private byte[] string;
+
+ /**
+ * return a IA5 string from the passed in object
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERIA5String getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERIA5String)
+ {
+ return (DERIA5String)obj;
+ }
+
+ if (obj instanceof byte[])
+ {
+ try
+ {
+ return (DERIA5String)fromByteArray((byte[])obj);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+ }
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an IA5 String 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 DERIA5String getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ ASN1Primitive o = obj.getObject();
+
+ if (explicit || o instanceof DERIA5String)
+ {
+ return getInstance(o);
+ }
+ else
+ {
+ return new DERIA5String(((ASN1OctetString)o).getOctets());
+ }
+ }
+
+ /**
+ * basic constructor - with bytes.
+ */
+ DERIA5String(
+ byte[] string)
+ {
+ this.string = string;
+ }
+
+ /**
+ * basic constructor - without validation.
+ */
+ public DERIA5String(
+ String string)
+ {
+ this(string, false);
+ }
+
+ /**
+ * Constructor with optional validation.
+ *
+ * @param string the base string to wrap.
+ * @param validate whether or not to check the string.
+ * @throws IllegalArgumentException if validate is true and the string
+ * contains characters that should not be in an IA5String.
+ */
+ public DERIA5String(
+ String string,
+ boolean validate)
+ {
+ if (string == null)
+ {
+ throw new NullPointerException("string cannot be null");
+ }
+ if (validate && !isIA5String(string))
+ {
+ throw new IllegalArgumentException("string contains illegal characters");
+ }
+
+ this.string = Strings.toByteArray(string);
+ }
+
+ public String getString()
+ {
+ return Strings.fromByteArray(string);
+ }
+
+ public String toString()
+ {
+ return getString();
+ }
+
+ public byte[] getOctets()
+ {
+ return Arrays.clone(string);
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ {
+ return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(BERTags.IA5_STRING, string);
+ }
+
+ public int hashCode()
+ {
+ return Arrays.hashCode(string);
+ }
+
+ boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof DERIA5String))
+ {
+ return false;
+ }
+
+ DERIA5String s = (DERIA5String)o;
+
+ return Arrays.areEqual(string, s.string);
+ }
+
+ /**
+ * return true if the passed in String can be represented without
+ * loss as an IA5String, false otherwise.
+ *
+ * @return true if in printable set, false otherwise.
+ */
+ public static boolean isIA5String(
+ String str)
+ {
+ for (int i = str.length() - 1; i >= 0; i--)
+ {
+ char ch = str.charAt(i);
+
+ if (ch > 0x007f)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERInteger.java b/core/src/main/java/org/bouncycastle/asn1/DERInteger.java
new file mode 100644
index 00000000..3804450e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERInteger.java
@@ -0,0 +1,148 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import org.bouncycastle.util.Arrays;
+
+public class DERInteger
+ extends ASN1Primitive
+{
+ byte[] bytes;
+
+ /**
+ * return an integer from the passed in object
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static ASN1Integer getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof ASN1Integer)
+ {
+ return (ASN1Integer)obj;
+ }
+ if (obj instanceof DERInteger)
+ {
+ return new ASN1Integer((((DERInteger)obj).getValue()));
+ }
+
+ if (obj instanceof byte[])
+ {
+ try
+ {
+ return (ASN1Integer)fromByteArray((byte[])obj);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+ }
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an Integer 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 ASN1Integer getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ ASN1Primitive o = obj.getObject();
+
+ if (explicit || o instanceof DERInteger)
+ {
+ return getInstance(o);
+ }
+ else
+ {
+ return new ASN1Integer(ASN1OctetString.getInstance(obj.getObject()).getOctets());
+ }
+ }
+
+ public DERInteger(
+ long value)
+ {
+ bytes = BigInteger.valueOf(value).toByteArray();
+ }
+
+ public DERInteger(
+ BigInteger value)
+ {
+ bytes = value.toByteArray();
+ }
+
+ public DERInteger(
+ byte[] bytes)
+ {
+ this.bytes = bytes;
+ }
+
+ public BigInteger getValue()
+ {
+ return new BigInteger(bytes);
+ }
+
+ /**
+ * in some cases positive values get crammed into a space,
+ * that's not quite big enough...
+ */
+ public BigInteger getPositiveValue()
+ {
+ return new BigInteger(1, bytes);
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ {
+ return 1 + StreamUtil.calculateBodyLength(bytes.length) + bytes.length;
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(BERTags.INTEGER, bytes);
+ }
+
+ public int hashCode()
+ {
+ int value = 0;
+
+ for (int i = 0; i != bytes.length; i++)
+ {
+ value ^= (bytes[i] & 0xff) << (i % 4);
+ }
+
+ return value;
+ }
+
+ boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof DERInteger))
+ {
+ return false;
+ }
+
+ DERInteger other = (DERInteger)o;
+
+ return Arrays.areEqual(bytes, other.bytes);
+ }
+
+ public String toString()
+ {
+ return getValue().toString();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERNull.java b/core/src/main/java/org/bouncycastle/asn1/DERNull.java
new file mode 100644
index 00000000..1eb9f450
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERNull.java
@@ -0,0 +1,38 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+/**
+ * A NULL object.
+ */
+public class DERNull
+ extends ASN1Null
+{
+ public static final DERNull INSTANCE = new DERNull();
+
+ private static final byte[] zeroBytes = new byte[0];
+
+ /**
+ * @deprecated use DERNull.INSTANCE
+ */
+ public DERNull()
+ {
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ {
+ return 2;
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(BERTags.NULL, zeroBytes);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERNumericString.java b/core/src/main/java/org/bouncycastle/asn1/DERNumericString.java
new file mode 100644
index 00000000..eca4eea2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERNumericString.java
@@ -0,0 +1,186 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+/**
+ * DER NumericString object - this is an ascii string of characters {0,1,2,3,4,5,6,7,8,9, }.
+ */
+public class DERNumericString
+ extends ASN1Primitive
+ implements ASN1String
+{
+ private byte[] string;
+
+ /**
+ * return a Numeric string from the passed in object
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERNumericString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERNumericString)
+ {
+ return (DERNumericString)obj;
+ }
+
+ if (obj instanceof byte[])
+ {
+ try
+ {
+ return (DERNumericString)fromByteArray((byte[])obj);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+ }
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an Numeric String 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 DERNumericString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ ASN1Primitive o = obj.getObject();
+
+ if (explicit || o instanceof DERNumericString)
+ {
+ return getInstance(o);
+ }
+ else
+ {
+ return new DERNumericString(ASN1OctetString.getInstance(o).getOctets());
+ }
+ }
+
+ /**
+ * basic constructor - with bytes.
+ */
+ DERNumericString(
+ byte[] string)
+ {
+ this.string = string;
+ }
+
+ /**
+ * basic constructor - without validation..
+ */
+ public DERNumericString(
+ String string)
+ {
+ this(string, false);
+ }
+
+ /**
+ * Constructor with optional validation.
+ *
+ * @param string the base string to wrap.
+ * @param validate whether or not to check the string.
+ * @throws IllegalArgumentException if validate is true and the string
+ * contains characters that should not be in a NumericString.
+ */
+ public DERNumericString(
+ String string,
+ boolean validate)
+ {
+ if (validate && !isNumericString(string))
+ {
+ throw new IllegalArgumentException("string contains illegal characters");
+ }
+
+ this.string = Strings.toByteArray(string);
+ }
+
+ public String getString()
+ {
+ return Strings.fromByteArray(string);
+ }
+
+ public String toString()
+ {
+ return getString();
+ }
+
+ public byte[] getOctets()
+ {
+ return Arrays.clone(string);
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ {
+ return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(BERTags.NUMERIC_STRING, string);
+ }
+
+ public int hashCode()
+ {
+ return Arrays.hashCode(string);
+ }
+
+ boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof DERNumericString))
+ {
+ return false;
+ }
+
+ DERNumericString s = (DERNumericString)o;
+
+ return Arrays.areEqual(string, s.string);
+ }
+
+ /**
+ * Return true if the string can be represented as a NumericString ('0'..'9', ' ')
+ *
+ * @param str string to validate.
+ * @return true if numeric, fale otherwise.
+ */
+ public static boolean isNumericString(
+ String str)
+ {
+ for (int i = str.length() - 1; i >= 0; i--)
+ {
+ char ch = str.charAt(i);
+
+ if (ch > 0x007f)
+ {
+ return false;
+ }
+
+ if (('0' <= ch && ch <= '9') || ch == ' ')
+ {
+ continue;
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java b/core/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java
new file mode 100644
index 00000000..e1de22a2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java
@@ -0,0 +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
+ *
+ * @throws 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.
+ * @throws 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/core/src/main/java/org/bouncycastle/asn1/DEROctetString.java b/core/src/main/java/org/bouncycastle/asn1/DEROctetString.java
new file mode 100644
index 00000000..988186fb
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DEROctetString.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public class DEROctetString
+ extends ASN1OctetString
+{
+ /**
+ * @param string the octets making up the octet string.
+ */
+ public DEROctetString(
+ byte[] string)
+ {
+ super(string);
+ }
+
+ public DEROctetString(
+ ASN1Encodable obj)
+ throws IOException
+ {
+ super(obj.toASN1Primitive().getEncoded(ASN1Encoding.DER));
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ {
+ return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(BERTags.OCTET_STRING, string);
+ }
+
+ static void encode(
+ DEROutputStream derOut,
+ byte[] bytes)
+ throws IOException
+ {
+ derOut.writeEncoded(BERTags.OCTET_STRING, bytes);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DEROctetStringParser.java b/core/src/main/java/org/bouncycastle/asn1/DEROctetStringParser.java
new file mode 100644
index 00000000..e6e20686
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DEROctetStringParser.java
@@ -0,0 +1,39 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class DEROctetStringParser
+ implements ASN1OctetStringParser
+{
+ private DefiniteLengthInputStream stream;
+
+ DEROctetStringParser(
+ DefiniteLengthInputStream stream)
+ {
+ this.stream = stream;
+ }
+
+ public InputStream getOctetStream()
+ {
+ return stream;
+ }
+
+ public ASN1Primitive getLoadedObject()
+ throws IOException
+ {
+ return new DEROctetString(stream.toByteArray());
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ try
+ {
+ return getLoadedObject();
+ }
+ catch (IOException e)
+ {
+ throw new ASN1ParsingException("IOException converting stream to byte array: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DEROutputStream.java b/core/src/main/java/org/bouncycastle/asn1/DEROutputStream.java
new file mode 100644
index 00000000..8b18c3d7
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DEROutputStream.java
@@ -0,0 +1,41 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Stream that outputs encoding based on distinguished encoding rules.
+ */
+public class DEROutputStream
+ extends ASN1OutputStream
+{
+ public DEROutputStream(
+ OutputStream os)
+ {
+ super(os);
+ }
+
+ public void writeObject(
+ ASN1Encodable obj)
+ throws IOException
+ {
+ if (obj != null)
+ {
+ obj.toASN1Primitive().toDERObject().encode(this);
+ }
+ else
+ {
+ throw new IOException("null object detected");
+ }
+ }
+
+ ASN1OutputStream getDERSubStream()
+ {
+ return this;
+ }
+
+ ASN1OutputStream getDLSubStream()
+ {
+ return this;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERPrintableString.java b/core/src/main/java/org/bouncycastle/asn1/DERPrintableString.java
new file mode 100644
index 00000000..9f9b3dd1
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERPrintableString.java
@@ -0,0 +1,213 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+/**
+ * DER PrintableString object.
+ */
+public class DERPrintableString
+ extends ASN1Primitive
+ implements ASN1String
+{
+ private byte[] string;
+
+ /**
+ * return a printable string from the passed in object.
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERPrintableString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERPrintableString)
+ {
+ return (DERPrintableString)obj;
+ }
+
+ if (obj instanceof byte[])
+ {
+ try
+ {
+ return (DERPrintableString)fromByteArray((byte[])obj);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+ }
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a Printable String 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 DERPrintableString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ ASN1Primitive o = obj.getObject();
+
+ if (explicit || o instanceof DERPrintableString)
+ {
+ return getInstance(o);
+ }
+ else
+ {
+ return new DERPrintableString(ASN1OctetString.getInstance(o).getOctets());
+ }
+ }
+
+ /**
+ * basic constructor - byte encoded string.
+ */
+ DERPrintableString(
+ byte[] string)
+ {
+ this.string = string;
+ }
+
+ /**
+ * basic constructor - this does not validate the string
+ */
+ public DERPrintableString(
+ String string)
+ {
+ this(string, false);
+ }
+
+ /**
+ * Constructor with optional validation.
+ *
+ * @param string the base string to wrap.
+ * @param validate whether or not to check the string.
+ * @throws IllegalArgumentException if validate is true and the string
+ * contains characters that should not be in a PrintableString.
+ */
+ public DERPrintableString(
+ String string,
+ boolean validate)
+ {
+ if (validate && !isPrintableString(string))
+ {
+ throw new IllegalArgumentException("string contains illegal characters");
+ }
+
+ this.string = Strings.toByteArray(string);
+ }
+
+ public String getString()
+ {
+ return Strings.fromByteArray(string);
+ }
+
+ public byte[] getOctets()
+ {
+ return Arrays.clone(string);
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ {
+ return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(BERTags.PRINTABLE_STRING, string);
+ }
+
+ public int hashCode()
+ {
+ return Arrays.hashCode(string);
+ }
+
+ boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof DERPrintableString))
+ {
+ return false;
+ }
+
+ DERPrintableString s = (DERPrintableString)o;
+
+ return Arrays.areEqual(string, s.string);
+ }
+
+ public String toString()
+ {
+ return getString();
+ }
+
+ /**
+ * return true if the passed in String can be represented without
+ * loss as a PrintableString, false otherwise.
+ *
+ * @return true if in printable set, false otherwise.
+ */
+ public static boolean isPrintableString(
+ String str)
+ {
+ for (int i = str.length() - 1; i >= 0; i--)
+ {
+ char ch = str.charAt(i);
+
+ if (ch > 0x007f)
+ {
+ return false;
+ }
+
+ if ('a' <= ch && ch <= 'z')
+ {
+ continue;
+ }
+
+ if ('A' <= ch && ch <= 'Z')
+ {
+ continue;
+ }
+
+ if ('0' <= ch && ch <= '9')
+ {
+ continue;
+ }
+
+ switch (ch)
+ {
+ case ' ':
+ case '\'':
+ case '(':
+ case ')':
+ case '+':
+ case '-':
+ case '.':
+ case ':':
+ case '=':
+ case '?':
+ case '/':
+ case ',':
+ continue;
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERSequence.java b/core/src/main/java/org/bouncycastle/asn1/DERSequence.java
new file mode 100644
index 00000000..ad48a833
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERSequence.java
@@ -0,0 +1,98 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+public class DERSequence
+ extends ASN1Sequence
+{
+ private int bodyLength = -1;
+
+ /**
+ * create an empty sequence
+ */
+ public DERSequence()
+ {
+ }
+
+ /**
+ * create a sequence containing one object
+ */
+ public DERSequence(
+ ASN1Encodable obj)
+ {
+ super(obj);
+ }
+
+ /**
+ * create a sequence containing a vector of objects.
+ */
+ public DERSequence(
+ ASN1EncodableVector v)
+ {
+ super(v);
+ }
+
+ /**
+ * create a sequence containing an array of objects.
+ */
+ public DERSequence(
+ ASN1Encodable[] array)
+ {
+ super(array);
+ }
+
+ private int getBodyLength()
+ throws IOException
+ {
+ if (bodyLength < 0)
+ {
+ int length = 0;
+
+ for (Enumeration e = this.getObjects(); e.hasMoreElements();)
+ {
+ Object obj = e.nextElement();
+
+ length += ((ASN1Encodable)obj).toASN1Primitive().toDERObject().encodedLength();
+ }
+
+ bodyLength = length;
+ }
+
+ return bodyLength;
+ }
+
+ int encodedLength()
+ throws IOException
+ {
+ int length = getBodyLength();
+
+ return 1 + StreamUtil.calculateBodyLength(length) + length;
+ }
+
+ /*
+ * A note on the implementation:
+ * <p>
+ * As DER requires the constructed, definite-length model to
+ * be used for structured types, this varies slightly from the
+ * ASN.1 descriptions given. Rather than just outputting SEQUENCE,
+ * we also have to specify CONSTRUCTED, and the objects length.
+ */
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ ASN1OutputStream dOut = out.getDERSubStream();
+ int length = getBodyLength();
+
+ out.write(BERTags.SEQUENCE | BERTags.CONSTRUCTED);
+ out.writeLength(length);
+
+ for (Enumeration e = this.getObjects(); e.hasMoreElements();)
+ {
+ Object obj = e.nextElement();
+
+ dOut.writeObject((ASN1Encodable)obj);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERSequenceGenerator.java b/core/src/main/java/org/bouncycastle/asn1/DERSequenceGenerator.java
new file mode 100644
index 00000000..8cb5271e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERSequenceGenerator.java
@@ -0,0 +1,45 @@
+package org.bouncycastle.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class DERSequenceGenerator
+ extends DERGenerator
+{
+ private final ByteArrayOutputStream _bOut = new ByteArrayOutputStream();
+
+ public DERSequenceGenerator(
+ OutputStream out)
+ throws IOException
+ {
+ super(out);
+ }
+
+ public DERSequenceGenerator(
+ OutputStream out,
+ int tagNo,
+ boolean isExplicit)
+ throws IOException
+ {
+ super(out, tagNo, isExplicit);
+ }
+
+ public void addObject(
+ ASN1Encodable object)
+ throws IOException
+ {
+ object.toASN1Primitive().encode(new DEROutputStream(_bOut));
+ }
+
+ public OutputStream getRawOutputStream()
+ {
+ return _bOut;
+ }
+
+ public void close()
+ throws IOException
+ {
+ writeDEREncoded(BERTags.CONSTRUCTED | BERTags.SEQUENCE, _bOut.toByteArray());
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERSequenceParser.java b/core/src/main/java/org/bouncycastle/asn1/DERSequenceParser.java
new file mode 100644
index 00000000..376c1fd8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERSequenceParser.java
@@ -0,0 +1,38 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public class DERSequenceParser
+ implements ASN1SequenceParser
+{
+ private ASN1StreamParser _parser;
+
+ DERSequenceParser(ASN1StreamParser parser)
+ {
+ this._parser = parser;
+ }
+
+ public ASN1Encodable readObject()
+ throws IOException
+ {
+ return _parser.readObject();
+ }
+
+ public ASN1Primitive getLoadedObject()
+ throws IOException
+ {
+ return new DERSequence(_parser.readVector());
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ try
+ {
+ return getLoadedObject();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERSet.java b/core/src/main/java/org/bouncycastle/asn1/DERSet.java
new file mode 100644
index 00000000..c1faf849
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERSet.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+/**
+ * A DER encoded set object
+ */
+public class DERSet
+ extends ASN1Set
+{
+ private int bodyLength = -1;
+
+ /**
+ * create an empty set
+ */
+ public DERSet()
+ {
+ }
+
+ /**
+ * @param obj - a single object that makes up the set.
+ */
+ public DERSet(
+ ASN1Encodable obj)
+ {
+ super(obj);
+ }
+
+ /**
+ * @param v - a vector of objects making up the set.
+ */
+ public DERSet(
+ ASN1EncodableVector v)
+ {
+ super(v, true);
+ }
+
+ /**
+ * create a set from an array of objects.
+ */
+ public DERSet(
+ ASN1Encodable[] a)
+ {
+ super(a, true);
+ }
+
+ DERSet(
+ ASN1EncodableVector v,
+ boolean doSort)
+ {
+ super(v, doSort);
+ }
+
+ private int getBodyLength()
+ throws IOException
+ {
+ if (bodyLength < 0)
+ {
+ int length = 0;
+
+ for (Enumeration e = this.getObjects(); e.hasMoreElements();)
+ {
+ Object obj = e.nextElement();
+
+ length += ((ASN1Encodable)obj).toASN1Primitive().toDERObject().encodedLength();
+ }
+
+ bodyLength = length;
+ }
+
+ return bodyLength;
+ }
+
+ int encodedLength()
+ throws IOException
+ {
+ int length = getBodyLength();
+
+ return 1 + StreamUtil.calculateBodyLength(length) + length;
+ }
+
+ /*
+ * A note on the implementation:
+ * <p>
+ * As DER requires the constructed, definite-length model to
+ * be used for structured types, this varies slightly from the
+ * ASN.1 descriptions given. Rather than just outputting SET,
+ * we also have to specify CONSTRUCTED, and the objects length.
+ */
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ ASN1OutputStream dOut = out.getDERSubStream();
+ int length = getBodyLength();
+
+ out.write(BERTags.SET | BERTags.CONSTRUCTED);
+ out.writeLength(length);
+
+ for (Enumeration e = this.getObjects(); e.hasMoreElements();)
+ {
+ Object obj = e.nextElement();
+
+ dOut.writeObject((ASN1Encodable)obj);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERSetParser.java b/core/src/main/java/org/bouncycastle/asn1/DERSetParser.java
new file mode 100644
index 00000000..17702fa4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERSetParser.java
@@ -0,0 +1,38 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public class DERSetParser
+ implements ASN1SetParser
+{
+ private ASN1StreamParser _parser;
+
+ DERSetParser(ASN1StreamParser parser)
+ {
+ this._parser = parser;
+ }
+
+ public ASN1Encodable readObject()
+ throws IOException
+ {
+ return _parser.readObject();
+ }
+
+ public ASN1Primitive getLoadedObject()
+ throws IOException
+ {
+ return new DERSet(_parser.readVector(), false);
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ try
+ {
+ return getLoadedObject();
+ }
+ catch (IOException e)
+ {
+ throw new ASN1ParsingException(e.getMessage(), e);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERT61String.java b/core/src/main/java/org/bouncycastle/asn1/DERT61String.java
new file mode 100644
index 00000000..d50fb7c3
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERT61String.java
@@ -0,0 +1,144 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+/**
+ * DER T61String (also the teletex string), try not to use this if you don't need to. The standard support the encoding for
+ * this has been withdrawn.
+ */
+public class DERT61String
+ extends ASN1Primitive
+ implements ASN1String
+{
+ private byte[] string;
+
+ /**
+ * return a T61 string from the passed in object.
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERT61String getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERT61String)
+ {
+ return (DERT61String)obj;
+ }
+
+ if (obj instanceof byte[])
+ {
+ try
+ {
+ return (DERT61String)fromByteArray((byte[])obj);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+ }
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an T61 String 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 DERT61String getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ ASN1Primitive o = obj.getObject();
+
+ if (explicit || o instanceof DERT61String)
+ {
+ return getInstance(o);
+ }
+ else
+ {
+ return new DERT61String(ASN1OctetString.getInstance(o).getOctets());
+ }
+ }
+
+ /**
+ * basic constructor - string encoded as a sequence of bytes.
+ */
+ public DERT61String(
+ byte[] string)
+ {
+ this.string = string;
+ }
+
+ /**
+ * basic constructor - with string 8 bit assumed.
+ */
+ public DERT61String(
+ String string)
+ {
+ this(Strings.toByteArray(string));
+ }
+
+ /**
+ * Decode the encoded string and return it, 8 bit encoding assumed.
+ * @return the decoded String
+ */
+ public String getString()
+ {
+ return Strings.fromByteArray(string);
+ }
+
+ public String toString()
+ {
+ return getString();
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ {
+ return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(BERTags.T61_STRING, string);
+ }
+
+ /**
+ * Return the encoded string as a byte array.
+ * @return the actual bytes making up the encoded body of the T61 string.
+ */
+ public byte[] getOctets()
+ {
+ return Arrays.clone(string);
+ }
+
+ boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof DERT61String))
+ {
+ return false;
+ }
+
+ return Arrays.areEqual(string, ((DERT61String)o).string);
+ }
+
+ public int hashCode()
+ {
+ return Arrays.hashCode(string);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERT61UTF8String.java b/core/src/main/java/org/bouncycastle/asn1/DERT61UTF8String.java
new file mode 100644
index 00000000..dd817989
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERT61UTF8String.java
@@ -0,0 +1,151 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+/**
+ * DER T61String (also the teletex string) - a "modern" encapsulation that uses UTF-8. If at all possible, avoid this one! It's only for emergencies.
+ * Use UTF8String instead.
+ */
+public class DERT61UTF8String
+ extends ASN1Primitive
+ implements ASN1String
+{
+ private byte[] string;
+
+ /**
+ * return a T61 string from the passed in object. UTF-8 Encoding is assumed in this case.
+ *
+ * @throws IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERT61UTF8String getInstance(
+ Object obj)
+ {
+ if (obj instanceof DERT61String)
+ {
+ return new DERT61UTF8String(((DERT61String)obj).getOctets());
+ }
+
+ if (obj == null || obj instanceof DERT61UTF8String)
+ {
+ return (DERT61UTF8String)obj;
+ }
+
+ if (obj instanceof byte[])
+ {
+ try
+ {
+ return new DERT61UTF8String(((DERT61String)fromByteArray((byte[])obj)).getOctets());
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+ }
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an T61 String from a tagged object. UTF-8 encoding is assumed in this case.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @throws IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERT61UTF8String getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ ASN1Primitive o = obj.getObject();
+
+ if (explicit || o instanceof DERT61String || o instanceof DERT61UTF8String)
+ {
+ return getInstance(o);
+ }
+ else
+ {
+ return new DERT61UTF8String(ASN1OctetString.getInstance(o).getOctets());
+ }
+ }
+
+ /**
+ * basic constructor - string encoded as a sequence of bytes.
+ */
+ public DERT61UTF8String(
+ byte[] string)
+ {
+ this.string = string;
+ }
+
+ /**
+ * basic constructor - with string UTF8 conversion assumed.
+ */
+ public DERT61UTF8String(
+ String string)
+ {
+ this(Strings.toUTF8ByteArray(string));
+ }
+
+ /**
+ * Decode the encoded string and return it, UTF8 assumed.
+ *
+ * @return the decoded String
+ */
+ public String getString()
+ {
+ return Strings.fromUTF8ByteArray(string);
+ }
+
+ public String toString()
+ {
+ return getString();
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ {
+ return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(BERTags.T61_STRING, string);
+ }
+
+ /**
+ * Return the encoded string as a byte array.
+ *
+ * @return the actual bytes making up the encoded body of the T61 string.
+ */
+ public byte[] getOctets()
+ {
+ return Arrays.clone(string);
+ }
+
+ boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof DERT61UTF8String))
+ {
+ return false;
+ }
+
+ return Arrays.areEqual(string, ((DERT61UTF8String)o).string);
+ }
+
+ public int hashCode()
+ {
+ return Arrays.hashCode(string);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERTaggedObject.java b/core/src/main/java/org/bouncycastle/asn1/DERTaggedObject.java
new file mode 100644
index 00000000..a87a0dc9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERTaggedObject.java
@@ -0,0 +1,118 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+/**
+ * DER TaggedObject - in ASN.1 notation this is any object preceded by
+ * a [n] where n is some number - these are assumed to follow the construction
+ * rules (as with sequences).
+ */
+public class DERTaggedObject
+ extends ASN1TaggedObject
+{
+ private static final byte[] ZERO_BYTES = new byte[0];
+
+ /**
+ * @param explicit true if an explicitly tagged object.
+ * @param tagNo the tag number for this object.
+ * @param obj the tagged object.
+ */
+ public DERTaggedObject(
+ boolean explicit,
+ int tagNo,
+ ASN1Encodable obj)
+ {
+ super(explicit, tagNo, obj);
+ }
+
+ public DERTaggedObject(int tagNo, ASN1Encodable encodable)
+ {
+ super(true, tagNo, encodable);
+ }
+
+ boolean isConstructed()
+ {
+ if (!empty)
+ {
+ if (explicit)
+ {
+ return true;
+ }
+ else
+ {
+ ASN1Primitive primitive = obj.toASN1Primitive().toDERObject();
+
+ return primitive.isConstructed();
+ }
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+ int encodedLength()
+ throws IOException
+ {
+ if (!empty)
+ {
+ ASN1Primitive primitive = obj.toASN1Primitive().toDERObject();
+ int length = primitive.encodedLength();
+
+ if (explicit)
+ {
+ return StreamUtil.calculateTagLength(tagNo) + StreamUtil.calculateBodyLength(length) + length;
+ }
+ else
+ {
+ // header length already in calculation
+ length = length - 1;
+
+ return StreamUtil.calculateTagLength(tagNo) + length;
+ }
+ }
+ else
+ {
+ return StreamUtil.calculateTagLength(tagNo) + 1;
+ }
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ if (!empty)
+ {
+ ASN1Primitive primitive = obj.toASN1Primitive().toDERObject();
+
+ if (explicit)
+ {
+ out.writeTag(BERTags.CONSTRUCTED | BERTags.TAGGED, tagNo);
+ out.writeLength(primitive.encodedLength());
+ out.writeObject(primitive);
+ }
+ else
+ {
+ //
+ // need to mark constructed types...
+ //
+ int flags;
+ if (primitive.isConstructed())
+ {
+ flags = BERTags.CONSTRUCTED | BERTags.TAGGED;
+ }
+ else
+ {
+ flags = BERTags.TAGGED;
+ }
+
+ out.writeTag(flags, tagNo);
+ out.writeImplicitObject(primitive);
+ }
+ }
+ else
+ {
+ out.writeEncoded(BERTags.CONSTRUCTED | BERTags.TAGGED, tagNo, ZERO_BYTES);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERTags.java b/core/src/main/java/org/bouncycastle/asn1/DERTags.java
new file mode 100644
index 00000000..83fd7fde
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERTags.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.asn1;
+
+/**
+ * @deprecated use BERTags
+ */
+public interface DERTags
+ extends BERTags
+{
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERUTCTime.java b/core/src/main/java/org/bouncycastle/asn1/DERUTCTime.java
new file mode 100644
index 00000000..c5bd536f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERUTCTime.java
@@ -0,0 +1,278 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.SimpleTimeZone;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+/**
+ * UTC time object.
+ */
+public class DERUTCTime
+ extends ASN1Primitive
+{
+ 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);
+ }
+
+ if (obj instanceof byte[])
+ {
+ try
+ {
+ return (ASN1UTCTime)fromByteArray((byte[])obj);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+ }
+ }
+
+ 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)
+ {
+ this.time = Strings.toByteArray(time);
+ try
+ {
+ this.getDate();
+ }
+ catch (ParseException e)
+ {
+ throw new IllegalArgumentException("invalid date string: " + e.getMessage());
+ }
+ }
+
+ /**
+ * base constructer from a java.util.date object
+ */
+ public DERUTCTime(
+ Date time)
+ {
+ SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmss'Z'");
+
+ dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
+
+ this.time = Strings.toByteArray(dateF.format(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
+ * @exception ParseException if the date string cannot be parsed.
+ */
+ public Date getDate()
+ throws ParseException
+ {
+ SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmssz");
+
+ return dateF.parse(getTime());
+ }
+
+ /**
+ * return the time as an adjusted date
+ * in the range of 1950 - 2049.
+ *
+ * @return a date in the range of 1950 to 2049.
+ * @exception ParseException if the date string cannot be parsed.
+ */
+ public Date getAdjustedDate()
+ throws ParseException
+ {
+ SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
+
+ dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
+
+ return dateF.parse(getAdjustedTime());
+ }
+
+ /**
+ * 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;
+ }
+ }
+
+ 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()
+ {
+ return Arrays.hashCode(time);
+ }
+
+ public String toString()
+ {
+ return Strings.fromByteArray(time);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERUTF8String.java b/core/src/main/java/org/bouncycastle/asn1/DERUTF8String.java
new file mode 100644
index 00000000..fa34b22a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERUTF8String.java
@@ -0,0 +1,132 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+/**
+ * DER UTF8String object.
+ */
+public class DERUTF8String
+ extends ASN1Primitive
+ implements ASN1String
+{
+ private byte[] string;
+
+ /**
+ * return an UTF8 string from the passed in object.
+ *
+ * @exception IllegalArgumentException
+ * if the object cannot be converted.
+ */
+ public static DERUTF8String getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof DERUTF8String)
+ {
+ return (DERUTF8String)obj;
+ }
+
+ if (obj instanceof byte[])
+ {
+ try
+ {
+ return (DERUTF8String)fromByteArray((byte[])obj);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+ }
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: "
+ + obj.getClass().getName());
+ }
+
+ /**
+ * return an UTF8 String 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 DERUTF8String getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ ASN1Primitive o = obj.getObject();
+
+ if (explicit || o instanceof DERUTF8String)
+ {
+ return getInstance(o);
+ }
+ else
+ {
+ return new DERUTF8String(ASN1OctetString.getInstance(o).getOctets());
+ }
+ }
+
+ /**
+ * basic constructor - byte encoded string.
+ */
+ DERUTF8String(byte[] string)
+ {
+ this.string = string;
+ }
+
+ /**
+ * basic constructor
+ */
+ public DERUTF8String(String string)
+ {
+ this.string = Strings.toUTF8ByteArray(string);
+ }
+
+ public String getString()
+ {
+ return Strings.fromUTF8ByteArray(string);
+ }
+
+ public String toString()
+ {
+ return getString();
+ }
+
+ public int hashCode()
+ {
+ return Arrays.hashCode(string);
+ }
+
+ boolean asn1Equals(ASN1Primitive o)
+ {
+ if (!(o instanceof DERUTF8String))
+ {
+ return false;
+ }
+
+ DERUTF8String s = (DERUTF8String)o;
+
+ return Arrays.areEqual(string, s.string);
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ throws IOException
+ {
+ return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
+ }
+
+ void encode(ASN1OutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(BERTags.UTF8_STRING, string);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERUniversalString.java b/core/src/main/java/org/bouncycastle/asn1/DERUniversalString.java
new file mode 100644
index 00000000..51b07993
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERUniversalString.java
@@ -0,0 +1,148 @@
+package org.bouncycastle.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * DER UniversalString object.
+ */
+public class DERUniversalString
+ extends ASN1Primitive
+ implements ASN1String
+{
+ private static final char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+ private byte[] string;
+
+ /**
+ * return a Universal String from the passed in object.
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERUniversalString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERUniversalString)
+ {
+ return (DERUniversalString)obj;
+ }
+
+ if (obj instanceof byte[])
+ {
+ try
+ {
+ return (DERUniversalString)fromByteArray((byte[])obj);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("encoding error getInstance: " + e.toString());
+ }
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a Universal String 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 DERUniversalString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ ASN1Primitive o = obj.getObject();
+
+ if (explicit || o instanceof DERUniversalString)
+ {
+ return getInstance(o);
+ }
+ else
+ {
+ return new DERUniversalString(((ASN1OctetString)o).getOctets());
+ }
+ }
+
+ /**
+ * basic constructor - byte encoded string.
+ */
+ public DERUniversalString(
+ byte[] string)
+ {
+ this.string = string;
+ }
+
+ public String getString()
+ {
+ StringBuffer buf = new StringBuffer("#");
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ try
+ {
+ aOut.writeObject(this);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("internal error encoding BitString");
+ }
+
+ byte[] string = bOut.toByteArray();
+
+ for (int i = 0; i != string.length; i++)
+ {
+ buf.append(table[(string[i] >>> 4) & 0xf]);
+ buf.append(table[string[i] & 0xf]);
+ }
+
+ return buf.toString();
+ }
+
+ public String toString()
+ {
+ return getString();
+ }
+
+ public byte[] getOctets()
+ {
+ return string;
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ {
+ return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(BERTags.UNIVERSAL_STRING, this.getOctets());
+ }
+
+ boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof DERUniversalString))
+ {
+ return false;
+ }
+
+ return Arrays.areEqual(string, ((DERUniversalString)o).string);
+ }
+
+ public int hashCode()
+ {
+ return Arrays.hashCode(string);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DERVisibleString.java b/core/src/main/java/org/bouncycastle/asn1/DERVisibleString.java
new file mode 100644
index 00000000..18e7d73b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DERVisibleString.java
@@ -0,0 +1,135 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+/**
+ * DER VisibleString object.
+ */
+public class DERVisibleString
+ extends ASN1Primitive
+ implements ASN1String
+{
+ private byte[] string;
+
+ /**
+ * return a Visible String from the passed in object.
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERVisibleString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERVisibleString)
+ {
+ return (DERVisibleString)obj;
+ }
+
+ if (obj instanceof byte[])
+ {
+ try
+ {
+ return (DERVisibleString)fromByteArray((byte[])obj);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+ }
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a Visible String 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 DERVisibleString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ ASN1Primitive o = obj.getObject();
+
+ if (explicit || o instanceof DERVisibleString)
+ {
+ return getInstance(o);
+ }
+ else
+ {
+ return new DERVisibleString(ASN1OctetString.getInstance(o).getOctets());
+ }
+ }
+
+ /**
+ * basic constructor - byte encoded string.
+ */
+ DERVisibleString(
+ byte[] string)
+ {
+ this.string = string;
+ }
+
+ /**
+ * basic constructor
+ */
+ public DERVisibleString(
+ String string)
+ {
+ this.string = Strings.toByteArray(string);
+ }
+
+ public String getString()
+ {
+ return Strings.fromByteArray(string);
+ }
+
+ public String toString()
+ {
+ return getString();
+ }
+
+ public byte[] getOctets()
+ {
+ return Arrays.clone(string);
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ {
+ return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(BERTags.VISIBLE_STRING, this.string);
+ }
+
+ boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof DERVisibleString))
+ {
+ return false;
+ }
+
+ return Arrays.areEqual(string, ((DERVisibleString)o).string);
+ }
+
+ public int hashCode()
+ {
+ return Arrays.hashCode(string);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DLOutputStream.java b/core/src/main/java/org/bouncycastle/asn1/DLOutputStream.java
new file mode 100644
index 00000000..68c0ed62
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DLOutputStream.java
@@ -0,0 +1,31 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Stream that outputs encoding based on definite length.
+ */
+public class DLOutputStream
+ extends ASN1OutputStream
+{
+ public DLOutputStream(
+ OutputStream os)
+ {
+ super(os);
+ }
+
+ public void writeObject(
+ ASN1Encodable obj)
+ throws IOException
+ {
+ if (obj != null)
+ {
+ obj.toASN1Primitive().toDLObject().encode(this);
+ }
+ else
+ {
+ throw new IOException("null object detected");
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DLSequence.java b/core/src/main/java/org/bouncycastle/asn1/DLSequence.java
new file mode 100644
index 00000000..bb8ec4e6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DLSequence.java
@@ -0,0 +1,98 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+public class DLSequence
+ extends ASN1Sequence
+{
+ private int bodyLength = -1;
+
+ /**
+ * create an empty sequence
+ */
+ public DLSequence()
+ {
+ }
+
+ /**
+ * create a sequence containing one object
+ */
+ public DLSequence(
+ ASN1Encodable obj)
+ {
+ super(obj);
+ }
+
+ /**
+ * create a sequence containing a vector of objects.
+ */
+ public DLSequence(
+ ASN1EncodableVector v)
+ {
+ super(v);
+ }
+
+ /**
+ * create a sequence containing an array of objects.
+ */
+ public DLSequence(
+ ASN1Encodable[] array)
+ {
+ super(array);
+ }
+
+ private int getBodyLength()
+ throws IOException
+ {
+ if (bodyLength < 0)
+ {
+ int length = 0;
+
+ for (Enumeration e = this.getObjects(); e.hasMoreElements();)
+ {
+ Object obj = e.nextElement();
+
+ length += ((ASN1Encodable)obj).toASN1Primitive().toDLObject().encodedLength();
+ }
+
+ bodyLength = length;
+ }
+
+ return bodyLength;
+ }
+
+ int encodedLength()
+ throws IOException
+ {
+ int length = getBodyLength();
+
+ return 1 + StreamUtil.calculateBodyLength(length) + length;
+ }
+
+ /*
+ * A note on the implementation:
+ * <p>
+ * As DL requires the constructed, definite-length model to
+ * be used for structured types, this varies slightly from the
+ * ASN.1 descriptions given. Rather than just outputting SEQUENCE,
+ * we also have to specify CONSTRUCTED, and the objects length.
+ */
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ ASN1OutputStream dOut = out.getDLSubStream();
+ int length = getBodyLength();
+
+ out.write(BERTags.SEQUENCE | BERTags.CONSTRUCTED);
+ out.writeLength(length);
+
+ for (Enumeration e = this.getObjects(); e.hasMoreElements();)
+ {
+ Object obj = e.nextElement();
+
+ dOut.writeObject((ASN1Encodable)obj);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DLSet.java b/core/src/main/java/org/bouncycastle/asn1/DLSet.java
new file mode 100644
index 00000000..755754b4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DLSet.java
@@ -0,0 +1,101 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+/**
+ * A DER encoded set object
+ */
+public class DLSet
+ extends ASN1Set
+{
+ private int bodyLength = -1;
+
+ /**
+ * create an empty set
+ */
+ public DLSet()
+ {
+ }
+
+ /**
+ * @param obj - a single object that makes up the set.
+ */
+ public DLSet(
+ ASN1Encodable obj)
+ {
+ super(obj);
+ }
+
+ /**
+ * @param v - a vector of objects making up the set.
+ */
+ public DLSet(
+ ASN1EncodableVector v)
+ {
+ super(v, false);
+ }
+
+ /**
+ * create a set from an array of objects.
+ */
+ public DLSet(
+ ASN1Encodable[] a)
+ {
+ super(a, false);
+ }
+
+ private int getBodyLength()
+ throws IOException
+ {
+ if (bodyLength < 0)
+ {
+ int length = 0;
+
+ for (Enumeration e = this.getObjects(); e.hasMoreElements();)
+ {
+ Object obj = e.nextElement();
+
+ length += ((ASN1Encodable)obj).toASN1Primitive().toDLObject().encodedLength();
+ }
+
+ bodyLength = length;
+ }
+
+ return bodyLength;
+ }
+
+ int encodedLength()
+ throws IOException
+ {
+ int length = getBodyLength();
+
+ return 1 + StreamUtil.calculateBodyLength(length) + length;
+ }
+
+ /*
+ * A note on the implementation:
+ * <p>
+ * As DL requires the constructed, definite-length model to
+ * be used for structured types, this varies slightly from the
+ * ASN.1 descriptions given. Rather than just outputting SET,
+ * we also have to specify CONSTRUCTED, and the objects length.
+ */
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ ASN1OutputStream dOut = out.getDLSubStream();
+ int length = getBodyLength();
+
+ out.write(BERTags.SET | BERTags.CONSTRUCTED);
+ out.writeLength(length);
+
+ for (Enumeration e = this.getObjects(); e.hasMoreElements();)
+ {
+ Object obj = e.nextElement();
+
+ dOut.writeObject((ASN1Encodable)obj);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DLTaggedObject.java b/core/src/main/java/org/bouncycastle/asn1/DLTaggedObject.java
new file mode 100644
index 00000000..4a245dff
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DLTaggedObject.java
@@ -0,0 +1,112 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+/**
+ * Definite Length TaggedObject - in ASN.1 notation this is any object preceded by
+ * a [n] where n is some number - these are assumed to follow the construction
+ * rules (as with sequences).
+ */
+public class DLTaggedObject
+ extends ASN1TaggedObject
+{
+ private static final byte[] ZERO_BYTES = new byte[0];
+
+ /**
+ * @param explicit true if an explicitly tagged object.
+ * @param tagNo the tag number for this object.
+ * @param obj the tagged object.
+ */
+ public DLTaggedObject(
+ boolean explicit,
+ int tagNo,
+ ASN1Encodable obj)
+ {
+ super(explicit, tagNo, obj);
+ }
+
+ boolean isConstructed()
+ {
+ if (!empty)
+ {
+ if (explicit)
+ {
+ return true;
+ }
+ else
+ {
+ ASN1Primitive primitive = obj.toASN1Primitive().toDLObject();
+
+ return primitive.isConstructed();
+ }
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+ int encodedLength()
+ throws IOException
+ {
+ if (!empty)
+ {
+ int length = obj.toASN1Primitive().toDLObject().encodedLength();
+
+ if (explicit)
+ {
+ return StreamUtil.calculateTagLength(tagNo) + StreamUtil.calculateBodyLength(length) + length;
+ }
+ else
+ {
+ // header length already in calculation
+ length = length - 1;
+
+ return StreamUtil.calculateTagLength(tagNo) + length;
+ }
+ }
+ else
+ {
+ return StreamUtil.calculateTagLength(tagNo) + 1;
+ }
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ if (!empty)
+ {
+ ASN1Primitive primitive = obj.toASN1Primitive().toDLObject();
+
+ if (explicit)
+ {
+ out.writeTag(BERTags.CONSTRUCTED | BERTags.TAGGED, tagNo);
+ out.writeLength(primitive.encodedLength());
+ out.writeObject(primitive);
+ }
+ else
+ {
+ //
+ // need to mark constructed types...
+ //
+ int flags;
+ if (primitive.isConstructed())
+ {
+ flags = BERTags.CONSTRUCTED | BERTags.TAGGED;
+ }
+ else
+ {
+ flags = BERTags.TAGGED;
+ }
+
+ out.writeTag(flags, tagNo);
+ out.writeImplicitObject(primitive);
+ }
+ }
+ else
+ {
+ out.writeEncoded(BERTags.CONSTRUCTED | BERTags.TAGGED, tagNo, ZERO_BYTES);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/DefiniteLengthInputStream.java b/core/src/main/java/org/bouncycastle/asn1/DefiniteLengthInputStream.java
new file mode 100644
index 00000000..37851748
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/DefiniteLengthInputStream.java
@@ -0,0 +1,105 @@
+package org.bouncycastle.asn1;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.util.io.Streams;
+
+class DefiniteLengthInputStream
+ extends LimitedInputStream
+{
+ private static final byte[] EMPTY_BYTES = new byte[0];
+
+ private final int _originalLength;
+ private int _remaining;
+
+ DefiniteLengthInputStream(
+ InputStream in,
+ int length)
+ {
+ super(in, length);
+
+ if (length < 0)
+ {
+ throw new IllegalArgumentException("negative lengths not allowed");
+ }
+
+ this._originalLength = length;
+ this._remaining = length;
+
+ if (length == 0)
+ {
+ setParentEofDetect(true);
+ }
+ }
+
+ int getRemaining()
+ {
+ return _remaining;
+ }
+
+ public int read()
+ throws IOException
+ {
+ if (_remaining == 0)
+ {
+ return -1;
+ }
+
+ int b = _in.read();
+
+ if (b < 0)
+ {
+ throw new EOFException("DEF length " + _originalLength + " object truncated by " + _remaining);
+ }
+
+ if (--_remaining == 0)
+ {
+ setParentEofDetect(true);
+ }
+
+ return b;
+ }
+
+ public int read(byte[] buf, int off, int len)
+ throws IOException
+ {
+ if (_remaining == 0)
+ {
+ return -1;
+ }
+
+ int toRead = Math.min(len, _remaining);
+ int numRead = _in.read(buf, off, toRead);
+
+ if (numRead < 0)
+ {
+ throw new EOFException("DEF length " + _originalLength + " object truncated by " + _remaining);
+ }
+
+ if ((_remaining -= numRead) == 0)
+ {
+ setParentEofDetect(true);
+ }
+
+ return numRead;
+ }
+
+ byte[] toByteArray()
+ throws IOException
+ {
+ if (_remaining == 0)
+ {
+ return EMPTY_BYTES;
+ }
+
+ byte[] bytes = new byte[_remaining];
+ if ((_remaining -= Streams.readFully(_in, bytes)) != 0)
+ {
+ throw new EOFException("DEF length " + _originalLength + " object truncated by " + _remaining);
+ }
+ setParentEofDetect(true);
+ return bytes;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/InMemoryRepresentable.java b/core/src/main/java/org/bouncycastle/asn1/InMemoryRepresentable.java
new file mode 100644
index 00000000..a4b1492c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/InMemoryRepresentable.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public interface InMemoryRepresentable
+{
+ ASN1Primitive getLoadedObject()
+ throws IOException;
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/IndefiniteLengthInputStream.java b/core/src/main/java/org/bouncycastle/asn1/IndefiniteLengthInputStream.java
new file mode 100644
index 00000000..353da3b0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/IndefiniteLengthInputStream.java
@@ -0,0 +1,111 @@
+package org.bouncycastle.asn1;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+class IndefiniteLengthInputStream
+ extends LimitedInputStream
+{
+ private int _b1;
+ private int _b2;
+ private boolean _eofReached = false;
+ private boolean _eofOn00 = true;
+
+ IndefiniteLengthInputStream(
+ InputStream in,
+ int limit)
+ throws IOException
+ {
+ super(in, limit);
+
+ _b1 = in.read();
+ _b2 = in.read();
+
+ if (_b2 < 0)
+ {
+ // Corrupted stream
+ throw new EOFException();
+ }
+
+ checkForEof();
+ }
+
+ void setEofOn00(
+ boolean eofOn00)
+ {
+ _eofOn00 = eofOn00;
+ checkForEof();
+ }
+
+ private boolean checkForEof()
+ {
+ if (!_eofReached && _eofOn00 && (_b1 == 0x00 && _b2 == 0x00))
+ {
+ _eofReached = true;
+ setParentEofDetect(true);
+ }
+ return _eofReached;
+ }
+
+ public int read(byte[] b, int off, int len)
+ throws IOException
+ {
+ // Only use this optimisation if we aren't checking for 00
+ if (_eofOn00 || len < 3)
+ {
+ return super.read(b, off, len);
+ }
+
+ if (_eofReached)
+ {
+ return -1;
+ }
+
+ int numRead = _in.read(b, off + 2, len - 2);
+
+ if (numRead < 0)
+ {
+ // Corrupted stream
+ throw new EOFException();
+ }
+
+ b[off] = (byte)_b1;
+ b[off + 1] = (byte)_b2;
+
+ _b1 = _in.read();
+ _b2 = _in.read();
+
+ if (_b2 < 0)
+ {
+ // Corrupted stream
+ throw new EOFException();
+ }
+
+ return numRead + 2;
+ }
+
+ public int read()
+ throws IOException
+ {
+ if (checkForEof())
+ {
+ return -1;
+ }
+
+ int b = _in.read();
+
+ if (b < 0)
+ {
+ // Corrupted stream
+ throw new EOFException();
+ }
+
+ int v = _b1;
+
+ _b1 = _b2;
+ _b2 = b;
+
+ return v;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/LazyConstructionEnumeration.java b/core/src/main/java/org/bouncycastle/asn1/LazyConstructionEnumeration.java
new file mode 100644
index 00000000..31d988d4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/LazyConstructionEnumeration.java
@@ -0,0 +1,43 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+class LazyConstructionEnumeration
+ implements Enumeration
+{
+ private ASN1InputStream aIn;
+ private Object nextObj;
+
+ public LazyConstructionEnumeration(byte[] encoded)
+ {
+ aIn = new ASN1InputStream(encoded, true);
+ nextObj = readObject();
+ }
+
+ public boolean hasMoreElements()
+ {
+ return nextObj != null;
+ }
+
+ public Object nextElement()
+ {
+ Object o = nextObj;
+
+ nextObj = readObject();
+
+ return o;
+ }
+
+ private Object readObject()
+ {
+ try
+ {
+ return aIn.readObject();
+ }
+ catch (IOException e)
+ {
+ throw new ASN1ParsingException("malformed DER construction: " + e, e);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/LazyEncodedSequence.java b/core/src/main/java/org/bouncycastle/asn1/LazyEncodedSequence.java
new file mode 100644
index 00000000..c7342adf
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/LazyEncodedSequence.java
@@ -0,0 +1,109 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+/**
+ * Note: this class is for processing DER/DL encoded sequences only.
+ */
+class LazyEncodedSequence
+ extends ASN1Sequence
+{
+ private byte[] encoded;
+
+ LazyEncodedSequence(
+ byte[] encoded)
+ throws IOException
+ {
+ this.encoded = encoded;
+ }
+
+ private void parse()
+ {
+ Enumeration en = new LazyConstructionEnumeration(encoded);
+
+ while (en.hasMoreElements())
+ {
+ seq.addElement(en.nextElement());
+ }
+
+ encoded = null;
+ }
+
+ public synchronized ASN1Encodable getObjectAt(int index)
+ {
+ if (encoded != null)
+ {
+ parse();
+ }
+
+ return super.getObjectAt(index);
+ }
+
+ public synchronized Enumeration getObjects()
+ {
+ if (encoded == null)
+ {
+ return super.getObjects();
+ }
+
+ return new LazyConstructionEnumeration(encoded);
+ }
+
+ public synchronized int size()
+ {
+ if (encoded != null)
+ {
+ parse();
+ }
+
+ return super.size();
+ }
+
+ ASN1Primitive toDERObject()
+ {
+ if (encoded != null)
+ {
+ parse();
+ }
+
+ return super.toDERObject();
+ }
+
+ ASN1Primitive toDLObject()
+ {
+ if (encoded != null)
+ {
+ parse();
+ }
+
+ return super.toDLObject();
+ }
+
+ int encodedLength()
+ throws IOException
+ {
+ if (encoded != null)
+ {
+ return 1 + StreamUtil.calculateBodyLength(encoded.length) + encoded.length;
+ }
+ else
+ {
+ return super.toDLObject().encodedLength();
+ }
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ if (encoded != null)
+ {
+ out.writeEncoded(BERTags.SEQUENCE | BERTags.CONSTRUCTED, encoded);
+ }
+ else
+ {
+ super.toDLObject().encode(out);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/LimitedInputStream.java b/core/src/main/java/org/bouncycastle/asn1/LimitedInputStream.java
new file mode 100644
index 00000000..d94b0bd8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/LimitedInputStream.java
@@ -0,0 +1,32 @@
+package org.bouncycastle.asn1;
+
+import java.io.InputStream;
+
+abstract class LimitedInputStream
+ extends InputStream
+{
+ protected final InputStream _in;
+ private int _limit;
+
+ LimitedInputStream(
+ InputStream in,
+ int limit)
+ {
+ this._in = in;
+ this._limit = limit;
+ }
+
+ int getRemaining()
+ {
+ // TODO: maybe one day this can become more accurate
+ return _limit;
+ }
+
+ protected void setParentEofDetect(boolean on)
+ {
+ if (_in instanceof IndefiniteLengthInputStream)
+ {
+ ((IndefiniteLengthInputStream)_in).setEofOn00(on);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/OIDTokenizer.java b/core/src/main/java/org/bouncycastle/asn1/OIDTokenizer.java
new file mode 100644
index 00000000..54679447
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/OIDTokenizer.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.asn1;
+
+/**
+ * class for breaking up an OID into it's component tokens, ala
+ * java.util.StringTokenizer. We need this class as some of the
+ * lightweight Java environment don't support classes like
+ * StringTokenizer.
+ */
+public class OIDTokenizer
+{
+ private String oid;
+ private int index;
+
+ public OIDTokenizer(
+ String oid)
+ {
+ this.oid = oid;
+ this.index = 0;
+ }
+
+ public boolean hasMoreTokens()
+ {
+ return (index != -1);
+ }
+
+ public String nextToken()
+ {
+ if (index == -1)
+ {
+ return null;
+ }
+
+ String token;
+ int end = oid.indexOf('.', index);
+
+ if (end == -1)
+ {
+ token = oid.substring(index);
+ index = -1;
+ return token;
+ }
+
+ token = oid.substring(index, end);
+
+ index = end + 1;
+ return token;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/StreamUtil.java b/core/src/main/java/org/bouncycastle/asn1/StreamUtil.java
new file mode 100644
index 00000000..b6cb0706
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/StreamUtil.java
@@ -0,0 +1,114 @@
+package org.bouncycastle.asn1;
+
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.channels.FileChannel;
+
+class StreamUtil
+{
+ private static final long MAX_MEMORY = Runtime.getRuntime().maxMemory();
+
+ /**
+ * Find out possible longest length...
+ *
+ * @param in input stream of interest
+ * @return length calculation or MAX_VALUE.
+ */
+ static int findLimit(InputStream in)
+ {
+ if (in instanceof LimitedInputStream)
+ {
+ return ((LimitedInputStream)in).getRemaining();
+ }
+ else if (in instanceof ASN1InputStream)
+ {
+ return ((ASN1InputStream)in).getLimit();
+ }
+ else if (in instanceof ByteArrayInputStream)
+ {
+ return ((ByteArrayInputStream)in).available();
+ }
+ else if (in instanceof FileInputStream)
+ {
+ try
+ {
+ FileChannel channel = ((FileInputStream)in).getChannel();
+ long size = (channel != null) ? channel.size() : Integer.MAX_VALUE;
+
+ if (size < Integer.MAX_VALUE)
+ {
+ return (int)size;
+ }
+ }
+ catch (IOException e)
+ {
+ // ignore - they'll find out soon enough!
+ }
+ }
+
+ if (MAX_MEMORY > Integer.MAX_VALUE)
+ {
+ return Integer.MAX_VALUE;
+ }
+
+ return (int)MAX_MEMORY;
+ }
+
+ static int calculateBodyLength(
+ int length)
+ {
+ int count = 1;
+
+ if (length > 127)
+ {
+ int size = 1;
+ int val = length;
+
+ while ((val >>>= 8) != 0)
+ {
+ size++;
+ }
+
+ for (int i = (size - 1) * 8; i >= 0; i -= 8)
+ {
+ count++;
+ }
+ }
+
+ return count;
+ }
+
+ static int calculateTagLength(int tagNo)
+ throws IOException
+ {
+ int length = 1;
+
+ if (tagNo >= 31)
+ {
+ if (tagNo < 128)
+ {
+ length++;
+ }
+ else
+ {
+ byte[] stack = new byte[5];
+ int pos = stack.length;
+
+ stack[--pos] = (byte)(tagNo & 0x7F);
+
+ do
+ {
+ tagNo >>= 7;
+ stack[--pos] = (byte)(tagNo & 0x7F | 0x80);
+ }
+ while (tagNo > 127);
+
+ length += stack.length - pos;
+ }
+ }
+
+ return length;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java
new file mode 100644
index 00000000..18fc66c9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java
@@ -0,0 +1,51 @@
+package org.bouncycastle.asn1.bc;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface BCObjectIdentifiers
+{
+ /**
+ * iso.org.dod.internet.private.enterprise.legion-of-the-bouncy-castle
+ *
+ * 1.3.6.1.4.1.22554
+ */
+ public static final ASN1ObjectIdentifier bc = new ASN1ObjectIdentifier("1.3.6.1.4.1.22554");
+
+ /**
+ * pbe(1) algorithms
+ */
+ public static final ASN1ObjectIdentifier bc_pbe = new ASN1ObjectIdentifier(bc.getId() + ".1");
+
+ /**
+ * SHA-1(1)
+ */
+ public static final ASN1ObjectIdentifier bc_pbe_sha1 = new ASN1ObjectIdentifier(bc_pbe.getId() + ".1");
+
+ /**
+ * SHA-2(2) . (SHA-256(1)|SHA-384(2)|SHA-512(3)|SHA-224(4))
+ */
+ public static final ASN1ObjectIdentifier bc_pbe_sha256 = new ASN1ObjectIdentifier(bc_pbe.getId() + ".2.1");
+ public static final ASN1ObjectIdentifier bc_pbe_sha384 = new ASN1ObjectIdentifier(bc_pbe.getId() + ".2.2");
+ public static final ASN1ObjectIdentifier bc_pbe_sha512 = new ASN1ObjectIdentifier(bc_pbe.getId() + ".2.3");
+ public static final ASN1ObjectIdentifier bc_pbe_sha224 = new ASN1ObjectIdentifier(bc_pbe.getId() + ".2.4");
+
+ /**
+ * PKCS-5(1)|PKCS-12(2)
+ */
+ public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs5 = new ASN1ObjectIdentifier(bc_pbe_sha1.getId() + ".1");
+ public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12 = new ASN1ObjectIdentifier(bc_pbe_sha1.getId() + ".2");
+
+ public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs5 = new ASN1ObjectIdentifier(bc_pbe_sha256.getId() + ".1");
+ public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12 = new ASN1ObjectIdentifier(bc_pbe_sha256.getId() + ".2");
+
+ /**
+ * AES(1) . (CBC-128(2)|CBC-192(22)|CBC-256(42))
+ */
+ public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes128_cbc = new ASN1ObjectIdentifier(bc_pbe_sha1_pkcs12.getId() + ".1.2");
+ public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes192_cbc = new ASN1ObjectIdentifier(bc_pbe_sha1_pkcs12.getId() + ".1.22");
+ public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes256_cbc = new ASN1ObjectIdentifier(bc_pbe_sha1_pkcs12.getId() + ".1.42");
+
+ public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes128_cbc = new ASN1ObjectIdentifier(bc_pbe_sha256_pkcs12.getId() + ".1.2");
+ public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes192_cbc = new ASN1ObjectIdentifier(bc_pbe_sha256_pkcs12.getId() + ".1.22");
+ public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes256_cbc = new ASN1ObjectIdentifier(bc_pbe_sha256_pkcs12.getId() + ".1.42");
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/CAKeyUpdAnnContent.java b/core/src/main/java/org/bouncycastle/asn1/cmp/CAKeyUpdAnnContent.java
new file mode 100644
index 00000000..41ebd512
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/CAKeyUpdAnnContent.java
@@ -0,0 +1,80 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class CAKeyUpdAnnContent
+ extends ASN1Object
+{
+ private CMPCertificate oldWithNew;
+ private CMPCertificate newWithOld;
+ private CMPCertificate newWithNew;
+
+ private CAKeyUpdAnnContent(ASN1Sequence seq)
+ {
+ oldWithNew = CMPCertificate.getInstance(seq.getObjectAt(0));
+ newWithOld = CMPCertificate.getInstance(seq.getObjectAt(1));
+ newWithNew = CMPCertificate.getInstance(seq.getObjectAt(2));
+ }
+
+ public static CAKeyUpdAnnContent getInstance(Object o)
+ {
+ if (o instanceof CAKeyUpdAnnContent)
+ {
+ return (CAKeyUpdAnnContent)o;
+ }
+
+ if (o != null)
+ {
+ return new CAKeyUpdAnnContent(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public CAKeyUpdAnnContent(CMPCertificate oldWithNew, CMPCertificate newWithOld, CMPCertificate newWithNew)
+ {
+ this.oldWithNew = oldWithNew;
+ this.newWithOld = newWithOld;
+ this.newWithNew = newWithNew;
+ }
+
+ public CMPCertificate getOldWithNew()
+ {
+ return oldWithNew;
+ }
+
+ public CMPCertificate getNewWithOld()
+ {
+ return newWithOld;
+ }
+
+ public CMPCertificate getNewWithNew()
+ {
+ return newWithNew;
+ }
+
+ /**
+ * <pre>
+ * CAKeyUpdAnnContent ::= SEQUENCE {
+ * oldWithNew CMPCertificate, -- old pub signed with new priv
+ * newWithOld CMPCertificate, -- new pub signed with old priv
+ * newWithNew CMPCertificate -- new pub signed with new priv
+ * }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(oldWithNew);
+ v.add(newWithOld);
+ v.add(newWithNew);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/CMPCertificate.java b/core/src/main/java/org/bouncycastle/asn1/cmp/CMPCertificate.java
new file mode 100644
index 00000000..243aacb9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/CMPCertificate.java
@@ -0,0 +1,92 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.AttributeCertificate;
+import org.bouncycastle.asn1.x509.Certificate;
+
+public class CMPCertificate
+ extends ASN1Object
+ implements ASN1Choice
+{
+ private Certificate x509v3PKCert;
+ private AttributeCertificate x509v2AttrCert;
+
+ /**
+ * Note: the addition of attribute certificates is a BC extension.
+ */
+ public CMPCertificate(AttributeCertificate x509v2AttrCert)
+ {
+ this.x509v2AttrCert = x509v2AttrCert;
+ }
+
+ public CMPCertificate(Certificate x509v3PKCert)
+ {
+ if (x509v3PKCert.getVersionNumber() != 3)
+ {
+ throw new IllegalArgumentException("only version 3 certificates allowed");
+ }
+
+ this.x509v3PKCert = x509v3PKCert;
+ }
+
+ public static CMPCertificate getInstance(Object o)
+ {
+ if (o == null || o instanceof CMPCertificate)
+ {
+ return (CMPCertificate)o;
+ }
+
+ if (o instanceof ASN1Sequence || o instanceof byte[])
+ {
+ return new CMPCertificate(Certificate.getInstance(o));
+ }
+
+ if (o instanceof ASN1TaggedObject)
+ {
+ return new CMPCertificate(AttributeCertificate.getInstance(((ASN1TaggedObject)o).getObject()));
+ }
+
+ throw new IllegalArgumentException("Invalid object: " + o.getClass().getName());
+ }
+
+ public boolean isX509v3PKCert()
+ {
+ return x509v3PKCert != null;
+ }
+
+ public Certificate getX509v3PKCert()
+ {
+ return x509v3PKCert;
+ }
+
+ public AttributeCertificate getX509v2AttrCert()
+ {
+ return x509v2AttrCert;
+ }
+
+ /**
+ * <pre>
+ * CMPCertificate ::= CHOICE {
+ * x509v3PKCert Certificate
+ * x509v2AttrCert [1] AttributeCertificate
+ * }
+ * </pre>
+ * Note: the addition of attribute certificates is a BC extension.
+ *
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ if (x509v2AttrCert != null)
+ { // explicit following CMP conventions
+ return new DERTaggedObject(true, 1, x509v2AttrCert);
+ }
+
+ return x509v3PKCert.toASN1Primitive();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/CMPObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/cmp/CMPObjectIdentifiers.java
new file mode 100644
index 00000000..c43afe60
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/CMPObjectIdentifiers.java
@@ -0,0 +1,106 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface CMPObjectIdentifiers
+{
+ // RFC 4210
+
+ // id-PasswordBasedMac OBJECT IDENTIFIER ::= {1 2 840 113533 7 66 13}
+ static final ASN1ObjectIdentifier passwordBasedMac = new ASN1ObjectIdentifier("1.2.840.113533.7.66.13");
+
+ // id-DHBasedMac OBJECT IDENTIFIER ::= {1 2 840 113533 7 66 30}
+ static final ASN1ObjectIdentifier dhBasedMac = new ASN1ObjectIdentifier("1.2.840.113533.7.66.30");
+
+ // Example InfoTypeAndValue contents include, but are not limited
+ // to, the following (un-comment in this ASN.1 module and use as
+ // appropriate for a given environment):
+ //
+ // id-it-caProtEncCert OBJECT IDENTIFIER ::= {id-it 1}
+ // CAProtEncCertValue ::= CMPCertificate
+ // id-it-signKeyPairTypes OBJECT IDENTIFIER ::= {id-it 2}
+ // SignKeyPairTypesValue ::= SEQUENCE OF AlgorithmIdentifier
+ // id-it-encKeyPairTypes OBJECT IDENTIFIER ::= {id-it 3}
+ // EncKeyPairTypesValue ::= SEQUENCE OF AlgorithmIdentifier
+ // id-it-preferredSymmAlg OBJECT IDENTIFIER ::= {id-it 4}
+ // PreferredSymmAlgValue ::= AlgorithmIdentifier
+ // id-it-caKeyUpdateInfo OBJECT IDENTIFIER ::= {id-it 5}
+ // CAKeyUpdateInfoValue ::= CAKeyUpdAnnContent
+ // id-it-currentCRL OBJECT IDENTIFIER ::= {id-it 6}
+ // CurrentCRLValue ::= CertificateList
+ // id-it-unsupportedOIDs OBJECT IDENTIFIER ::= {id-it 7}
+ // UnsupportedOIDsValue ::= SEQUENCE OF OBJECT IDENTIFIER
+ // id-it-keyPairParamReq OBJECT IDENTIFIER ::= {id-it 10}
+ // KeyPairParamReqValue ::= OBJECT IDENTIFIER
+ // id-it-keyPairParamRep OBJECT IDENTIFIER ::= {id-it 11}
+ // KeyPairParamRepValue ::= AlgorithmIdentifer
+ // id-it-revPassphrase OBJECT IDENTIFIER ::= {id-it 12}
+ // RevPassphraseValue ::= EncryptedValue
+ // id-it-implicitConfirm OBJECT IDENTIFIER ::= {id-it 13}
+ // ImplicitConfirmValue ::= NULL
+ // id-it-confirmWaitTime OBJECT IDENTIFIER ::= {id-it 14}
+ // ConfirmWaitTimeValue ::= GeneralizedTime
+ // id-it-origPKIMessage OBJECT IDENTIFIER ::= {id-it 15}
+ // OrigPKIMessageValue ::= PKIMessages
+ // id-it-suppLangTags OBJECT IDENTIFIER ::= {id-it 16}
+ // SuppLangTagsValue ::= SEQUENCE OF UTF8String
+ //
+ // where
+ //
+ // id-pkix OBJECT IDENTIFIER ::= {
+ // iso(1) identified-organization(3)
+ // dod(6) internet(1) security(5) mechanisms(5) pkix(7)}
+ // and
+ // id-it OBJECT IDENTIFIER ::= {id-pkix 4}
+ static final ASN1ObjectIdentifier it_caProtEncCert = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.1");
+ static final ASN1ObjectIdentifier it_signKeyPairTypes = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.2");
+ static final ASN1ObjectIdentifier it_encKeyPairTypes = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.3");
+ static final ASN1ObjectIdentifier it_preferredSymAlg = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.4");
+ static final ASN1ObjectIdentifier it_caKeyUpdateInfo = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.5");
+ static final ASN1ObjectIdentifier it_currentCRL = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.6");
+ static final ASN1ObjectIdentifier it_unsupportedOIDs = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.7");
+ static final ASN1ObjectIdentifier it_keyPairParamReq = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.10");
+ static final ASN1ObjectIdentifier it_keyPairParamRep = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.11");
+ static final ASN1ObjectIdentifier it_revPassphrase = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.12");
+ static final ASN1ObjectIdentifier it_implicitConfirm = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.13");
+ static final ASN1ObjectIdentifier it_confirmWaitTime = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.14");
+ static final ASN1ObjectIdentifier it_origPKIMessage = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.15");
+ static final ASN1ObjectIdentifier it_suppLangTags = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.16");
+
+ // RFC 4211
+
+ // id-pkix OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
+ // dod(6) internet(1) security(5) mechanisms(5) pkix(7) }
+ //
+ // arc for Internet X.509 PKI protocols and their components
+ // id-pkip OBJECT IDENTIFIER :: { id-pkix pkip(5) }
+ //
+ // arc for Registration Controls in CRMF
+ // id-regCtrl OBJECT IDENTIFIER ::= { id-pkip regCtrl(1) }
+ //
+ // arc for Registration Info in CRMF
+ // id-regInfo OBJECT IDENTIFIER ::= { id-pkip id-regInfo(2) }
+
+ static final ASN1ObjectIdentifier regCtrl_regToken = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1.1");
+ static final ASN1ObjectIdentifier regCtrl_authenticator = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1.2");
+ static final ASN1ObjectIdentifier regCtrl_pkiPublicationInfo = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1.3");
+ static final ASN1ObjectIdentifier regCtrl_pkiArchiveOptions = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1.4");
+ static final ASN1ObjectIdentifier regCtrl_oldCertID = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1.5");
+ static final ASN1ObjectIdentifier regCtrl_protocolEncrKey = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1.6");
+
+ // From RFC4210:
+ // id-regCtrl-altCertTemplate OBJECT IDENTIFIER ::= {id-regCtrl 7}
+ static final ASN1ObjectIdentifier regCtrl_altCertTemplate = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1.7");
+
+ static final ASN1ObjectIdentifier regInfo_utf8Pairs = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.2.1");
+ static final ASN1ObjectIdentifier regInfo_certReq = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.2.2");
+
+ // id-smime OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+ // us(840) rsadsi(113549) pkcs(1) pkcs9(9) 16 }
+ //
+ // id-ct OBJECT IDENTIFIER ::= { id-smime 1 } -- content types
+ //
+ // id-ct-encKeyWithID OBJECT IDENTIFIER ::= {id-ct 21}
+ static final ASN1ObjectIdentifier ct_encKeyWithID = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.1.21");
+
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/CRLAnnContent.java b/core/src/main/java/org/bouncycastle/asn1/cmp/CRLAnnContent.java
new file mode 100644
index 00000000..10948ae7
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/CRLAnnContent.java
@@ -0,0 +1,61 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.CertificateList;
+
+public class CRLAnnContent
+ extends ASN1Object
+{
+ private ASN1Sequence content;
+
+ private CRLAnnContent(ASN1Sequence seq)
+ {
+ content = seq;
+ }
+
+ public static CRLAnnContent getInstance(Object o)
+ {
+ if (o instanceof CRLAnnContent)
+ {
+ return (CRLAnnContent)o;
+ }
+
+ if (o != null)
+ {
+ return new CRLAnnContent(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public CRLAnnContent(CertificateList crl)
+ {
+ this.content = new DERSequence(crl);
+ }
+
+ public CertificateList[] getCertificateLists()
+ {
+ CertificateList[] result = new CertificateList[content.size()];
+
+ for (int i = 0; i != result.length; i++)
+ {
+ result[i] = CertificateList.getInstance(content.getObjectAt(i));
+ }
+
+ return result;
+ }
+
+ /**
+ * <pre>
+ * CRLAnnContent ::= SEQUENCE OF CertificateList
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return content;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/CertConfirmContent.java b/core/src/main/java/org/bouncycastle/asn1/cmp/CertConfirmContent.java
new file mode 100644
index 00000000..e4d786f1
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/CertConfirmContent.java
@@ -0,0 +1,54 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+
+public class CertConfirmContent
+ extends ASN1Object
+{
+ private ASN1Sequence content;
+
+ private CertConfirmContent(ASN1Sequence seq)
+ {
+ content = seq;
+ }
+
+ public static CertConfirmContent getInstance(Object o)
+ {
+ if (o instanceof CertConfirmContent)
+ {
+ return (CertConfirmContent)o;
+ }
+
+ if (o != null)
+ {
+ return new CertConfirmContent(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public CertStatus[] toCertStatusArray()
+ {
+ CertStatus[] result = new CertStatus[content.size()];
+
+ for (int i = 0; i != result.length; i++)
+ {
+ result[i] = CertStatus.getInstance(content.getObjectAt(i));
+ }
+
+ return result;
+ }
+
+ /**
+ * <pre>
+ * CertConfirmContent ::= SEQUENCE OF CertStatus
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return content;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/CertOrEncCert.java b/core/src/main/java/org/bouncycastle/asn1/cmp/CertOrEncCert.java
new file mode 100644
index 00000000..b94a79c3
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/CertOrEncCert.java
@@ -0,0 +1,96 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.crmf.EncryptedValue;
+
+public class CertOrEncCert
+ extends ASN1Object
+ implements ASN1Choice
+{
+ private CMPCertificate certificate;
+ private EncryptedValue encryptedCert;
+
+ private CertOrEncCert(ASN1TaggedObject tagged)
+ {
+ if (tagged.getTagNo() == 0)
+ {
+ certificate = CMPCertificate.getInstance(tagged.getObject());
+ }
+ else if (tagged.getTagNo() == 1)
+ {
+ encryptedCert = EncryptedValue.getInstance(tagged.getObject());
+ }
+ else
+ {
+ throw new IllegalArgumentException("unknown tag: " + tagged.getTagNo());
+ }
+ }
+
+ public static CertOrEncCert getInstance(Object o)
+ {
+ if (o instanceof CertOrEncCert)
+ {
+ return (CertOrEncCert)o;
+ }
+
+ if (o instanceof ASN1TaggedObject)
+ {
+ return new CertOrEncCert((ASN1TaggedObject)o);
+ }
+
+ return null;
+ }
+
+ public CertOrEncCert(CMPCertificate certificate)
+ {
+ if (certificate == null)
+ {
+ throw new IllegalArgumentException("'certificate' cannot be null");
+ }
+
+ this.certificate = certificate;
+ }
+
+ public CertOrEncCert(EncryptedValue encryptedCert)
+ {
+ if (encryptedCert == null)
+ {
+ throw new IllegalArgumentException("'encryptedCert' cannot be null");
+ }
+
+ this.encryptedCert = encryptedCert;
+ }
+
+ public CMPCertificate getCertificate()
+ {
+ return certificate;
+ }
+
+ public EncryptedValue getEncryptedCert()
+ {
+ return encryptedCert;
+ }
+
+ /**
+ * <pre>
+ * CertOrEncCert ::= CHOICE {
+ * certificate [0] CMPCertificate,
+ * encryptedCert [1] EncryptedValue
+ * }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ if (certificate != null)
+ {
+ return new DERTaggedObject(true, 0, certificate);
+ }
+
+ return new DERTaggedObject(true, 1, encryptedCert);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/CertRepMessage.java b/core/src/main/java/org/bouncycastle/asn1/cmp/CertRepMessage.java
new file mode 100644
index 00000000..6180796c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/CertRepMessage.java
@@ -0,0 +1,123 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class CertRepMessage
+ extends ASN1Object
+{
+ private ASN1Sequence caPubs;
+ private ASN1Sequence response;
+
+ private CertRepMessage(ASN1Sequence seq)
+ {
+ int index = 0;
+
+ if (seq.size() > 1)
+ {
+ caPubs = ASN1Sequence.getInstance((ASN1TaggedObject)seq.getObjectAt(index++), true);
+ }
+
+ response = ASN1Sequence.getInstance(seq.getObjectAt(index));
+ }
+
+ public static CertRepMessage getInstance(Object o)
+ {
+ if (o instanceof CertRepMessage)
+ {
+ return (CertRepMessage)o;
+ }
+
+ if (o != null)
+ {
+ return new CertRepMessage(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public CertRepMessage(CMPCertificate[] caPubs, CertResponse[] response)
+ {
+ if (response == null)
+ {
+ throw new IllegalArgumentException("'response' cannot be null");
+ }
+
+ if (caPubs != null)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ for (int i = 0; i < caPubs.length; i++)
+ {
+ v.add(caPubs[i]);
+ }
+ this.caPubs = new DERSequence(v);
+ }
+
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ for (int i = 0; i < response.length; i++)
+ {
+ v.add(response[i]);
+ }
+ this.response = new DERSequence(v);
+ }
+ }
+
+ public CMPCertificate[] getCaPubs()
+ {
+ if (caPubs == null)
+ {
+ return null;
+ }
+
+ CMPCertificate[] results = new CMPCertificate[caPubs.size()];
+
+ for (int i = 0; i != results.length; i++)
+ {
+ results[i] = CMPCertificate.getInstance(caPubs.getObjectAt(i));
+ }
+
+ return results;
+ }
+
+ public CertResponse[] getResponse()
+ {
+ CertResponse[] results = new CertResponse[response.size()];
+
+ for (int i = 0; i != results.length; i++)
+ {
+ results[i] = CertResponse.getInstance(response.getObjectAt(i));
+ }
+
+ return results;
+ }
+
+ /**
+ * <pre>
+ * CertRepMessage ::= SEQUENCE {
+ * caPubs [1] SEQUENCE SIZE (1..MAX) OF CMPCertificate
+ * OPTIONAL,
+ * response SEQUENCE OF CertResponse
+ * }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (caPubs != null)
+ {
+ v.add(new DERTaggedObject(true, 1, caPubs));
+ }
+
+ v.add(response);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/CertResponse.java b/core/src/main/java/org/bouncycastle/asn1/cmp/CertResponse.java
new file mode 100644
index 00000000..794e7bd8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/CertResponse.java
@@ -0,0 +1,139 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class CertResponse
+ extends ASN1Object
+{
+ private ASN1Integer certReqId;
+ private PKIStatusInfo status;
+ private CertifiedKeyPair certifiedKeyPair;
+ private ASN1OctetString rspInfo;
+
+ private CertResponse(ASN1Sequence seq)
+ {
+ certReqId = ASN1Integer.getInstance(seq.getObjectAt(0));
+ status = PKIStatusInfo.getInstance(seq.getObjectAt(1));
+
+ if (seq.size() >= 3)
+ {
+ if (seq.size() == 3)
+ {
+ ASN1Encodable o = seq.getObjectAt(2);
+ if (o instanceof ASN1OctetString)
+ {
+ rspInfo = ASN1OctetString.getInstance(o);
+ }
+ else
+ {
+ certifiedKeyPair = CertifiedKeyPair.getInstance(o);
+ }
+ }
+ else
+ {
+ certifiedKeyPair = CertifiedKeyPair.getInstance(seq.getObjectAt(2));
+ rspInfo = ASN1OctetString.getInstance(seq.getObjectAt(3));
+ }
+ }
+ }
+
+ public static CertResponse getInstance(Object o)
+ {
+ if (o instanceof CertResponse)
+ {
+ return (CertResponse)o;
+ }
+
+ if (o != null)
+ {
+ return new CertResponse(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public CertResponse(
+ ASN1Integer certReqId,
+ PKIStatusInfo status)
+ {
+ this(certReqId, status, null, null);
+ }
+
+ public CertResponse(
+ ASN1Integer certReqId,
+ PKIStatusInfo status,
+ CertifiedKeyPair certifiedKeyPair,
+ ASN1OctetString rspInfo)
+ {
+ if (certReqId == null)
+ {
+ throw new IllegalArgumentException("'certReqId' cannot be null");
+ }
+ if (status == null)
+ {
+ throw new IllegalArgumentException("'status' cannot be null");
+ }
+ this.certReqId = certReqId;
+ this.status = status;
+ this.certifiedKeyPair = certifiedKeyPair;
+ this.rspInfo = rspInfo;
+ }
+
+ public ASN1Integer getCertReqId()
+ {
+ return certReqId;
+ }
+
+ public PKIStatusInfo getStatus()
+ {
+ return status;
+ }
+
+ public CertifiedKeyPair getCertifiedKeyPair()
+ {
+ return certifiedKeyPair;
+ }
+
+ /**
+ * <pre>
+ * CertResponse ::= SEQUENCE {
+ * certReqId INTEGER,
+ * -- to match this response with corresponding request (a value
+ * -- of -1 is to be used if certReqId is not specified in the
+ * -- corresponding request)
+ * status PKIStatusInfo,
+ * certifiedKeyPair CertifiedKeyPair OPTIONAL,
+ * rspInfo OCTET STRING OPTIONAL
+ * -- analogous to the id-regInfo-utf8Pairs string defined
+ * -- for regInfo in CertReqMsg [CRMF]
+ * }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(certReqId);
+ v.add(status);
+
+ if (certifiedKeyPair != null)
+ {
+ v.add(certifiedKeyPair);
+ }
+
+ if (rspInfo != null)
+ {
+ v.add(rspInfo);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/CertStatus.java b/core/src/main/java/org/bouncycastle/asn1/cmp/CertStatus.java
new file mode 100644
index 00000000..c92b2a24
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/CertStatus.java
@@ -0,0 +1,102 @@
+package org.bouncycastle.asn1.cmp;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+
+public class CertStatus
+ extends ASN1Object
+{
+ private ASN1OctetString certHash;
+ private ASN1Integer certReqId;
+ private PKIStatusInfo statusInfo;
+
+ private CertStatus(ASN1Sequence seq)
+ {
+ certHash = ASN1OctetString.getInstance(seq.getObjectAt(0));
+ certReqId = ASN1Integer.getInstance(seq.getObjectAt(1));
+
+ if (seq.size() > 2)
+ {
+ statusInfo = PKIStatusInfo.getInstance(seq.getObjectAt(2));
+ }
+ }
+
+ public CertStatus(byte[] certHash, BigInteger certReqId)
+ {
+ this.certHash = new DEROctetString(certHash);
+ this.certReqId = new ASN1Integer(certReqId);
+ }
+
+ public CertStatus(byte[] certHash, BigInteger certReqId, PKIStatusInfo statusInfo)
+ {
+ this.certHash = new DEROctetString(certHash);
+ this.certReqId = new ASN1Integer(certReqId);
+ this.statusInfo = statusInfo;
+ }
+
+ public static CertStatus getInstance(Object o)
+ {
+ if (o instanceof CertStatus)
+ {
+ return (CertStatus)o;
+ }
+
+ if (o != null)
+ {
+ return new CertStatus(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public ASN1OctetString getCertHash()
+ {
+ return certHash;
+ }
+
+ public ASN1Integer getCertReqId()
+ {
+ return certReqId;
+ }
+
+ public PKIStatusInfo getStatusInfo()
+ {
+ return statusInfo;
+ }
+
+ /**
+ * <pre>
+ * CertStatus ::= SEQUENCE {
+ * certHash OCTET STRING,
+ * -- the hash of the certificate, using the same hash algorithm
+ * -- as is used to create and verify the certificate signature
+ * certReqId INTEGER,
+ * -- to match this confirmation with the corresponding req/rep
+ * statusInfo PKIStatusInfo OPTIONAL
+ * }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(certHash);
+ v.add(certReqId);
+
+ if (statusInfo != null)
+ {
+ v.add(statusInfo);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/CertifiedKeyPair.java b/core/src/main/java/org/bouncycastle/asn1/cmp/CertifiedKeyPair.java
new file mode 100644
index 00000000..949ad730
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/CertifiedKeyPair.java
@@ -0,0 +1,127 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.crmf.EncryptedValue;
+import org.bouncycastle.asn1.crmf.PKIPublicationInfo;
+
+public class CertifiedKeyPair
+ extends ASN1Object
+{
+ private CertOrEncCert certOrEncCert;
+ private EncryptedValue privateKey;
+ private PKIPublicationInfo publicationInfo;
+
+ private CertifiedKeyPair(ASN1Sequence seq)
+ {
+ certOrEncCert = CertOrEncCert.getInstance(seq.getObjectAt(0));
+
+ if (seq.size() >= 2)
+ {
+ if (seq.size() == 2)
+ {
+ ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(seq.getObjectAt(1));
+ if (tagged.getTagNo() == 0)
+ {
+ privateKey = EncryptedValue.getInstance(tagged.getObject());
+ }
+ else
+ {
+ publicationInfo = PKIPublicationInfo.getInstance(tagged.getObject());
+ }
+ }
+ else
+ {
+ privateKey = EncryptedValue.getInstance(ASN1TaggedObject.getInstance(seq.getObjectAt(1)));
+ publicationInfo = PKIPublicationInfo.getInstance(ASN1TaggedObject.getInstance(seq.getObjectAt(2)));
+ }
+ }
+ }
+
+ public static CertifiedKeyPair getInstance(Object o)
+ {
+ if (o instanceof CertifiedKeyPair)
+ {
+ return (CertifiedKeyPair)o;
+ }
+
+ if (o != null)
+ {
+ return new CertifiedKeyPair(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public CertifiedKeyPair(
+ CertOrEncCert certOrEncCert)
+ {
+ this(certOrEncCert, null, null);
+ }
+
+ public CertifiedKeyPair(
+ CertOrEncCert certOrEncCert,
+ EncryptedValue privateKey,
+ PKIPublicationInfo publicationInfo
+ )
+ {
+ if (certOrEncCert == null)
+ {
+ throw new IllegalArgumentException("'certOrEncCert' cannot be null");
+ }
+
+ this.certOrEncCert = certOrEncCert;
+ this.privateKey = privateKey;
+ this.publicationInfo = publicationInfo;
+ }
+
+ public CertOrEncCert getCertOrEncCert()
+ {
+ return certOrEncCert;
+ }
+
+ public EncryptedValue getPrivateKey()
+ {
+ return privateKey;
+ }
+
+ public PKIPublicationInfo getPublicationInfo()
+ {
+ return publicationInfo;
+ }
+
+ /**
+ * <pre>
+ * CertifiedKeyPair ::= SEQUENCE {
+ * certOrEncCert CertOrEncCert,
+ * privateKey [0] EncryptedValue OPTIONAL,
+ * -- see [CRMF] for comment on encoding
+ * publicationInfo [1] PKIPublicationInfo OPTIONAL
+ * }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(certOrEncCert);
+
+ if (privateKey != null)
+ {
+ v.add(new DERTaggedObject(true, 0, privateKey));
+ }
+
+ if (publicationInfo != null)
+ {
+ v.add(new DERTaggedObject(true, 1, publicationInfo));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/Challenge.java b/core/src/main/java/org/bouncycastle/asn1/cmp/Challenge.java
new file mode 100644
index 00000000..60eb1bac
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/Challenge.java
@@ -0,0 +1,120 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class Challenge
+ extends ASN1Object
+{
+ private AlgorithmIdentifier owf;
+ private ASN1OctetString witness;
+ private ASN1OctetString challenge;
+
+ private Challenge(ASN1Sequence seq)
+ {
+ int index = 0;
+
+ if (seq.size() == 3)
+ {
+ owf = AlgorithmIdentifier.getInstance(seq.getObjectAt(index++));
+ }
+
+ witness = ASN1OctetString.getInstance(seq.getObjectAt(index++));
+ challenge = ASN1OctetString.getInstance(seq.getObjectAt(index));
+ }
+
+ public static Challenge getInstance(Object o)
+ {
+ if (o instanceof Challenge)
+ {
+ return (Challenge)o;
+ }
+
+ if (o != null)
+ {
+ return new Challenge(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public Challenge(byte[] witness, byte[] challenge)
+ {
+ this(null, witness, challenge);
+ }
+
+ public Challenge(AlgorithmIdentifier owf, byte[] witness, byte[] challenge)
+ {
+ this.owf = owf;
+ this.witness = new DEROctetString(witness);
+ this.challenge = new DEROctetString(challenge);
+ }
+
+ public AlgorithmIdentifier getOwf()
+ {
+ return owf;
+ }
+
+ public byte[] getWitness()
+ {
+ return witness.getOctets();
+ }
+
+ public byte[] getChallenge()
+ {
+ return challenge.getOctets();
+ }
+
+ /**
+ * <pre>
+ * Challenge ::= SEQUENCE {
+ * owf AlgorithmIdentifier OPTIONAL,
+ *
+ * -- MUST be present in the first Challenge; MAY be omitted in
+ * -- any subsequent Challenge in POPODecKeyChallContent (if
+ * -- omitted, then the owf used in the immediately preceding
+ * -- Challenge is to be used).
+ *
+ * witness OCTET STRING,
+ * -- the result of applying the one-way function (owf) to a
+ * -- randomly-generated INTEGER, A. [Note that a different
+ * -- INTEGER MUST be used for each Challenge.]
+ * challenge OCTET STRING
+ * -- the encryption (under the public key for which the cert.
+ * -- request is being made) of Rand, where Rand is specified as
+ * -- Rand ::= SEQUENCE {
+ * -- int INTEGER,
+ * -- - the randomly-generated INTEGER A (above)
+ * -- sender GeneralName
+ * -- - the sender's name (as included in PKIHeader)
+ * -- }
+ * }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ addOptional(v, owf);
+ v.add(witness);
+ v.add(challenge);
+
+ return new DERSequence(v);
+ }
+
+ private void addOptional(ASN1EncodableVector v, ASN1Encodable obj)
+ {
+ if (obj != null)
+ {
+ v.add(obj);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/ErrorMsgContent.java b/core/src/main/java/org/bouncycastle/asn1/cmp/ErrorMsgContent.java
new file mode 100644
index 00000000..5dc1ac38
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/ErrorMsgContent.java
@@ -0,0 +1,121 @@
+package org.bouncycastle.asn1.cmp;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class ErrorMsgContent
+ extends ASN1Object
+{
+ private PKIStatusInfo pkiStatusInfo;
+ private ASN1Integer errorCode;
+ private PKIFreeText errorDetails;
+
+ private ErrorMsgContent(ASN1Sequence seq)
+ {
+ Enumeration en = seq.getObjects();
+
+ pkiStatusInfo = PKIStatusInfo.getInstance(en.nextElement());
+
+ while (en.hasMoreElements())
+ {
+ Object o = en.nextElement();
+
+ if (o instanceof ASN1Integer)
+ {
+ errorCode = ASN1Integer.getInstance(o);
+ }
+ else
+ {
+ errorDetails = PKIFreeText.getInstance(o);
+ }
+ }
+ }
+
+ public static ErrorMsgContent getInstance(Object o)
+ {
+ if (o instanceof ErrorMsgContent)
+ {
+ return (ErrorMsgContent)o;
+ }
+
+ if (o != null)
+ {
+ return new ErrorMsgContent(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public ErrorMsgContent(PKIStatusInfo pkiStatusInfo)
+ {
+ this(pkiStatusInfo, null, null);
+ }
+
+ public ErrorMsgContent(
+ PKIStatusInfo pkiStatusInfo,
+ ASN1Integer errorCode,
+ PKIFreeText errorDetails)
+ {
+ if (pkiStatusInfo == null)
+ {
+ throw new IllegalArgumentException("'pkiStatusInfo' cannot be null");
+ }
+
+ this.pkiStatusInfo = pkiStatusInfo;
+ this.errorCode = errorCode;
+ this.errorDetails = errorDetails;
+ }
+
+ public PKIStatusInfo getPKIStatusInfo()
+ {
+ return pkiStatusInfo;
+ }
+
+ public ASN1Integer getErrorCode()
+ {
+ return errorCode;
+ }
+
+ public PKIFreeText getErrorDetails()
+ {
+ return errorDetails;
+ }
+
+ /**
+ * <pre>
+ * ErrorMsgContent ::= SEQUENCE {
+ * pKIStatusInfo PKIStatusInfo,
+ * errorCode INTEGER OPTIONAL,
+ * -- implementation-specific error codes
+ * errorDetails PKIFreeText OPTIONAL
+ * -- implementation-specific error details
+ * }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(pkiStatusInfo);
+ addOptional(v, errorCode);
+ addOptional(v, errorDetails);
+
+ return new DERSequence(v);
+ }
+
+ private void addOptional(ASN1EncodableVector v, ASN1Encodable obj)
+ {
+ if (obj != null)
+ {
+ v.add(obj);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/GenMsgContent.java b/core/src/main/java/org/bouncycastle/asn1/cmp/GenMsgContent.java
new file mode 100644
index 00000000..109d1801
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/GenMsgContent.java
@@ -0,0 +1,71 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class GenMsgContent
+ extends ASN1Object
+{
+ private ASN1Sequence content;
+
+ private GenMsgContent(ASN1Sequence seq)
+ {
+ content = seq;
+ }
+
+ public static GenMsgContent getInstance(Object o)
+ {
+ if (o instanceof GenMsgContent)
+ {
+ return (GenMsgContent)o;
+ }
+
+ if (o != null)
+ {
+ return new GenMsgContent(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public GenMsgContent(InfoTypeAndValue itv)
+ {
+ content = new DERSequence(itv);
+ }
+
+ public GenMsgContent(InfoTypeAndValue[] itv)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ for (int i = 0; i < itv.length; i++)
+ {
+ v.add(itv[i]);
+ }
+ content = new DERSequence(v);
+ }
+
+ public InfoTypeAndValue[] toInfoTypeAndValueArray()
+ {
+ InfoTypeAndValue[] result = new InfoTypeAndValue[content.size()];
+
+ for (int i = 0; i != result.length; i++)
+ {
+ result[i] = InfoTypeAndValue.getInstance(content.getObjectAt(i));
+ }
+
+ return result;
+ }
+
+ /**
+ * <pre>
+ * GenMsgContent ::= SEQUENCE OF InfoTypeAndValue
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return content;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/GenRepContent.java b/core/src/main/java/org/bouncycastle/asn1/cmp/GenRepContent.java
new file mode 100644
index 00000000..aca4d30f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/GenRepContent.java
@@ -0,0 +1,71 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class GenRepContent
+ extends ASN1Object
+{
+ private ASN1Sequence content;
+
+ private GenRepContent(ASN1Sequence seq)
+ {
+ content = seq;
+ }
+
+ public static GenRepContent getInstance(Object o)
+ {
+ if (o instanceof GenRepContent)
+ {
+ return (GenRepContent)o;
+ }
+
+ if (o != null)
+ {
+ return new GenRepContent(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public GenRepContent(InfoTypeAndValue itv)
+ {
+ content = new DERSequence(itv);
+ }
+
+ public GenRepContent(InfoTypeAndValue[] itv)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ for (int i = 0; i < itv.length; i++)
+ {
+ v.add(itv[i]);
+ }
+ content = new DERSequence(v);
+ }
+
+ public InfoTypeAndValue[] toInfoTypeAndValueArray()
+ {
+ InfoTypeAndValue[] result = new InfoTypeAndValue[content.size()];
+
+ for (int i = 0; i != result.length; i++)
+ {
+ result[i] = InfoTypeAndValue.getInstance(content.getObjectAt(i));
+ }
+
+ return result;
+ }
+
+ /**
+ * <pre>
+ * GenRepContent ::= SEQUENCE OF InfoTypeAndValue
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return content;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/InfoTypeAndValue.java b/core/src/main/java/org/bouncycastle/asn1/cmp/InfoTypeAndValue.java
new file mode 100644
index 00000000..9405462e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/InfoTypeAndValue.java
@@ -0,0 +1,132 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * Example InfoTypeAndValue contents include, but are not limited
+ * to, the following (un-comment in this ASN.1 module and use as
+ * appropriate for a given environment):
+ * <pre>
+ * id-it-caProtEncCert OBJECT IDENTIFIER ::= {id-it 1}
+ * CAProtEncCertValue ::= CMPCertificate
+ * id-it-signKeyPairTypes OBJECT IDENTIFIER ::= {id-it 2}
+ * SignKeyPairTypesValue ::= SEQUENCE OF AlgorithmIdentifier
+ * id-it-encKeyPairTypes OBJECT IDENTIFIER ::= {id-it 3}
+ * EncKeyPairTypesValue ::= SEQUENCE OF AlgorithmIdentifier
+ * id-it-preferredSymmAlg OBJECT IDENTIFIER ::= {id-it 4}
+ * PreferredSymmAlgValue ::= AlgorithmIdentifier
+ * id-it-caKeyUpdateInfo OBJECT IDENTIFIER ::= {id-it 5}
+ * CAKeyUpdateInfoValue ::= CAKeyUpdAnnContent
+ * id-it-currentCRL OBJECT IDENTIFIER ::= {id-it 6}
+ * CurrentCRLValue ::= CertificateList
+ * id-it-unsupportedOIDs OBJECT IDENTIFIER ::= {id-it 7}
+ * UnsupportedOIDsValue ::= SEQUENCE OF OBJECT IDENTIFIER
+ * id-it-keyPairParamReq OBJECT IDENTIFIER ::= {id-it 10}
+ * KeyPairParamReqValue ::= OBJECT IDENTIFIER
+ * id-it-keyPairParamRep OBJECT IDENTIFIER ::= {id-it 11}
+ * KeyPairParamRepValue ::= AlgorithmIdentifer
+ * id-it-revPassphrase OBJECT IDENTIFIER ::= {id-it 12}
+ * RevPassphraseValue ::= EncryptedValue
+ * id-it-implicitConfirm OBJECT IDENTIFIER ::= {id-it 13}
+ * ImplicitConfirmValue ::= NULL
+ * id-it-confirmWaitTime OBJECT IDENTIFIER ::= {id-it 14}
+ * ConfirmWaitTimeValue ::= GeneralizedTime
+ * id-it-origPKIMessage OBJECT IDENTIFIER ::= {id-it 15}
+ * OrigPKIMessageValue ::= PKIMessages
+ * id-it-suppLangTags OBJECT IDENTIFIER ::= {id-it 16}
+ * SuppLangTagsValue ::= SEQUENCE OF UTF8String
+ *
+ * where
+ *
+ * id-pkix OBJECT IDENTIFIER ::= {
+ * iso(1) identified-organization(3)
+ * dod(6) internet(1) security(5) mechanisms(5) pkix(7)}
+ * and
+ * id-it OBJECT IDENTIFIER ::= {id-pkix 4}
+ * </pre>
+ */
+public class InfoTypeAndValue
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier infoType;
+ private ASN1Encodable infoValue;
+
+ private InfoTypeAndValue(ASN1Sequence seq)
+ {
+ infoType = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+
+ if (seq.size() > 1)
+ {
+ infoValue = (ASN1Encodable)seq.getObjectAt(1);
+ }
+ }
+
+ public static InfoTypeAndValue getInstance(Object o)
+ {
+ if (o instanceof InfoTypeAndValue)
+ {
+ return (InfoTypeAndValue)o;
+ }
+
+ if (o != null)
+ {
+ return new InfoTypeAndValue(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public InfoTypeAndValue(
+ ASN1ObjectIdentifier infoType)
+ {
+ this.infoType = infoType;
+ this.infoValue = null;
+ }
+
+ public InfoTypeAndValue(
+ ASN1ObjectIdentifier infoType,
+ ASN1Encodable optionalValue)
+ {
+ this.infoType = infoType;
+ this.infoValue = optionalValue;
+ }
+
+ public ASN1ObjectIdentifier getInfoType()
+ {
+ return infoType;
+ }
+
+ public ASN1Encodable getInfoValue()
+ {
+ return infoValue;
+ }
+
+ /**
+ * <pre>
+ * InfoTypeAndValue ::= SEQUENCE {
+ * infoType OBJECT IDENTIFIER,
+ * infoValue ANY DEFINED BY infoType OPTIONAL
+ * }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(infoType);
+
+ if (infoValue != null)
+ {
+ v.add(infoValue);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/KeyRecRepContent.java b/core/src/main/java/org/bouncycastle/asn1/cmp/KeyRecRepContent.java
new file mode 100644
index 00000000..3bc50329
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/KeyRecRepContent.java
@@ -0,0 +1,142 @@
+package org.bouncycastle.asn1.cmp;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class KeyRecRepContent
+ extends ASN1Object
+{
+ private PKIStatusInfo status;
+ private CMPCertificate newSigCert;
+ private ASN1Sequence caCerts;
+ private ASN1Sequence keyPairHist;
+
+ private KeyRecRepContent(ASN1Sequence seq)
+ {
+ Enumeration en = seq.getObjects();
+
+ status = PKIStatusInfo.getInstance(en.nextElement());
+
+ while (en.hasMoreElements())
+ {
+ ASN1TaggedObject tObj = ASN1TaggedObject.getInstance(en.nextElement());
+
+ switch (tObj.getTagNo())
+ {
+ case 0:
+ newSigCert = CMPCertificate.getInstance(tObj.getObject());
+ break;
+ case 1:
+ caCerts = ASN1Sequence.getInstance(tObj.getObject());
+ break;
+ case 2:
+ keyPairHist = ASN1Sequence.getInstance(tObj.getObject());
+ break;
+ default:
+ throw new IllegalArgumentException("unknown tag number: " + tObj.getTagNo());
+ }
+ }
+ }
+
+ public static KeyRecRepContent getInstance(Object o)
+ {
+ if (o instanceof KeyRecRepContent)
+ {
+ return (KeyRecRepContent)o;
+ }
+
+ if (o != null)
+ {
+ return new KeyRecRepContent(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+
+ public PKIStatusInfo getStatus()
+ {
+ return status;
+ }
+
+ public CMPCertificate getNewSigCert()
+ {
+ return newSigCert;
+ }
+
+ public CMPCertificate[] getCaCerts()
+ {
+ if (caCerts == null)
+ {
+ return null;
+ }
+
+ CMPCertificate[] results = new CMPCertificate[caCerts.size()];
+
+ for (int i = 0; i != results.length; i++)
+ {
+ results[i] = CMPCertificate.getInstance(caCerts.getObjectAt(i));
+ }
+
+ return results;
+ }
+
+ public CertifiedKeyPair[] getKeyPairHist()
+ {
+ if (keyPairHist == null)
+ {
+ return null;
+ }
+
+ CertifiedKeyPair[] results = new CertifiedKeyPair[keyPairHist.size()];
+
+ for (int i = 0; i != results.length; i++)
+ {
+ results[i] = CertifiedKeyPair.getInstance(keyPairHist.getObjectAt(i));
+ }
+
+ return results;
+ }
+
+ /**
+ * <pre>
+ * KeyRecRepContent ::= SEQUENCE {
+ * status PKIStatusInfo,
+ * newSigCert [0] CMPCertificate OPTIONAL,
+ * caCerts [1] SEQUENCE SIZE (1..MAX) OF
+ * CMPCertificate OPTIONAL,
+ * keyPairHist [2] SEQUENCE SIZE (1..MAX) OF
+ * CertifiedKeyPair OPTIONAL
+ * }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(status);
+
+ addOptional(v, 0, newSigCert);
+ addOptional(v, 1, caCerts);
+ addOptional(v, 2, keyPairHist);
+
+ return new DERSequence(v);
+ }
+
+ private void addOptional(ASN1EncodableVector v, int tagNo, ASN1Encodable obj)
+ {
+ if (obj != null)
+ {
+ v.add(new DERTaggedObject(true, tagNo, obj));
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/OOBCertHash.java b/core/src/main/java/org/bouncycastle/asn1/cmp/OOBCertHash.java
new file mode 100644
index 00000000..fd833c49
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/OOBCertHash.java
@@ -0,0 +1,117 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.crmf.CertId;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class OOBCertHash
+ extends ASN1Object
+{
+ private AlgorithmIdentifier hashAlg;
+ private CertId certId;
+ private DERBitString hashVal;
+
+ private OOBCertHash(ASN1Sequence seq)
+ {
+ int index = seq.size() - 1;
+
+ hashVal = DERBitString.getInstance(seq.getObjectAt(index--));
+
+ for (int i = index; i >= 0; i--)
+ {
+ ASN1TaggedObject tObj = (ASN1TaggedObject)seq.getObjectAt(i);
+
+ if (tObj.getTagNo() == 0)
+ {
+ hashAlg = AlgorithmIdentifier.getInstance(tObj, true);
+ }
+ else
+ {
+ certId = CertId.getInstance(tObj, true);
+ }
+ }
+
+ }
+
+ public static OOBCertHash getInstance(Object o)
+ {
+ if (o instanceof OOBCertHash)
+ {
+ return (OOBCertHash)o;
+ }
+
+ if (o != null)
+ {
+ return new OOBCertHash(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public OOBCertHash(AlgorithmIdentifier hashAlg, CertId certId, byte[] hashVal)
+ {
+ this(hashAlg, certId, new DERBitString(hashVal));
+ }
+
+ public OOBCertHash(AlgorithmIdentifier hashAlg, CertId certId, DERBitString hashVal)
+ {
+ this.hashAlg = hashAlg;
+ this.certId = certId;
+ this.hashVal = hashVal;
+ }
+
+ public AlgorithmIdentifier getHashAlg()
+ {
+ return hashAlg;
+ }
+
+ public CertId getCertId()
+ {
+ return certId;
+ }
+
+ public DERBitString getHashVal()
+ {
+ return hashVal;
+ }
+
+ /**
+ * <pre>
+ * OOBCertHash ::= SEQUENCE {
+ * hashAlg [0] AlgorithmIdentifier OPTIONAL,
+ * certId [1] CertId OPTIONAL,
+ * hashVal BIT STRING
+ * -- hashVal is calculated over the DER encoding of the
+ * -- self-signed certificate with the identifier certID.
+ * }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ addOptional(v, 0, hashAlg);
+ addOptional(v, 1, certId);
+
+ v.add(hashVal);
+
+ return new DERSequence(v);
+ }
+
+ private void addOptional(ASN1EncodableVector v, int tagNo, ASN1Encodable obj)
+ {
+ if (obj != null)
+ {
+ v.add(new DERTaggedObject(true, tagNo, obj));
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/PBMParameter.java b/core/src/main/java/org/bouncycastle/asn1/cmp/PBMParameter.java
new file mode 100644
index 00000000..fdf2c250
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/PBMParameter.java
@@ -0,0 +1,117 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class PBMParameter
+ extends ASN1Object
+{
+ private ASN1OctetString salt;
+ private AlgorithmIdentifier owf;
+ private ASN1Integer iterationCount;
+ private AlgorithmIdentifier mac;
+
+ private PBMParameter(ASN1Sequence seq)
+ {
+ salt = ASN1OctetString.getInstance(seq.getObjectAt(0));
+ owf = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
+ iterationCount = ASN1Integer.getInstance(seq.getObjectAt(2));
+ mac = AlgorithmIdentifier.getInstance(seq.getObjectAt(3));
+ }
+
+ public static PBMParameter getInstance(Object o)
+ {
+ if (o instanceof PBMParameter)
+ {
+ return (PBMParameter)o;
+ }
+
+ if (o != null)
+ {
+ return new PBMParameter(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public PBMParameter(
+ byte[] salt,
+ AlgorithmIdentifier owf,
+ int iterationCount,
+ AlgorithmIdentifier mac)
+ {
+ this(new DEROctetString(salt), owf,
+ new ASN1Integer(iterationCount), mac);
+ }
+
+ public PBMParameter(
+ ASN1OctetString salt,
+ AlgorithmIdentifier owf,
+ ASN1Integer iterationCount,
+ AlgorithmIdentifier mac)
+ {
+ this.salt = salt;
+ this.owf = owf;
+ this.iterationCount = iterationCount;
+ this.mac = mac;
+ }
+
+ public ASN1OctetString getSalt()
+ {
+ return salt;
+ }
+
+ public AlgorithmIdentifier getOwf()
+ {
+ return owf;
+ }
+
+ public ASN1Integer getIterationCount()
+ {
+ return iterationCount;
+ }
+
+ public AlgorithmIdentifier getMac()
+ {
+ return mac;
+ }
+
+ /**
+ * <pre>
+ * PBMParameter ::= SEQUENCE {
+ * salt OCTET STRING,
+ * -- note: implementations MAY wish to limit acceptable sizes
+ * -- of this string to values appropriate for their environment
+ * -- in order to reduce the risk of denial-of-service attacks
+ * owf AlgorithmIdentifier,
+ * -- AlgId for a One-Way Function (SHA-1 recommended)
+ * iterationCount INTEGER,
+ * -- number of times the OWF is applied
+ * -- note: implementations MAY wish to limit acceptable sizes
+ * -- of this integer to values appropriate for their environment
+ * -- in order to reduce the risk of denial-of-service attacks
+ * mac AlgorithmIdentifier
+ * -- the MAC AlgId (e.g., DES-MAC, Triple-DES-MAC [PKCS11],
+ * } -- or HMAC [RFC2104, RFC2202])
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(salt);
+ v.add(owf);
+ v.add(iterationCount);
+ v.add(mac);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/PKIBody.java b/core/src/main/java/org/bouncycastle/asn1/cmp/PKIBody.java
new file mode 100644
index 00000000..269c3695
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/PKIBody.java
@@ -0,0 +1,194 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.crmf.CertReqMessages;
+import org.bouncycastle.asn1.pkcs.CertificationRequest;
+
+public class PKIBody
+ extends ASN1Object
+ implements ASN1Choice
+{
+ public static final int TYPE_INIT_REQ = 0;
+ public static final int TYPE_INIT_REP = 1;
+ public static final int TYPE_CERT_REQ = 2;
+ public static final int TYPE_CERT_REP = 3;
+ public static final int TYPE_P10_CERT_REQ = 4;
+ public static final int TYPE_POPO_CHALL = 5;
+ public static final int TYPE_POPO_REP = 6;
+ public static final int TYPE_KEY_UPDATE_REQ = 7;
+ public static final int TYPE_KEY_UPDATE_REP = 8;
+ public static final int TYPE_KEY_RECOVERY_REQ = 9;
+ public static final int TYPE_KEY_RECOVERY_REP = 10;
+ public static final int TYPE_REVOCATION_REQ = 11;
+ public static final int TYPE_REVOCATION_REP = 12;
+ public static final int TYPE_CROSS_CERT_REQ = 13;
+ public static final int TYPE_CROSS_CERT_REP = 14;
+ public static final int TYPE_CA_KEY_UPDATE_ANN = 15;
+ public static final int TYPE_CERT_ANN = 16;
+ public static final int TYPE_REVOCATION_ANN = 17;
+ public static final int TYPE_CRL_ANN = 18;
+ public static final int TYPE_CONFIRM = 19;
+ public static final int TYPE_NESTED = 20;
+ public static final int TYPE_GEN_MSG = 21;
+ public static final int TYPE_GEN_REP = 22;
+ public static final int TYPE_ERROR = 23;
+ public static final int TYPE_CERT_CONFIRM = 24;
+ public static final int TYPE_POLL_REQ = 25;
+ public static final int TYPE_POLL_REP = 26;
+
+ private int tagNo;
+ private ASN1Encodable body;
+
+ public static PKIBody getInstance(Object o)
+ {
+ if (o == null || o instanceof PKIBody)
+ {
+ return (PKIBody)o;
+ }
+
+ if (o instanceof ASN1TaggedObject)
+ {
+ return new PKIBody((ASN1TaggedObject)o);
+ }
+
+ throw new IllegalArgumentException("Invalid object: " + o.getClass().getName());
+ }
+
+ private PKIBody(ASN1TaggedObject tagged)
+ {
+ tagNo = tagged.getTagNo();
+ body = getBodyForType(tagNo, tagged.getObject());
+ }
+
+ /**
+ * Creates a new PKIBody.
+ * @param type one of the TYPE_* constants
+ * @param content message content
+ */
+ public PKIBody(
+ int type,
+ ASN1Encodable content)
+ {
+ tagNo = type;
+ body = getBodyForType(type, content);
+ }
+
+ private static ASN1Encodable getBodyForType(
+ int type,
+ ASN1Encodable o)
+ {
+ switch (type)
+ {
+ case TYPE_INIT_REQ:
+ return CertReqMessages.getInstance(o);
+ case TYPE_INIT_REP:
+ return CertRepMessage.getInstance(o);
+ case TYPE_CERT_REQ:
+ return CertReqMessages.getInstance(o);
+ case TYPE_CERT_REP:
+ return CertRepMessage.getInstance(o);
+ case TYPE_P10_CERT_REQ:
+ return CertificationRequest.getInstance(o);
+ case TYPE_POPO_CHALL:
+ return POPODecKeyChallContent.getInstance(o);
+ case TYPE_POPO_REP:
+ return POPODecKeyRespContent.getInstance(o);
+ case TYPE_KEY_UPDATE_REQ:
+ return CertReqMessages.getInstance(o);
+ case TYPE_KEY_UPDATE_REP:
+ return CertRepMessage.getInstance(o);
+ case TYPE_KEY_RECOVERY_REQ:
+ return CertReqMessages.getInstance(o);
+ case TYPE_KEY_RECOVERY_REP:
+ return KeyRecRepContent.getInstance(o);
+ case TYPE_REVOCATION_REQ:
+ return RevReqContent.getInstance(o);
+ case TYPE_REVOCATION_REP:
+ return RevRepContent.getInstance(o);
+ case TYPE_CROSS_CERT_REQ:
+ return CertReqMessages.getInstance(o);
+ case TYPE_CROSS_CERT_REP:
+ return CertRepMessage.getInstance(o);
+ case TYPE_CA_KEY_UPDATE_ANN:
+ return CAKeyUpdAnnContent.getInstance(o);
+ case TYPE_CERT_ANN:
+ return CMPCertificate.getInstance(o);
+ case TYPE_REVOCATION_ANN:
+ return RevAnnContent.getInstance(o);
+ case TYPE_CRL_ANN:
+ return CRLAnnContent.getInstance(o);
+ case TYPE_CONFIRM:
+ return PKIConfirmContent.getInstance(o);
+ case TYPE_NESTED:
+ return PKIMessages.getInstance(o);
+ case TYPE_GEN_MSG:
+ return GenMsgContent.getInstance(o);
+ case TYPE_GEN_REP:
+ return GenRepContent.getInstance(o);
+ case TYPE_ERROR:
+ return ErrorMsgContent.getInstance(o);
+ case TYPE_CERT_CONFIRM:
+ return CertConfirmContent.getInstance(o);
+ case TYPE_POLL_REQ:
+ return PollReqContent.getInstance(o);
+ case TYPE_POLL_REP:
+ return PollRepContent.getInstance(o);
+ default:
+ throw new IllegalArgumentException("unknown tag number: " + type);
+ }
+ }
+
+ public int getType()
+ {
+ return tagNo;
+ }
+
+ public ASN1Encodable getContent()
+ {
+ return body;
+ }
+
+ /**
+ * <pre>
+ * PKIBody ::= CHOICE { -- message-specific body elements
+ * ir [0] CertReqMessages, --Initialization Request
+ * ip [1] CertRepMessage, --Initialization Response
+ * cr [2] CertReqMessages, --Certification Request
+ * cp [3] CertRepMessage, --Certification Response
+ * p10cr [4] CertificationRequest, --imported from [PKCS10]
+ * popdecc [5] POPODecKeyChallContent, --pop Challenge
+ * popdecr [6] POPODecKeyRespContent, --pop Response
+ * kur [7] CertReqMessages, --Key Update Request
+ * kup [8] CertRepMessage, --Key Update Response
+ * krr [9] CertReqMessages, --Key Recovery Request
+ * krp [10] KeyRecRepContent, --Key Recovery Response
+ * rr [11] RevReqContent, --Revocation Request
+ * rp [12] RevRepContent, --Revocation Response
+ * ccr [13] CertReqMessages, --Cross-Cert. Request
+ * ccp [14] CertRepMessage, --Cross-Cert. Response
+ * ckuann [15] CAKeyUpdAnnContent, --CA Key Update Ann.
+ * cann [16] CertAnnContent, --Certificate Ann.
+ * rann [17] RevAnnContent, --Revocation Ann.
+ * crlann [18] CRLAnnContent, --CRL Announcement
+ * pkiconf [19] PKIConfirmContent, --Confirmation
+ * nested [20] NestedMessageContent, --Nested Message
+ * genm [21] GenMsgContent, --General Message
+ * genp [22] GenRepContent, --General Response
+ * error [23] ErrorMsgContent, --Error Message
+ * certConf [24] CertConfirmContent, --Certificate confirm
+ * pollReq [25] PollReqContent, --Polling request
+ * pollRep [26] PollRepContent --Polling response
+ * }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return new DERTaggedObject(true, tagNo, body);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/PKIConfirmContent.java b/core/src/main/java/org/bouncycastle/asn1/cmp/PKIConfirmContent.java
new file mode 100644
index 00000000..5af3f7d7
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/PKIConfirmContent.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1Null;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DERNull;
+
+public class PKIConfirmContent
+ extends ASN1Object
+{
+ private ASN1Null val;
+
+ private PKIConfirmContent(ASN1Null val)
+ {
+ this.val = val;
+ }
+
+ public static PKIConfirmContent getInstance(Object o)
+ {
+ if (o == null || o instanceof PKIConfirmContent)
+ {
+ return (PKIConfirmContent)o;
+ }
+
+ if (o instanceof ASN1Null)
+ {
+ return new PKIConfirmContent((ASN1Null)o);
+ }
+
+ throw new IllegalArgumentException("Invalid object: " + o.getClass().getName());
+ }
+
+ public PKIConfirmContent()
+ {
+ val = DERNull.INSTANCE;
+ }
+
+ /**
+ * <pre>
+ * PKIConfirmContent ::= NULL
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return val;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/PKIFailureInfo.java b/core/src/main/java/org/bouncycastle/asn1/cmp/PKIFailureInfo.java
new file mode 100644
index 00000000..10acbb44
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/PKIFailureInfo.java
@@ -0,0 +1,126 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.DERBitString;
+
+/**
+ * <pre>
+ * PKIFailureInfo ::= BIT STRING {
+ * badAlg (0),
+ * -- unrecognized or unsupported Algorithm Identifier
+ * badMessageCheck (1), -- integrity check failed (e.g., signature did not verify)
+ * badRequest (2),
+ * -- transaction not permitted or supported
+ * badTime (3), -- messageTime was not sufficiently close to the system time, as defined by local policy
+ * badCertId (4), -- no certificate could be found matching the provided criteria
+ * badDataFormat (5),
+ * -- the data submitted has the wrong format
+ * wrongAuthority (6), -- the authority indicated in the request is different from the one creating the response token
+ * incorrectData (7), -- the requester's data is incorrect (for notary services)
+ * missingTimeStamp (8), -- when the timestamp is missing but should be there (by policy)
+ * badPOP (9) -- the proof-of-possession failed
+ * certRevoked (10),
+ * certConfirmed (11),
+ * wrongIntegrity (12),
+ * badRecipientNonce (13),
+ * timeNotAvailable (14),
+ * -- the TSA's time source is not available
+ * unacceptedPolicy (15),
+ * -- the requested TSA policy is not supported by the TSA
+ * unacceptedExtension (16),
+ * -- the requested extension is not supported by the TSA
+ * addInfoNotAvailable (17)
+ * -- the additional information requested could not be understood
+ * -- or is not available
+ * badSenderNonce (18),
+ * badCertTemplate (19),
+ * signerNotTrusted (20),
+ * transactionIdInUse (21),
+ * unsupportedVersion (22),
+ * notAuthorized (23),
+ * systemUnavail (24),
+ * systemFailure (25),
+ * -- the request cannot be handled due to system failure
+ * duplicateCertReq (26)
+ * </pre>
+ */
+public class PKIFailureInfo
+ extends DERBitString
+{
+ public static final int badAlg = (1 << 7); // unrecognized or unsupported Algorithm Identifier
+ public static final int badMessageCheck = (1 << 6); // integrity check failed (e.g., signature did not verify)
+ public static final int badRequest = (1 << 5);
+ public static final int badTime = (1 << 4); // -- messageTime was not sufficiently close to the system time, as defined by local policy
+ public static final int badCertId = (1 << 3); // no certificate could be found matching the provided criteria
+ public static final int badDataFormat = (1 << 2);
+ public static final int wrongAuthority = (1 << 1); // the authority indicated in the request is different from the one creating the response token
+ public static final int incorrectData = 1; // the requester's data is incorrect (for notary services)
+ public static final int missingTimeStamp = (1 << 15); // when the timestamp is missing but should be there (by policy)
+ public static final int badPOP = (1 << 14); // the proof-of-possession failed
+ public static final int certRevoked = (1 << 13);
+ public static final int certConfirmed = (1 << 12);
+ public static final int wrongIntegrity = (1 << 11);
+ public static final int badRecipientNonce = (1 << 10);
+ public static final int timeNotAvailable = (1 << 9); // the TSA's time source is not available
+ public static final int unacceptedPolicy = (1 << 8); // the requested TSA policy is not supported by the TSA
+ public static final int unacceptedExtension = (1 << 23); //the requested extension is not supported by the TSA
+ public static final int addInfoNotAvailable = (1 << 22); //the additional information requested could not be understood or is not available
+ public static final int badSenderNonce = (1 << 21);
+ public static final int badCertTemplate = (1 << 20);
+ public static final int signerNotTrusted = (1 << 19);
+ public static final int transactionIdInUse = (1 << 18);
+ public static final int unsupportedVersion = (1 << 17);
+ public static final int notAuthorized = (1 << 16);
+ public static final int systemUnavail = (1 << 31);
+ public static final int systemFailure = (1 << 30); //the request cannot be handled due to system failure
+ public static final int duplicateCertReq = (1 << 29);
+
+ /** @deprecated use lower case version */
+ public static final int BAD_ALG = badAlg; // unrecognized or unsupported Algorithm Identifier
+ /** @deprecated use lower case version */
+ public static final int BAD_MESSAGE_CHECK = badMessageCheck;
+ /** @deprecated use lower case version */
+ public static final int BAD_REQUEST = badRequest; // transaction not permitted or supported
+ /** @deprecated use lower case version */
+ public static final int BAD_TIME = badTime;
+ /** @deprecated use lower case version */
+ public static final int BAD_CERT_ID = badCertId;
+ /** @deprecated use lower case version */
+ public static final int BAD_DATA_FORMAT = badDataFormat; // the data submitted has the wrong format
+ /** @deprecated use lower case version */
+ public static final int WRONG_AUTHORITY = wrongAuthority;
+ /** @deprecated use lower case version */
+ public static final int INCORRECT_DATA = incorrectData;
+ /** @deprecated use lower case version */
+ public static final int MISSING_TIME_STAMP = missingTimeStamp;
+ /** @deprecated use lower case version */
+ public static final int BAD_POP = badPOP;
+ /** @deprecated use lower case version */
+ public static final int TIME_NOT_AVAILABLE = timeNotAvailable;
+ /** @deprecated use lower case version */
+ public static final int UNACCEPTED_POLICY = unacceptedPolicy;
+ /** @deprecated use lower case version */
+ public static final int UNACCEPTED_EXTENSION = unacceptedExtension;
+ /** @deprecated use lower case version */
+ public static final int ADD_INFO_NOT_AVAILABLE = addInfoNotAvailable;
+ /** @deprecated use lower case version */
+ public static final int SYSTEM_FAILURE = systemFailure;
+ /**
+ * Basic constructor.
+ */
+ public PKIFailureInfo(
+ int info)
+ {
+ super(getBytes(info), getPadBits(info));
+ }
+
+ public PKIFailureInfo(
+ DERBitString info)
+ {
+ super(info.getBytes(), info.getPadBits());
+ }
+
+ public String toString()
+ {
+ return "PKIFailureInfo: 0x" + Integer.toHexString(this.intValue());
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/PKIFreeText.java b/core/src/main/java/org/bouncycastle/asn1/cmp/PKIFreeText.java
new file mode 100644
index 00000000..5b63c194
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/PKIFreeText.java
@@ -0,0 +1,115 @@
+package org.bouncycastle.asn1.cmp;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERUTF8String;
+
+public class PKIFreeText
+ extends ASN1Object
+{
+ ASN1Sequence strings;
+
+ public static PKIFreeText getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static PKIFreeText getInstance(
+ Object obj)
+ {
+ if (obj instanceof PKIFreeText)
+ {
+ return (PKIFreeText)obj;
+ }
+ else if (obj != null)
+ {
+ return new PKIFreeText(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private PKIFreeText(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+ while (e.hasMoreElements())
+ {
+ if (!(e.nextElement() instanceof DERUTF8String))
+ {
+ throw new IllegalArgumentException("attempt to insert non UTF8 STRING into PKIFreeText");
+ }
+ }
+
+ strings = seq;
+ }
+
+ public PKIFreeText(
+ DERUTF8String p)
+ {
+ strings = new DERSequence(p);
+ }
+
+ public PKIFreeText(
+ String p)
+ {
+ this(new DERUTF8String(p));
+ }
+
+ public PKIFreeText(
+ DERUTF8String[] strs)
+ {
+ strings = new DERSequence(strs);
+ }
+
+ public PKIFreeText(
+ String[] strs)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ for (int i = 0; i < strs.length; i++)
+ {
+ v.add(new DERUTF8String(strs[i]));
+ }
+ strings = new DERSequence(v);
+ }
+
+ /**
+ * Return the number of string elements present.
+ *
+ * @return number of elements present.
+ */
+ public int size()
+ {
+ return strings.size();
+ }
+
+ /**
+ * Return the UTF8STRING at index i.
+ *
+ * @param i index of the string of interest
+ * @return the string at index i.
+ */
+ public DERUTF8String getStringAt(
+ int i)
+ {
+ return (DERUTF8String)strings.getObjectAt(i);
+ }
+
+ /**
+ * <pre>
+ * PKIFreeText ::= SEQUENCE SIZE (1..MAX) OF UTF8String
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return strings;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/PKIHeader.java b/core/src/main/java/org/bouncycastle/asn1/cmp/PKIHeader.java
new file mode 100644
index 00000000..afab1920
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/PKIHeader.java
@@ -0,0 +1,260 @@
+package org.bouncycastle.asn1.cmp;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERGeneralizedTime;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.GeneralName;
+
+public class PKIHeader
+ extends ASN1Object
+{
+ /**
+ * Value for a "null" recipient or sender.
+ */
+ public static final GeneralName NULL_NAME = new GeneralName(X500Name.getInstance(new DERSequence()));
+
+ public static final int CMP_1999 = 1;
+ public static final int CMP_2000 = 2;
+
+ private ASN1Integer pvno;
+ private GeneralName sender;
+ private GeneralName recipient;
+ private DERGeneralizedTime messageTime;
+ private AlgorithmIdentifier protectionAlg;
+ private ASN1OctetString senderKID; // KeyIdentifier
+ private ASN1OctetString recipKID; // KeyIdentifier
+ private ASN1OctetString transactionID;
+ private ASN1OctetString senderNonce;
+ private ASN1OctetString recipNonce;
+ private PKIFreeText freeText;
+ private ASN1Sequence generalInfo;
+
+ private PKIHeader(ASN1Sequence seq)
+ {
+ Enumeration en = seq.getObjects();
+
+ pvno = ASN1Integer.getInstance(en.nextElement());
+ sender = GeneralName.getInstance(en.nextElement());
+ recipient = GeneralName.getInstance(en.nextElement());
+
+ while (en.hasMoreElements())
+ {
+ ASN1TaggedObject tObj = (ASN1TaggedObject)en.nextElement();
+
+ switch (tObj.getTagNo())
+ {
+ case 0:
+ messageTime = DERGeneralizedTime.getInstance(tObj, true);
+ break;
+ case 1:
+ protectionAlg = AlgorithmIdentifier.getInstance(tObj, true);
+ break;
+ case 2:
+ senderKID = ASN1OctetString.getInstance(tObj, true);
+ break;
+ case 3:
+ recipKID = ASN1OctetString.getInstance(tObj, true);
+ break;
+ case 4:
+ transactionID = ASN1OctetString.getInstance(tObj, true);
+ break;
+ case 5:
+ senderNonce = ASN1OctetString.getInstance(tObj, true);
+ break;
+ case 6:
+ recipNonce = ASN1OctetString.getInstance(tObj, true);
+ break;
+ case 7:
+ freeText = PKIFreeText.getInstance(tObj, true);
+ break;
+ case 8:
+ generalInfo = ASN1Sequence.getInstance(tObj, true);
+ break;
+ default:
+ throw new IllegalArgumentException("unknown tag number: " + tObj.getTagNo());
+ }
+ }
+ }
+
+ public static PKIHeader getInstance(Object o)
+ {
+ if (o instanceof PKIHeader)
+ {
+ return (PKIHeader)o;
+ }
+
+ if (o != null)
+ {
+ return new PKIHeader(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public PKIHeader(
+ int pvno,
+ GeneralName sender,
+ GeneralName recipient)
+ {
+ this(new ASN1Integer(pvno), sender, recipient);
+ }
+
+ private PKIHeader(
+ ASN1Integer pvno,
+ GeneralName sender,
+ GeneralName recipient)
+ {
+ this.pvno = pvno;
+ this.sender = sender;
+ this.recipient = recipient;
+ }
+
+ public ASN1Integer getPvno()
+ {
+ return pvno;
+ }
+
+ public GeneralName getSender()
+ {
+ return sender;
+ }
+
+ public GeneralName getRecipient()
+ {
+ return recipient;
+ }
+
+ public DERGeneralizedTime getMessageTime()
+ {
+ return messageTime;
+ }
+
+ public AlgorithmIdentifier getProtectionAlg()
+ {
+ return protectionAlg;
+ }
+
+ public ASN1OctetString getSenderKID()
+ {
+ return senderKID;
+ }
+
+ public ASN1OctetString getRecipKID()
+ {
+ return recipKID;
+ }
+
+ public ASN1OctetString getTransactionID()
+ {
+ return transactionID;
+ }
+
+ public ASN1OctetString getSenderNonce()
+ {
+ return senderNonce;
+ }
+
+ public ASN1OctetString getRecipNonce()
+ {
+ return recipNonce;
+ }
+
+ public PKIFreeText getFreeText()
+ {
+ return freeText;
+ }
+
+ public InfoTypeAndValue[] getGeneralInfo()
+ {
+ if (generalInfo == null)
+ {
+ return null;
+ }
+ InfoTypeAndValue[] results = new InfoTypeAndValue[generalInfo.size()];
+ for (int i = 0; i < results.length; i++)
+ {
+ results[i]
+ = InfoTypeAndValue.getInstance(generalInfo.getObjectAt(i));
+ }
+ return results;
+ }
+
+ /**
+ * <pre>
+ * PKIHeader ::= SEQUENCE {
+ * pvno INTEGER { cmp1999(1), cmp2000(2) },
+ * sender GeneralName,
+ * -- identifies the sender
+ * recipient GeneralName,
+ * -- identifies the intended recipient
+ * messageTime [0] GeneralizedTime OPTIONAL,
+ * -- time of production of this message (used when sender
+ * -- believes that the transport will be "suitable"; i.e.,
+ * -- that the time will still be meaningful upon receipt)
+ * protectionAlg [1] AlgorithmIdentifier OPTIONAL,
+ * -- algorithm used for calculation of protection bits
+ * senderKID [2] KeyIdentifier OPTIONAL,
+ * recipKID [3] KeyIdentifier OPTIONAL,
+ * -- to identify specific keys used for protection
+ * transactionID [4] OCTET STRING OPTIONAL,
+ * -- identifies the transaction; i.e., this will be the same in
+ * -- corresponding request, response, certConf, and PKIConf
+ * -- messages
+ * senderNonce [5] OCTET STRING OPTIONAL,
+ * recipNonce [6] OCTET STRING OPTIONAL,
+ * -- nonces used to provide replay protection, senderNonce
+ * -- is inserted by the creator of this message; recipNonce
+ * -- is a nonce previously inserted in a related message by
+ * -- the intended recipient of this message
+ * freeText [7] PKIFreeText OPTIONAL,
+ * -- this may be used to indicate context-specific instructions
+ * -- (this field is intended for human consumption)
+ * generalInfo [8] SEQUENCE SIZE (1..MAX) OF
+ * InfoTypeAndValue OPTIONAL
+ * -- this may be used to convey context-specific information
+ * -- (this field not primarily intended for human consumption)
+ * }
+ * </pre>
+ *
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(pvno);
+ v.add(sender);
+ v.add(recipient);
+ addOptional(v, 0, messageTime);
+ addOptional(v, 1, protectionAlg);
+ addOptional(v, 2, senderKID);
+ addOptional(v, 3, recipKID);
+ addOptional(v, 4, transactionID);
+ addOptional(v, 5, senderNonce);
+ addOptional(v, 6, recipNonce);
+ addOptional(v, 7, freeText);
+ addOptional(v, 8, generalInfo);
+
+ return new DERSequence(v);
+ }
+
+ private void addOptional(ASN1EncodableVector v, int tagNo, ASN1Encodable obj)
+ {
+ if (obj != null)
+ {
+ v.add(new DERTaggedObject(true, tagNo, obj));
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/PKIHeaderBuilder.java b/core/src/main/java/org/bouncycastle/asn1/cmp/PKIHeaderBuilder.java
new file mode 100644
index 00000000..76d6bab3
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/PKIHeaderBuilder.java
@@ -0,0 +1,254 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERGeneralizedTime;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.GeneralName;
+
+public class PKIHeaderBuilder
+{
+ private ASN1Integer pvno;
+ private GeneralName sender;
+ private GeneralName recipient;
+ private ASN1GeneralizedTime messageTime;
+ private AlgorithmIdentifier protectionAlg;
+ private ASN1OctetString senderKID; // KeyIdentifier
+ private ASN1OctetString recipKID; // KeyIdentifier
+ private ASN1OctetString transactionID;
+ private ASN1OctetString senderNonce;
+ private ASN1OctetString recipNonce;
+ private PKIFreeText freeText;
+ private ASN1Sequence generalInfo;
+
+ public PKIHeaderBuilder(
+ int pvno,
+ GeneralName sender,
+ GeneralName recipient)
+ {
+ this(new ASN1Integer(pvno), sender, recipient);
+ }
+
+ private PKIHeaderBuilder(
+ ASN1Integer pvno,
+ GeneralName sender,
+ GeneralName recipient)
+ {
+ this.pvno = pvno;
+ this.sender = sender;
+ this.recipient = recipient;
+ }
+
+ /**
+ * @deprecated use ASN1GeneralizedTime
+ */
+ public PKIHeaderBuilder setMessageTime(DERGeneralizedTime time)
+ {
+ messageTime = ASN1GeneralizedTime.getInstance(time);
+
+ return this;
+ }
+
+ public PKIHeaderBuilder setMessageTime(ASN1GeneralizedTime time)
+ {
+ messageTime = time;
+
+ return this;
+ }
+
+ public PKIHeaderBuilder setProtectionAlg(AlgorithmIdentifier aid)
+ {
+ protectionAlg = aid;
+
+ return this;
+ }
+
+ public PKIHeaderBuilder setSenderKID(byte[] kid)
+ {
+ return setSenderKID(kid == null ? null : new DEROctetString(kid));
+ }
+
+ public PKIHeaderBuilder setSenderKID(ASN1OctetString kid)
+ {
+ senderKID = kid;
+
+ return this;
+ }
+
+ public PKIHeaderBuilder setRecipKID(byte[] kid)
+ {
+ return setRecipKID(kid == null ? null : new DEROctetString(kid));
+ }
+
+ public PKIHeaderBuilder setRecipKID(DEROctetString kid)
+ {
+ recipKID = kid;
+
+ return this;
+ }
+
+ public PKIHeaderBuilder setTransactionID(byte[] tid)
+ {
+ return setTransactionID(tid == null ? null : new DEROctetString(tid));
+ }
+
+ public PKIHeaderBuilder setTransactionID(ASN1OctetString tid)
+ {
+ transactionID = tid;
+
+ return this;
+ }
+
+ public PKIHeaderBuilder setSenderNonce(byte[] nonce)
+ {
+ return setSenderNonce(nonce == null ? null : new DEROctetString(nonce));
+ }
+
+ public PKIHeaderBuilder setSenderNonce(ASN1OctetString nonce)
+ {
+ senderNonce = nonce;
+
+ return this;
+ }
+
+ public PKIHeaderBuilder setRecipNonce(byte[] nonce)
+ {
+ return setRecipNonce(nonce == null ? null : new DEROctetString(nonce));
+ }
+
+ public PKIHeaderBuilder setRecipNonce(ASN1OctetString nonce)
+ {
+ recipNonce = nonce;
+
+ return this;
+ }
+
+ public PKIHeaderBuilder setFreeText(PKIFreeText text)
+ {
+ freeText = text;
+
+ return this;
+ }
+
+ public PKIHeaderBuilder setGeneralInfo(InfoTypeAndValue genInfo)
+ {
+ return setGeneralInfo(makeGeneralInfoSeq(genInfo));
+ }
+
+ public PKIHeaderBuilder setGeneralInfo(InfoTypeAndValue[] genInfos)
+ {
+ return setGeneralInfo(makeGeneralInfoSeq(genInfos));
+ }
+
+ public PKIHeaderBuilder setGeneralInfo(ASN1Sequence seqOfInfoTypeAndValue)
+ {
+ generalInfo = seqOfInfoTypeAndValue;
+
+ return this;
+ }
+
+ private static ASN1Sequence makeGeneralInfoSeq(
+ InfoTypeAndValue generalInfo)
+ {
+ return new DERSequence(generalInfo);
+ }
+
+ private static ASN1Sequence makeGeneralInfoSeq(
+ InfoTypeAndValue[] generalInfos)
+ {
+ ASN1Sequence genInfoSeq = null;
+ if (generalInfos != null)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ for (int i = 0; i < generalInfos.length; i++)
+ {
+ v.add(generalInfos[i]);
+ }
+ genInfoSeq = new DERSequence(v);
+ }
+ return genInfoSeq;
+ }
+
+ /**
+ * <pre>
+ * PKIHeader ::= SEQUENCE {
+ * pvno INTEGER { cmp1999(1), cmp2000(2) },
+ * sender GeneralName,
+ * -- identifies the sender
+ * recipient GeneralName,
+ * -- identifies the intended recipient
+ * messageTime [0] GeneralizedTime OPTIONAL,
+ * -- time of production of this message (used when sender
+ * -- believes that the transport will be "suitable"; i.e.,
+ * -- that the time will still be meaningful upon receipt)
+ * protectionAlg [1] AlgorithmIdentifier OPTIONAL,
+ * -- algorithm used for calculation of protection bits
+ * senderKID [2] KeyIdentifier OPTIONAL,
+ * recipKID [3] KeyIdentifier OPTIONAL,
+ * -- to identify specific keys used for protection
+ * transactionID [4] OCTET STRING OPTIONAL,
+ * -- identifies the transaction; i.e., this will be the same in
+ * -- corresponding request, response, certConf, and PKIConf
+ * -- messages
+ * senderNonce [5] OCTET STRING OPTIONAL,
+ * recipNonce [6] OCTET STRING OPTIONAL,
+ * -- nonces used to provide replay protection, senderNonce
+ * -- is inserted by the creator of this message; recipNonce
+ * -- is a nonce previously inserted in a related message by
+ * -- the intended recipient of this message
+ * freeText [7] PKIFreeText OPTIONAL,
+ * -- this may be used to indicate context-specific instructions
+ * -- (this field is intended for human consumption)
+ * generalInfo [8] SEQUENCE SIZE (1..MAX) OF
+ * InfoTypeAndValue OPTIONAL
+ * -- this may be used to convey context-specific information
+ * -- (this field not primarily intended for human consumption)
+ * }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public PKIHeader build()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(pvno);
+ v.add(sender);
+ v.add(recipient);
+ addOptional(v, 0, messageTime);
+ addOptional(v, 1, protectionAlg);
+ addOptional(v, 2, senderKID);
+ addOptional(v, 3, recipKID);
+ addOptional(v, 4, transactionID);
+ addOptional(v, 5, senderNonce);
+ addOptional(v, 6, recipNonce);
+ addOptional(v, 7, freeText);
+ addOptional(v, 8, generalInfo);
+
+ messageTime = null;
+ protectionAlg = null;
+ senderKID = null;
+ recipKID = null;
+ transactionID = null;
+ senderNonce = null;
+ recipNonce = null;
+ freeText = null;
+ generalInfo = null;
+
+ return PKIHeader.getInstance(new DERSequence(v));
+ }
+
+ private void addOptional(ASN1EncodableVector v, int tagNo, ASN1Encodable obj)
+ {
+ if (obj != null)
+ {
+ v.add(new DERTaggedObject(true, tagNo, obj));
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/PKIMessage.java b/core/src/main/java/org/bouncycastle/asn1/cmp/PKIMessage.java
new file mode 100644
index 00000000..bfc21133
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/PKIMessage.java
@@ -0,0 +1,166 @@
+package org.bouncycastle.asn1.cmp;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class PKIMessage
+ extends ASN1Object
+{
+ private PKIHeader header;
+ private PKIBody body;
+ private DERBitString protection;
+ private ASN1Sequence extraCerts;
+
+ private PKIMessage(ASN1Sequence seq)
+ {
+ Enumeration en = seq.getObjects();
+
+ header = PKIHeader.getInstance(en.nextElement());
+ body = PKIBody.getInstance(en.nextElement());
+
+ while (en.hasMoreElements())
+ {
+ ASN1TaggedObject tObj = (ASN1TaggedObject)en.nextElement();
+
+ if (tObj.getTagNo() == 0)
+ {
+ protection = DERBitString.getInstance(tObj, true);
+ }
+ else
+ {
+ extraCerts = ASN1Sequence.getInstance(tObj, true);
+ }
+ }
+ }
+
+ public static PKIMessage getInstance(Object o)
+ {
+ if (o instanceof PKIMessage)
+ {
+ return (PKIMessage)o;
+ }
+ else if (o != null)
+ {
+ return new PKIMessage(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ /**
+ * Creates a new PKIMessage.
+ *
+ * @param header message header
+ * @param body message body
+ * @param protection message protection (may be null)
+ * @param extraCerts extra certificates (may be null)
+ */
+ public PKIMessage(
+ PKIHeader header,
+ PKIBody body,
+ DERBitString protection,
+ CMPCertificate[] extraCerts)
+ {
+ this.header = header;
+ this.body = body;
+ this.protection = protection;
+ if (extraCerts != null)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ for (int i = 0; i < extraCerts.length; i++)
+ {
+ v.add(extraCerts[i]);
+ }
+ this.extraCerts = new DERSequence(v);
+ }
+ }
+
+ public PKIMessage(
+ PKIHeader header,
+ PKIBody body,
+ DERBitString protection)
+ {
+ this(header, body, protection, null);
+ }
+
+ public PKIMessage(
+ PKIHeader header,
+ PKIBody body)
+ {
+ this(header, body, null, null);
+ }
+
+ public PKIHeader getHeader()
+ {
+ return header;
+ }
+
+ public PKIBody getBody()
+ {
+ return body;
+ }
+
+ public DERBitString getProtection()
+ {
+ return protection;
+ }
+
+ public CMPCertificate[] getExtraCerts()
+ {
+ if (extraCerts == null)
+ {
+ return null;
+ }
+
+ CMPCertificate[] results = new CMPCertificate[extraCerts.size()];
+
+ for (int i = 0; i < results.length; i++)
+ {
+ results[i] = CMPCertificate.getInstance(extraCerts.getObjectAt(i));
+ }
+ return results;
+ }
+
+ /**
+ * <pre>
+ * PKIMessage ::= SEQUENCE {
+ * header PKIHeader,
+ * body PKIBody,
+ * protection [0] PKIProtection OPTIONAL,
+ * extraCerts [1] SEQUENCE SIZE (1..MAX) OF CMPCertificate
+ * OPTIONAL
+ * }
+ * </pre>
+ *
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(header);
+ v.add(body);
+
+ addOptional(v, 0, protection);
+ addOptional(v, 1, extraCerts);
+
+ return new DERSequence(v);
+ }
+
+ private void addOptional(ASN1EncodableVector v, int tagNo, ASN1Encodable obj)
+ {
+ if (obj != null)
+ {
+ v.add(new DERTaggedObject(true, tagNo, obj));
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/PKIMessages.java b/core/src/main/java/org/bouncycastle/asn1/cmp/PKIMessages.java
new file mode 100644
index 00000000..5a80a0f4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/PKIMessages.java
@@ -0,0 +1,71 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class PKIMessages
+ extends ASN1Object
+{
+ private ASN1Sequence content;
+
+ private PKIMessages(ASN1Sequence seq)
+ {
+ content = seq;
+ }
+
+ public static PKIMessages getInstance(Object o)
+ {
+ if (o instanceof PKIMessages)
+ {
+ return (PKIMessages)o;
+ }
+
+ if (o != null)
+ {
+ return new PKIMessages(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public PKIMessages(PKIMessage msg)
+ {
+ content = new DERSequence(msg);
+ }
+
+ public PKIMessages(PKIMessage[] msgs)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ for (int i = 0; i < msgs.length; i++)
+ {
+ v.add(msgs[i]);
+ }
+ content = new DERSequence(v);
+ }
+
+ public PKIMessage[] toPKIMessageArray()
+ {
+ PKIMessage[] result = new PKIMessage[content.size()];
+
+ for (int i = 0; i != result.length; i++)
+ {
+ result[i] = PKIMessage.getInstance(content.getObjectAt(i));
+ }
+
+ return result;
+ }
+
+ /**
+ * <pre>
+ * PKIMessages ::= SEQUENCE SIZE (1..MAX) OF PKIMessage
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return content;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/PKIStatus.java b/core/src/main/java/org/bouncycastle/asn1/cmp/PKIStatus.java
new file mode 100644
index 00000000..58f7ec02
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/PKIStatus.java
@@ -0,0 +1,64 @@
+package org.bouncycastle.asn1.cmp;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+
+public class PKIStatus
+ extends ASN1Object
+{
+ public static final int GRANTED = 0;
+ public static final int GRANTED_WITH_MODS = 1;
+ public static final int REJECTION = 2;
+ public static final int WAITING = 3;
+ public static final int REVOCATION_WARNING = 4;
+ public static final int REVOCATION_NOTIFICATION = 5;
+ public static final int KEY_UPDATE_WARNING = 6;
+
+ public static final PKIStatus granted = new PKIStatus(GRANTED);
+ public static final PKIStatus grantedWithMods = new PKIStatus(GRANTED_WITH_MODS);
+ public static final PKIStatus rejection = new PKIStatus(REJECTION);
+ public static final PKIStatus waiting = new PKIStatus(WAITING);
+ public static final PKIStatus revocationWarning = new PKIStatus(REVOCATION_WARNING);
+ public static final PKIStatus revocationNotification = new PKIStatus(REVOCATION_NOTIFICATION);
+ public static final PKIStatus keyUpdateWaiting = new PKIStatus(KEY_UPDATE_WARNING);
+
+ private ASN1Integer value;
+
+ private PKIStatus(int value)
+ {
+ this(new ASN1Integer(value));
+ }
+
+ private PKIStatus(ASN1Integer value)
+ {
+ this.value = value;
+ }
+
+ public static PKIStatus getInstance(Object o)
+ {
+ if (o instanceof PKIStatus)
+ {
+ return (PKIStatus)o;
+ }
+
+ if (o != null)
+ {
+ return new PKIStatus(ASN1Integer.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public BigInteger getValue()
+ {
+ return value.getValue();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return value;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/PKIStatusInfo.java b/core/src/main/java/org/bouncycastle/asn1/cmp/PKIStatusInfo.java
new file mode 100644
index 00000000..bac1ba56
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/PKIStatusInfo.java
@@ -0,0 +1,165 @@
+package org.bouncycastle.asn1.cmp;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+
+public class PKIStatusInfo
+ extends ASN1Object
+{
+ ASN1Integer status;
+ PKIFreeText statusString;
+ DERBitString failInfo;
+
+ public static PKIStatusInfo getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static PKIStatusInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof PKIStatusInfo)
+ {
+ return (PKIStatusInfo)obj;
+ }
+ else if (obj != null)
+ {
+ return new PKIStatusInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private PKIStatusInfo(
+ ASN1Sequence seq)
+ {
+ this.status = ASN1Integer.getInstance(seq.getObjectAt(0));
+
+ this.statusString = null;
+ this.failInfo = null;
+
+ if (seq.size() > 2)
+ {
+ this.statusString = PKIFreeText.getInstance(seq.getObjectAt(1));
+ this.failInfo = DERBitString.getInstance(seq.getObjectAt(2));
+ }
+ else if (seq.size() > 1)
+ {
+ Object obj = seq.getObjectAt(1);
+ if (obj instanceof DERBitString)
+ {
+ this.failInfo = DERBitString.getInstance(obj);
+ }
+ else
+ {
+ this.statusString = PKIFreeText.getInstance(obj);
+ }
+ }
+ }
+
+ /**
+ * @param status
+ */
+ public PKIStatusInfo(PKIStatus status)
+ {
+ this.status = ASN1Integer.getInstance(status.toASN1Primitive());
+ }
+
+ /**
+ *
+ * @param status
+ * @param statusString
+ */
+ public PKIStatusInfo(
+ PKIStatus status,
+ PKIFreeText statusString)
+ {
+ this.status = ASN1Integer.getInstance(status.toASN1Primitive());
+ this.statusString = statusString;
+ }
+
+ public PKIStatusInfo(
+ PKIStatus status,
+ PKIFreeText statusString,
+ PKIFailureInfo failInfo)
+ {
+ this.status = ASN1Integer.getInstance(status.toASN1Primitive());
+ this.statusString = statusString;
+ this.failInfo = failInfo;
+ }
+
+ public BigInteger getStatus()
+ {
+ return status.getValue();
+ }
+
+ public PKIFreeText getStatusString()
+ {
+ return statusString;
+ }
+
+ public DERBitString getFailInfo()
+ {
+ return failInfo;
+ }
+
+ /**
+ * <pre>
+ * PKIStatusInfo ::= SEQUENCE {
+ * status PKIStatus, (INTEGER)
+ * statusString PKIFreeText OPTIONAL,
+ * failInfo PKIFailureInfo OPTIONAL (BIT STRING)
+ * }
+ *
+ * PKIStatus:
+ * granted (0), -- you got exactly what you asked for
+ * grantedWithMods (1), -- you got something like what you asked for
+ * rejection (2), -- you don't get it, more information elsewhere in the message
+ * waiting (3), -- the request body part has not yet been processed, expect to hear more later
+ * revocationWarning (4), -- this message contains a warning that a revocation is imminent
+ * revocationNotification (5), -- notification that a revocation has occurred
+ * keyUpdateWarning (6) -- update already done for the oldCertId specified in CertReqMsg
+ *
+ * PKIFailureInfo:
+ * badAlg (0), -- unrecognized or unsupported Algorithm Identifier
+ * badMessageCheck (1), -- integrity check failed (e.g., signature did not verify)
+ * badRequest (2), -- transaction not permitted or supported
+ * badTime (3), -- messageTime was not sufficiently close to the system time, as defined by local policy
+ * badCertId (4), -- no certificate could be found matching the provided criteria
+ * badDataFormat (5), -- the data submitted has the wrong format
+ * wrongAuthority (6), -- the authority indicated in the request is different from the one creating the response token
+ * incorrectData (7), -- the requester's data is incorrect (for notary services)
+ * missingTimeStamp (8), -- when the timestamp is missing but should be there (by policy)
+ * badPOP (9) -- the proof-of-possession failed
+ *
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(status);
+
+ if (statusString != null)
+ {
+ v.add(statusString);
+ }
+
+ if (failInfo!= null)
+ {
+ v.add(failInfo);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/POPODecKeyChallContent.java b/core/src/main/java/org/bouncycastle/asn1/cmp/POPODecKeyChallContent.java
new file mode 100644
index 00000000..22340687
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/POPODecKeyChallContent.java
@@ -0,0 +1,54 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+
+public class POPODecKeyChallContent
+ extends ASN1Object
+{
+ private ASN1Sequence content;
+
+ private POPODecKeyChallContent(ASN1Sequence seq)
+ {
+ content = seq;
+ }
+
+ public static POPODecKeyChallContent getInstance(Object o)
+ {
+ if (o instanceof POPODecKeyChallContent)
+ {
+ return (POPODecKeyChallContent)o;
+ }
+
+ if (o != null)
+ {
+ return new POPODecKeyChallContent(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public Challenge[] toChallengeArray()
+ {
+ Challenge[] result = new Challenge[content.size()];
+
+ for (int i = 0; i != result.length; i++)
+ {
+ result[i] = Challenge.getInstance(content.getObjectAt(i));
+ }
+
+ return result;
+ }
+
+ /**
+ * <pre>
+ * POPODecKeyChallContent ::= SEQUENCE OF Challenge
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return content;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/POPODecKeyRespContent.java b/core/src/main/java/org/bouncycastle/asn1/cmp/POPODecKeyRespContent.java
new file mode 100644
index 00000000..9c64db06
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/POPODecKeyRespContent.java
@@ -0,0 +1,55 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+
+public class POPODecKeyRespContent
+ extends ASN1Object
+{
+ private ASN1Sequence content;
+
+ private POPODecKeyRespContent(ASN1Sequence seq)
+ {
+ content = seq;
+ }
+
+ public static POPODecKeyRespContent getInstance(Object o)
+ {
+ if (o instanceof POPODecKeyRespContent)
+ {
+ return (POPODecKeyRespContent)o;
+ }
+
+ if (o != null)
+ {
+ return new POPODecKeyRespContent(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public ASN1Integer[] toASN1IntegerArray()
+ {
+ ASN1Integer[] result = new ASN1Integer[content.size()];
+
+ for (int i = 0; i != result.length; i++)
+ {
+ result[i] = ASN1Integer.getInstance(content.getObjectAt(i));
+ }
+
+ return result;
+ }
+
+ /**
+ * <pre>
+ * POPODecKeyRespContent ::= SEQUENCE OF INTEGER
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return content;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/PollRepContent.java b/core/src/main/java/org/bouncycastle/asn1/cmp/PollRepContent.java
new file mode 100644
index 00000000..95d5f82c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/PollRepContent.java
@@ -0,0 +1,119 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class PollRepContent
+ extends ASN1Object
+{
+ private ASN1Integer[] certReqId;
+ private ASN1Integer[] checkAfter;
+ private PKIFreeText[] reason;
+
+ private PollRepContent(ASN1Sequence seq)
+ {
+ certReqId = new ASN1Integer[seq.size()];
+ checkAfter = new ASN1Integer[seq.size()];
+ reason = new PKIFreeText[seq.size()];
+
+ for (int i = 0; i != seq.size(); i++)
+ {
+ ASN1Sequence s = ASN1Sequence.getInstance(seq.getObjectAt(i));
+
+ certReqId[i] = ASN1Integer.getInstance(s.getObjectAt(0));
+ checkAfter[i] = ASN1Integer.getInstance(s.getObjectAt(1));
+
+ if (s.size() > 2)
+ {
+ reason[i] = PKIFreeText.getInstance(s.getObjectAt(2));
+ }
+ }
+ }
+
+ public static PollRepContent getInstance(Object o)
+ {
+ if (o instanceof PollRepContent)
+ {
+ return (PollRepContent)o;
+ }
+
+ if (o != null)
+ {
+ return new PollRepContent(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public PollRepContent(ASN1Integer certReqId, ASN1Integer checkAfter)
+ {
+ this(certReqId, checkAfter, null);
+ }
+
+ public PollRepContent(ASN1Integer certReqId, ASN1Integer checkAfter, PKIFreeText reason)
+ {
+ this.certReqId = new ASN1Integer[1];
+ this.checkAfter = new ASN1Integer[1];
+ this.reason = new PKIFreeText[1];
+
+ this.certReqId[0] = certReqId;
+ this.checkAfter[0] = checkAfter;
+ this.reason[0] = reason;
+ }
+
+ public int size()
+ {
+ return certReqId.length;
+ }
+
+ public ASN1Integer getCertReqId(int index)
+ {
+ return certReqId[index];
+ }
+
+ public ASN1Integer getCheckAfter(int index)
+ {
+ return checkAfter[index];
+ }
+
+ public PKIFreeText getReason(int index)
+ {
+ return reason[index];
+ }
+
+ /**
+ * <pre>
+ * PollRepContent ::= SEQUENCE OF SEQUENCE {
+ * certReqId INTEGER,
+ * checkAfter INTEGER, -- time in seconds
+ * reason PKIFreeText OPTIONAL
+ * }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector outer = new ASN1EncodableVector();
+
+ for (int i = 0; i != certReqId.length; i++)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(certReqId[i]);
+ v.add(checkAfter[i]);
+
+ if (reason[i] != null)
+ {
+ v.add(reason[i]);
+ }
+
+ outer.add(new DERSequence(v));
+ }
+
+ return new DERSequence(outer);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/PollReqContent.java b/core/src/main/java/org/bouncycastle/asn1/cmp/PollReqContent.java
new file mode 100644
index 00000000..de059c5c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/PollReqContent.java
@@ -0,0 +1,80 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class PollReqContent
+ extends ASN1Object
+{
+ private ASN1Sequence content;
+
+ private PollReqContent(ASN1Sequence seq)
+ {
+ content = seq;
+ }
+
+ public static PollReqContent getInstance(Object o)
+ {
+ if (o instanceof PollReqContent)
+ {
+ return (PollReqContent)o;
+ }
+
+ if (o != null)
+ {
+ return new PollReqContent(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ /**
+ * Create a pollReqContent for a single certReqId.
+ *
+ * @param certReqId the certificate request ID.
+ */
+ public PollReqContent(ASN1Integer certReqId)
+ {
+ this(new DERSequence(new DERSequence(certReqId)));
+ }
+
+ public ASN1Integer[][] getCertReqIds()
+ {
+ ASN1Integer[][] result = new ASN1Integer[content.size()][];
+
+ for (int i = 0; i != result.length; i++)
+ {
+ result[i] = sequenceToASN1IntegerArray((ASN1Sequence)content.getObjectAt(i));
+ }
+
+ return result;
+ }
+
+ private static ASN1Integer[] sequenceToASN1IntegerArray(ASN1Sequence seq)
+ {
+ ASN1Integer[] result = new ASN1Integer[seq.size()];
+
+ for (int i = 0; i != result.length; i++)
+ {
+ result[i] = ASN1Integer.getInstance(seq.getObjectAt(i));
+ }
+
+ return result;
+ }
+
+ /**
+ * <pre>
+ * PollReqContent ::= SEQUENCE OF SEQUENCE {
+ * certReqId INTEGER
+ * }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return content;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/ProtectedPart.java b/core/src/main/java/org/bouncycastle/asn1/cmp/ProtectedPart.java
new file mode 100644
index 00000000..38e4fb80
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/ProtectedPart.java
@@ -0,0 +1,70 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class ProtectedPart
+ extends ASN1Object
+{
+ private PKIHeader header;
+ private PKIBody body;
+
+ private ProtectedPart(ASN1Sequence seq)
+ {
+ header = PKIHeader.getInstance(seq.getObjectAt(0));
+ body = PKIBody.getInstance(seq.getObjectAt(1));
+ }
+
+ public static ProtectedPart getInstance(Object o)
+ {
+ if (o instanceof ProtectedPart)
+ {
+ return (ProtectedPart)o;
+ }
+
+ if (o != null)
+ {
+ return new ProtectedPart(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public ProtectedPart(PKIHeader header, PKIBody body)
+ {
+ this.header = header;
+ this.body = body;
+ }
+
+ public PKIHeader getHeader()
+ {
+ return header;
+ }
+
+ public PKIBody getBody()
+ {
+ return body;
+ }
+
+ /**
+ * <pre>
+ * ProtectedPart ::= SEQUENCE {
+ * header PKIHeader,
+ * body PKIBody
+ * }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(header);
+ v.add(body);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/RevAnnContent.java b/core/src/main/java/org/bouncycastle/asn1/cmp/RevAnnContent.java
new file mode 100644
index 00000000..36b4621b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/RevAnnContent.java
@@ -0,0 +1,103 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.crmf.CertId;
+import org.bouncycastle.asn1.x509.Extensions;
+
+public class RevAnnContent
+ extends ASN1Object
+{
+ private PKIStatus status;
+ private CertId certId;
+ private ASN1GeneralizedTime willBeRevokedAt;
+ private ASN1GeneralizedTime badSinceDate;
+ private Extensions crlDetails;
+
+ private RevAnnContent(ASN1Sequence seq)
+ {
+ status = PKIStatus.getInstance(seq.getObjectAt(0));
+ certId = CertId.getInstance(seq.getObjectAt(1));
+ willBeRevokedAt = ASN1GeneralizedTime.getInstance(seq.getObjectAt(2));
+ badSinceDate = ASN1GeneralizedTime.getInstance(seq.getObjectAt(3));
+
+ if (seq.size() > 4)
+ {
+ crlDetails = Extensions.getInstance(seq.getObjectAt(4));
+ }
+ }
+
+ public static RevAnnContent getInstance(Object o)
+ {
+ if (o instanceof RevAnnContent)
+ {
+ return (RevAnnContent)o;
+ }
+
+ if (o != null)
+ {
+ return new RevAnnContent(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public PKIStatus getStatus()
+ {
+ return status;
+ }
+
+ public CertId getCertId()
+ {
+ return certId;
+ }
+
+ public ASN1GeneralizedTime getWillBeRevokedAt()
+ {
+ return willBeRevokedAt;
+ }
+
+ public ASN1GeneralizedTime getBadSinceDate()
+ {
+ return badSinceDate;
+ }
+
+ public Extensions getCrlDetails()
+ {
+ return crlDetails;
+ }
+
+ /**
+ * <pre>
+ * RevAnnContent ::= SEQUENCE {
+ * status PKIStatus,
+ * certId CertId,
+ * willBeRevokedAt GeneralizedTime,
+ * badSinceDate GeneralizedTime,
+ * crlDetails Extensions OPTIONAL
+ * -- extra CRL details (e.g., crl number, reason, location, etc.)
+ * }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(status);
+ v.add(certId);
+ v.add(willBeRevokedAt);
+ v.add(badSinceDate);
+
+ if (crlDetails != null)
+ {
+ v.add(crlDetails);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/RevDetails.java b/core/src/main/java/org/bouncycastle/asn1/cmp/RevDetails.java
new file mode 100644
index 00000000..3d9eb712
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/RevDetails.java
@@ -0,0 +1,100 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.crmf.CertTemplate;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.X509Extensions;
+
+public class RevDetails
+ extends ASN1Object
+{
+ private CertTemplate certDetails;
+ private Extensions crlEntryDetails;
+
+ private RevDetails(ASN1Sequence seq)
+ {
+ certDetails = CertTemplate.getInstance(seq.getObjectAt(0));
+ if (seq.size() > 1)
+ {
+ crlEntryDetails = Extensions.getInstance(seq.getObjectAt(1));
+ }
+ }
+
+ public static RevDetails getInstance(Object o)
+ {
+ if (o instanceof RevDetails)
+ {
+ return (RevDetails)o;
+ }
+
+ if (o != null)
+ {
+ return new RevDetails(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public RevDetails(CertTemplate certDetails)
+ {
+ this.certDetails = certDetails;
+ }
+
+ /**
+ * @deprecated use method taking Extensions
+ * @param certDetails
+ * @param crlEntryDetails
+ */
+ public RevDetails(CertTemplate certDetails, X509Extensions crlEntryDetails)
+ {
+ this.certDetails = certDetails;
+ this.crlEntryDetails = Extensions.getInstance(crlEntryDetails.toASN1Primitive());
+ }
+
+ public RevDetails(CertTemplate certDetails, Extensions crlEntryDetails)
+ {
+ this.certDetails = certDetails;
+ this.crlEntryDetails = crlEntryDetails;
+ }
+
+ public CertTemplate getCertDetails()
+ {
+ return certDetails;
+ }
+
+ public Extensions getCrlEntryDetails()
+ {
+ return crlEntryDetails;
+ }
+
+ /**
+ * <pre>
+ * RevDetails ::= SEQUENCE {
+ * certDetails CertTemplate,
+ * -- allows requester to specify as much as they can about
+ * -- the cert. for which revocation is requested
+ * -- (e.g., for cases in which serialNumber is not available)
+ * crlEntryDetails Extensions OPTIONAL
+ * -- requested crlEntryExtensions
+ * }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(certDetails);
+
+ if (crlEntryDetails != null)
+ {
+ v.add(crlEntryDetails);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/RevRepContent.java b/core/src/main/java/org/bouncycastle/asn1/cmp/RevRepContent.java
new file mode 100644
index 00000000..5cbb8e67
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/RevRepContent.java
@@ -0,0 +1,137 @@
+package org.bouncycastle.asn1.cmp;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.crmf.CertId;
+import org.bouncycastle.asn1.x509.CertificateList;
+
+public class RevRepContent
+ extends ASN1Object
+{
+ private ASN1Sequence status;
+ private ASN1Sequence revCerts;
+ private ASN1Sequence crls;
+
+ private RevRepContent(ASN1Sequence seq)
+ {
+ Enumeration en = seq.getObjects();
+
+ status = ASN1Sequence.getInstance(en.nextElement());
+ while (en.hasMoreElements())
+ {
+ ASN1TaggedObject tObj = ASN1TaggedObject.getInstance(en.nextElement());
+
+ if (tObj.getTagNo() == 0)
+ {
+ revCerts = ASN1Sequence.getInstance(tObj, true);
+ }
+ else
+ {
+ crls = ASN1Sequence.getInstance(tObj, true);
+ }
+ }
+ }
+
+ public static RevRepContent getInstance(Object o)
+ {
+ if (o instanceof RevRepContent)
+ {
+ return (RevRepContent)o;
+ }
+
+ if (o != null)
+ {
+ return new RevRepContent(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public PKIStatusInfo[] getStatus()
+ {
+ PKIStatusInfo[] results = new PKIStatusInfo[status.size()];
+
+ for (int i = 0; i != results.length; i++)
+ {
+ results[i] = PKIStatusInfo.getInstance(status.getObjectAt(i));
+ }
+
+ return results;
+ }
+
+ public CertId[] getRevCerts()
+ {
+ if (revCerts == null)
+ {
+ return null;
+ }
+
+ CertId[] results = new CertId[revCerts.size()];
+
+ for (int i = 0; i != results.length; i++)
+ {
+ results[i] = CertId.getInstance(revCerts.getObjectAt(i));
+ }
+
+ return results;
+ }
+
+ public CertificateList[] getCrls()
+ {
+ if (crls == null)
+ {
+ return null;
+ }
+
+ CertificateList[] results = new CertificateList[crls.size()];
+
+ for (int i = 0; i != results.length; i++)
+ {
+ results[i] = CertificateList.getInstance(crls.getObjectAt(i));
+ }
+
+ return results;
+ }
+
+ /**
+ * <pre>
+ * RevRepContent ::= SEQUENCE {
+ * status SEQUENCE SIZE (1..MAX) OF PKIStatusInfo,
+ * -- in same order as was sent in RevReqContent
+ * revCerts [0] SEQUENCE SIZE (1..MAX) OF CertId OPTIONAL,
+ * -- IDs for which revocation was requested
+ * -- (same order as status)
+ * crls [1] SEQUENCE SIZE (1..MAX) OF CertificateList OPTIONAL
+ * -- the resulting CRLs (there may be more than one)
+ * }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(status);
+
+ addOptional(v, 0, revCerts);
+ addOptional(v, 1, crls);
+
+ return new DERSequence(v);
+ }
+
+ private void addOptional(ASN1EncodableVector v, int tagNo, ASN1Encodable obj)
+ {
+ if (obj != null)
+ {
+ v.add(new DERTaggedObject(true, tagNo, obj));
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/RevRepContentBuilder.java b/core/src/main/java/org/bouncycastle/asn1/cmp/RevRepContentBuilder.java
new file mode 100644
index 00000000..10522c2f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/RevRepContentBuilder.java
@@ -0,0 +1,59 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.crmf.CertId;
+import org.bouncycastle.asn1.x509.CertificateList;
+
+public class RevRepContentBuilder
+{
+ private ASN1EncodableVector status = new ASN1EncodableVector();
+ private ASN1EncodableVector revCerts = new ASN1EncodableVector();
+ private ASN1EncodableVector crls = new ASN1EncodableVector();
+
+ public RevRepContentBuilder add(PKIStatusInfo status)
+ {
+ this.status.add(status);
+
+ return this;
+ }
+
+ public RevRepContentBuilder add(PKIStatusInfo status, CertId certId)
+ {
+ if (this.status.size() != this.revCerts.size())
+ {
+ throw new IllegalStateException("status and revCerts sequence must be in common order");
+ }
+ this.status.add(status);
+ this.revCerts.add(certId);
+
+ return this;
+ }
+
+ public RevRepContentBuilder addCrl(CertificateList crl)
+ {
+ this.crls.add(crl);
+
+ return this;
+ }
+
+ public RevRepContent build()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new DERSequence(status));
+
+ if (revCerts.size() != 0)
+ {
+ v.add(new DERTaggedObject(true, 0, new DERSequence(revCerts)));
+ }
+
+ if (crls.size() != 0)
+ {
+ v.add(new DERTaggedObject(true, 1, new DERSequence(crls)));
+ }
+
+ return RevRepContent.getInstance(new DERSequence(v));
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cmp/RevReqContent.java b/core/src/main/java/org/bouncycastle/asn1/cmp/RevReqContent.java
new file mode 100644
index 00000000..468be4ef
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cmp/RevReqContent.java
@@ -0,0 +1,73 @@
+package org.bouncycastle.asn1.cmp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class RevReqContent
+ extends ASN1Object
+{
+ private ASN1Sequence content;
+
+ private RevReqContent(ASN1Sequence seq)
+ {
+ content = seq;
+ }
+
+ public static RevReqContent getInstance(Object o)
+ {
+ if (o instanceof RevReqContent)
+ {
+ return (RevReqContent)o;
+ }
+
+ if (o != null)
+ {
+ return new RevReqContent(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public RevReqContent(RevDetails revDetails)
+ {
+ this.content = new DERSequence(revDetails);
+ }
+
+ public RevReqContent(RevDetails[] revDetailsArray)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ for (int i = 0; i != revDetailsArray.length; i++)
+ {
+ v.add(revDetailsArray[i]);
+ }
+
+ this.content = new DERSequence(v);
+ }
+
+ public RevDetails[] toRevDetailsArray()
+ {
+ RevDetails[] result = new RevDetails[content.size()];
+
+ for (int i = 0; i != result.length; i++)
+ {
+ result[i] = RevDetails.getInstance(content.getObjectAt(i));
+ }
+
+ return result;
+ }
+
+ /**
+ * <pre>
+ * RevReqContent ::= SEQUENCE OF RevDetails
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return content;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/Attribute.java b/core/src/main/java/org/bouncycastle/asn1/cms/Attribute.java
new file mode 100644
index 00000000..b5a2f345
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/Attribute.java
@@ -0,0 +1,100 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERSequence;
+
+public class Attribute
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier attrType;
+ private ASN1Set attrValues;
+
+ /**
+ * return an Attribute object from the given object.
+ *
+ * @param o the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static Attribute getInstance(
+ Object o)
+ {
+ if (o instanceof Attribute)
+ {
+ return (Attribute)o;
+ }
+
+ if (o != null)
+ {
+ return new Attribute(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ private Attribute(
+ ASN1Sequence seq)
+ {
+ attrType = (ASN1ObjectIdentifier)seq.getObjectAt(0);
+ attrValues = (ASN1Set)seq.getObjectAt(1);
+ }
+
+ /**
+ * @deprecated use ASN1ObjectIdentifier
+ */
+ public Attribute(
+ DERObjectIdentifier attrType,
+ ASN1Set attrValues)
+ {
+ this.attrType = new ASN1ObjectIdentifier(attrType.getId());
+ this.attrValues = attrValues;
+ }
+
+ public Attribute(
+ ASN1ObjectIdentifier attrType,
+ ASN1Set attrValues)
+ {
+ this.attrType = attrType;
+ this.attrValues = attrValues;
+ }
+
+ public ASN1ObjectIdentifier getAttrType()
+ {
+ return attrType;
+ }
+
+ public ASN1Set getAttrValues()
+ {
+ return attrValues;
+ }
+
+ public ASN1Encodable[] getAttributeValues()
+ {
+ return attrValues.toArray();
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * Attribute ::= SEQUENCE {
+ * attrType OBJECT IDENTIFIER,
+ * attrValues SET OF AttributeValue
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(attrType);
+ v.add(attrValues);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/AttributeTable.java b/core/src/main/java/org/bouncycastle/asn1/cms/AttributeTable.java
new file mode 100644
index 00000000..f114623b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/AttributeTable.java
@@ -0,0 +1,254 @@
+package org.bouncycastle.asn1.cms;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERSet;
+
+public class AttributeTable
+{
+ private Hashtable attributes = new Hashtable();
+
+ public AttributeTable(
+ Hashtable attrs)
+ {
+ attributes = copyTable(attrs);
+ }
+
+ public AttributeTable(
+ ASN1EncodableVector v)
+ {
+ for (int i = 0; i != v.size(); i++)
+ {
+ Attribute a = Attribute.getInstance(v.get(i));
+
+ addAttribute(a.getAttrType(), a);
+ }
+ }
+
+ public AttributeTable(
+ ASN1Set s)
+ {
+ for (int i = 0; i != s.size(); i++)
+ {
+ Attribute a = Attribute.getInstance(s.getObjectAt(i));
+
+ addAttribute(a.getAttrType(), a);
+ }
+ }
+
+ public AttributeTable(
+ Attribute attr)
+ {
+ addAttribute(attr.getAttrType(), attr);
+ }
+
+ public AttributeTable(
+ Attributes attrs)
+ {
+ this(ASN1Set.getInstance(attrs.toASN1Primitive()));
+ }
+
+ private void addAttribute(
+ ASN1ObjectIdentifier oid,
+ Attribute a)
+ {
+ Object value = attributes.get(oid);
+
+ if (value == null)
+ {
+ attributes.put(oid, a);
+ }
+ else
+ {
+ Vector v;
+
+ if (value instanceof Attribute)
+ {
+ v = new Vector();
+
+ v.addElement(value);
+ v.addElement(a);
+ }
+ else
+ {
+ v = (Vector)value;
+
+ v.addElement(a);
+ }
+
+ attributes.put(oid, v);
+ }
+ }
+
+ /**
+ * @deprecated use ASN1ObjectIdentifier
+ */
+ public Attribute get(DERObjectIdentifier oid)
+ {
+ return get(new ASN1ObjectIdentifier(oid.getId()));
+ }
+
+ /**
+ * Return the first attribute matching the OBJECT IDENTIFIER oid.
+ *
+ * @param oid type of attribute required.
+ * @return first attribute found of type oid.
+ */
+ public Attribute get(
+ ASN1ObjectIdentifier oid)
+ {
+ Object value = attributes.get(oid);
+
+ if (value instanceof Vector)
+ {
+ return (Attribute)((Vector)value).elementAt(0);
+ }
+
+ return (Attribute)value;
+ }
+
+ /**
+ * @deprecated use ASN1ObjectIdentifier
+ */
+ public ASN1EncodableVector getAll(DERObjectIdentifier oid)
+ {
+ return getAll(new ASN1ObjectIdentifier(oid.getId()));
+ }
+
+ /**
+ * Return all the attributes matching the OBJECT IDENTIFIER oid. The vector will be
+ * empty if there are no attributes of the required type present.
+ *
+ * @param oid type of attribute required.
+ * @return a vector of all the attributes found of type oid.
+ */
+ public ASN1EncodableVector getAll(
+ ASN1ObjectIdentifier oid)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ Object value = attributes.get(oid);
+
+ if (value instanceof Vector)
+ {
+ Enumeration e = ((Vector)value).elements();
+
+ while (e.hasMoreElements())
+ {
+ v.add((Attribute)e.nextElement());
+ }
+ }
+ else if (value != null)
+ {
+ v.add((Attribute)value);
+ }
+
+ return v;
+ }
+
+ public int size()
+ {
+ int size = 0;
+
+ for (Enumeration en = attributes.elements(); en.hasMoreElements();)
+ {
+ Object o = en.nextElement();
+
+ if (o instanceof Vector)
+ {
+ size += ((Vector)o).size();
+ }
+ else
+ {
+ size++;
+ }
+ }
+
+ return size;
+ }
+
+ public Hashtable toHashtable()
+ {
+ return copyTable(attributes);
+ }
+
+ public ASN1EncodableVector toASN1EncodableVector()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ Enumeration e = attributes.elements();
+
+ while (e.hasMoreElements())
+ {
+ Object value = e.nextElement();
+
+ if (value instanceof Vector)
+ {
+ Enumeration en = ((Vector)value).elements();
+
+ while (en.hasMoreElements())
+ {
+ v.add(Attribute.getInstance(en.nextElement()));
+ }
+ }
+ else
+ {
+ v.add(Attribute.getInstance(value));
+ }
+ }
+
+ return v;
+ }
+
+ public Attributes toASN1Structure()
+ {
+ return new Attributes(this.toASN1EncodableVector());
+ }
+
+ private Hashtable copyTable(
+ Hashtable in)
+ {
+ Hashtable out = new Hashtable();
+ Enumeration e = in.keys();
+
+ while (e.hasMoreElements())
+ {
+ Object key = e.nextElement();
+
+ out.put(key, in.get(key));
+ }
+
+ return out;
+ }
+
+ /**
+ * Return a new table with the passed in attribute added.
+ *
+ * @param attrType
+ * @param attrValue
+ * @return
+ */
+ public AttributeTable add(ASN1ObjectIdentifier attrType, ASN1Encodable attrValue)
+ {
+ AttributeTable newTable = new AttributeTable(attributes);
+
+ newTable.addAttribute(attrType, new Attribute(attrType, new DERSet(attrValue)));
+
+ return newTable;
+ }
+
+ public AttributeTable remove(ASN1ObjectIdentifier attrType)
+ {
+ AttributeTable newTable = new AttributeTable(attributes);
+
+ newTable.attributes.remove(attrType);
+
+ return newTable;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/Attributes.java b/core/src/main/java/org/bouncycastle/asn1/cms/Attributes.java
new file mode 100644
index 00000000..614e2244
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/Attributes.java
@@ -0,0 +1,61 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.DLSet;
+
+public class Attributes
+ extends ASN1Object
+{
+ private ASN1Set attributes;
+
+ private Attributes(ASN1Set set)
+ {
+ attributes = set;
+ }
+
+ public Attributes(ASN1EncodableVector v)
+ {
+ attributes = new DLSet(v);
+ }
+
+ public static Attributes getInstance(Object obj)
+ {
+ if (obj instanceof Attributes)
+ {
+ return (Attributes)obj;
+ }
+ else if (obj != null)
+ {
+ return new Attributes(ASN1Set.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public Attribute[] getAttributes()
+ {
+ Attribute[] rv = new Attribute[attributes.size()];
+
+ for (int i = 0; i != rv.length; i++)
+ {
+ rv[i] = Attribute.getInstance(attributes.getObjectAt(i));
+ }
+
+ return rv;
+ }
+
+ /**
+ * <pre>
+ * Attributes ::=
+ * SET SIZE(1..MAX) OF Attribute -- according to RFC 5652
+ * </pre>
+ * @return
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return attributes;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedData.java b/core/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedData.java
new file mode 100644
index 00000000..5152dc92
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedData.java
@@ -0,0 +1,218 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.BERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class AuthEnvelopedData
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private OriginatorInfo originatorInfo;
+ private ASN1Set recipientInfos;
+ private EncryptedContentInfo authEncryptedContentInfo;
+ private ASN1Set authAttrs;
+ private ASN1OctetString mac;
+ private ASN1Set unauthAttrs;
+
+ public AuthEnvelopedData(
+ OriginatorInfo originatorInfo,
+ ASN1Set recipientInfos,
+ EncryptedContentInfo authEncryptedContentInfo,
+ ASN1Set authAttrs,
+ ASN1OctetString mac,
+ ASN1Set unauthAttrs)
+ {
+ // "It MUST be set to 0."
+ this.version = new ASN1Integer(0);
+
+ this.originatorInfo = originatorInfo;
+
+ // TODO
+ // "There MUST be at least one element in the collection."
+ this.recipientInfos = recipientInfos;
+
+ this.authEncryptedContentInfo = authEncryptedContentInfo;
+
+ // TODO
+ // "The authAttrs MUST be present if the content type carried in
+ // EncryptedContentInfo is not id-data."
+ this.authAttrs = authAttrs;
+
+ this.mac = mac;
+
+ this.unauthAttrs = unauthAttrs;
+ }
+
+ public AuthEnvelopedData(
+ ASN1Sequence seq)
+ {
+ int index = 0;
+
+ // TODO
+ // "It MUST be set to 0."
+ ASN1Primitive tmp = seq.getObjectAt(index++).toASN1Primitive();
+ version = (ASN1Integer)tmp;
+
+ tmp = seq.getObjectAt(index++).toASN1Primitive();
+ if (tmp instanceof ASN1TaggedObject)
+ {
+ originatorInfo = OriginatorInfo.getInstance((ASN1TaggedObject)tmp, false);
+ tmp = seq.getObjectAt(index++).toASN1Primitive();
+ }
+
+ // TODO
+ // "There MUST be at least one element in the collection."
+ recipientInfos = ASN1Set.getInstance(tmp);
+
+ tmp = seq.getObjectAt(index++).toASN1Primitive();
+ authEncryptedContentInfo = EncryptedContentInfo.getInstance(tmp);
+
+ tmp = seq.getObjectAt(index++).toASN1Primitive();
+ if (tmp instanceof ASN1TaggedObject)
+ {
+ authAttrs = ASN1Set.getInstance((ASN1TaggedObject)tmp, false);
+ tmp = seq.getObjectAt(index++).toASN1Primitive();
+ }
+ else
+ {
+ // TODO
+ // "The authAttrs MUST be present if the content type carried in
+ // EncryptedContentInfo is not id-data."
+ }
+
+ mac = ASN1OctetString.getInstance(tmp);
+
+ if (seq.size() > index)
+ {
+ tmp = seq.getObjectAt(index++).toASN1Primitive();
+ unauthAttrs = ASN1Set.getInstance((ASN1TaggedObject)tmp, false);
+ }
+ }
+
+ /**
+ * return an AuthEnvelopedData 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.
+ * @throws IllegalArgumentException if the object held by the
+ * tagged object cannot be converted.
+ */
+ public static AuthEnvelopedData getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * return an AuthEnvelopedData object from the given object.
+ *
+ * @param obj the object we want converted.
+ * @throws IllegalArgumentException if the object cannot be converted.
+ */
+ public static AuthEnvelopedData getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof AuthEnvelopedData)
+ {
+ return (AuthEnvelopedData)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new AuthEnvelopedData((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid AuthEnvelopedData: " + obj.getClass().getName());
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public OriginatorInfo getOriginatorInfo()
+ {
+ return originatorInfo;
+ }
+
+ public ASN1Set getRecipientInfos()
+ {
+ return recipientInfos;
+ }
+
+ public EncryptedContentInfo getAuthEncryptedContentInfo()
+ {
+ return authEncryptedContentInfo;
+ }
+
+ public ASN1Set getAuthAttrs()
+ {
+ return authAttrs;
+ }
+
+ public ASN1OctetString getMac()
+ {
+ return mac;
+ }
+
+ public ASN1Set getUnauthAttrs()
+ {
+ return unauthAttrs;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * AuthEnvelopedData ::= SEQUENCE {
+ * version CMSVersion,
+ * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+ * recipientInfos RecipientInfos,
+ * authEncryptedContentInfo EncryptedContentInfo,
+ * authAttrs [1] IMPLICIT AuthAttributes OPTIONAL,
+ * mac MessageAuthenticationCode,
+ * unauthAttrs [2] IMPLICIT UnauthAttributes OPTIONAL }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+
+ if (originatorInfo != null)
+ {
+ v.add(new DERTaggedObject(false, 0, originatorInfo));
+ }
+
+ v.add(recipientInfos);
+ v.add(authEncryptedContentInfo);
+
+ // "authAttrs optionally contains the authenticated attributes."
+ if (authAttrs != null)
+ {
+ // "AuthAttributes MUST be DER encoded, even if the rest of the
+ // AuthEnvelopedData structure is BER encoded."
+ v.add(new DERTaggedObject(false, 1, authAttrs));
+ }
+
+ v.add(mac);
+
+ // "unauthAttrs optionally contains the unauthenticated attributes."
+ if (unauthAttrs != null)
+ {
+ v.add(new DERTaggedObject(false, 2, unauthAttrs));
+ }
+
+ return new BERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedDataParser.java b/core/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedDataParser.java
new file mode 100644
index 00000000..55569a77
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedDataParser.java
@@ -0,0 +1,157 @@
+package org.bouncycastle.asn1.cms;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1SequenceParser;
+import org.bouncycastle.asn1.ASN1SetParser;
+import org.bouncycastle.asn1.ASN1TaggedObjectParser;
+import org.bouncycastle.asn1.BERTags;
+
+/**
+ * Produce an object suitable for an ASN1OutputStream.
+ *
+ * <pre>
+ * AuthEnvelopedData ::= SEQUENCE {
+ * version CMSVersion,
+ * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+ * recipientInfos RecipientInfos,
+ * authEncryptedContentInfo EncryptedContentInfo,
+ * authAttrs [1] IMPLICIT AuthAttributes OPTIONAL,
+ * mac MessageAuthenticationCode,
+ * unauthAttrs [2] IMPLICIT UnauthAttributes OPTIONAL }
+ * </pre>
+ */
+public class AuthEnvelopedDataParser
+{
+ private ASN1SequenceParser seq;
+ private ASN1Integer version;
+ private ASN1Encodable nextObject;
+ private boolean originatorInfoCalled;
+
+ public AuthEnvelopedDataParser(ASN1SequenceParser seq) throws IOException
+ {
+ this.seq = seq;
+
+ // TODO
+ // "It MUST be set to 0."
+ this.version = ASN1Integer.getInstance(seq.readObject());
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public OriginatorInfo getOriginatorInfo()
+ throws IOException
+ {
+ originatorInfoCalled = true;
+
+ if (nextObject == null)
+ {
+ nextObject = seq.readObject();
+ }
+
+ if (nextObject instanceof ASN1TaggedObjectParser && ((ASN1TaggedObjectParser)nextObject).getTagNo() == 0)
+ {
+ ASN1SequenceParser originatorInfo = (ASN1SequenceParser) ((ASN1TaggedObjectParser)nextObject).getObjectParser(BERTags.SEQUENCE, false);
+ nextObject = null;
+ return OriginatorInfo.getInstance(originatorInfo.toASN1Primitive());
+ }
+
+ return null;
+ }
+
+ public ASN1SetParser getRecipientInfos()
+ throws IOException
+ {
+ if (!originatorInfoCalled)
+ {
+ getOriginatorInfo();
+ }
+
+ if (nextObject == null)
+ {
+ nextObject = seq.readObject();
+ }
+
+ ASN1SetParser recipientInfos = (ASN1SetParser)nextObject;
+ nextObject = null;
+ return recipientInfos;
+ }
+
+ public EncryptedContentInfoParser getAuthEncryptedContentInfo()
+ throws IOException
+ {
+ if (nextObject == null)
+ {
+ nextObject = seq.readObject();
+ }
+
+ if (nextObject != null)
+ {
+ ASN1SequenceParser o = (ASN1SequenceParser) nextObject;
+ nextObject = null;
+ return new EncryptedContentInfoParser(o);
+ }
+
+ return null;
+ }
+
+ public ASN1SetParser getAuthAttrs()
+ throws IOException
+ {
+ if (nextObject == null)
+ {
+ nextObject = seq.readObject();
+ }
+
+ if (nextObject instanceof ASN1TaggedObjectParser)
+ {
+ ASN1Encodable o = nextObject;
+ nextObject = null;
+ return (ASN1SetParser)((ASN1TaggedObjectParser)o).getObjectParser(BERTags.SET, false);
+ }
+
+ // TODO
+ // "The authAttrs MUST be present if the content type carried in
+ // EncryptedContentInfo is not id-data."
+
+ return null;
+ }
+
+ public ASN1OctetString getMac()
+ throws IOException
+ {
+ if (nextObject == null)
+ {
+ nextObject = seq.readObject();
+ }
+
+ ASN1Encodable o = nextObject;
+ nextObject = null;
+
+ return ASN1OctetString.getInstance(o.toASN1Primitive());
+ }
+
+ public ASN1SetParser getUnauthAttrs()
+ throws IOException
+ {
+ if (nextObject == null)
+ {
+ nextObject = seq.readObject();
+ }
+
+ if (nextObject != null)
+ {
+ ASN1Encodable o = nextObject;
+ nextObject = null;
+ return (ASN1SetParser)((ASN1TaggedObjectParser)o).getObjectParser(BERTags.SET, false);
+ }
+
+ return null;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedData.java b/core/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedData.java
new file mode 100644
index 00000000..bbf98f1e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedData.java
@@ -0,0 +1,296 @@
+package org.bouncycastle.asn1.cms;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.BERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class AuthenticatedData
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private OriginatorInfo originatorInfo;
+ private ASN1Set recipientInfos;
+ private AlgorithmIdentifier macAlgorithm;
+ private AlgorithmIdentifier digestAlgorithm;
+ private ContentInfo encapsulatedContentInfo;
+ private ASN1Set authAttrs;
+ private ASN1OctetString mac;
+ private ASN1Set unauthAttrs;
+
+ public AuthenticatedData(
+ OriginatorInfo originatorInfo,
+ ASN1Set recipientInfos,
+ AlgorithmIdentifier macAlgorithm,
+ AlgorithmIdentifier digestAlgorithm,
+ ContentInfo encapsulatedContent,
+ ASN1Set authAttrs,
+ ASN1OctetString mac,
+ ASN1Set unauthAttrs)
+ {
+ if (digestAlgorithm != null || authAttrs != null)
+ {
+ if (digestAlgorithm == null || authAttrs == null)
+ {
+ throw new IllegalArgumentException("digestAlgorithm and authAttrs must be set together");
+ }
+ }
+
+ version = new ASN1Integer(calculateVersion(originatorInfo));
+
+ this.originatorInfo = originatorInfo;
+ this.macAlgorithm = macAlgorithm;
+ this.digestAlgorithm = digestAlgorithm;
+ this.recipientInfos = recipientInfos;
+ this.encapsulatedContentInfo = encapsulatedContent;
+ this.authAttrs = authAttrs;
+ this.mac = mac;
+ this.unauthAttrs = unauthAttrs;
+ }
+
+ public AuthenticatedData(
+ ASN1Sequence seq)
+ {
+ int index = 0;
+
+ version = (ASN1Integer)seq.getObjectAt(index++);
+
+ Object tmp = seq.getObjectAt(index++);
+
+ if (tmp instanceof ASN1TaggedObject)
+ {
+ originatorInfo = OriginatorInfo.getInstance((ASN1TaggedObject)tmp, false);
+ tmp = seq.getObjectAt(index++);
+ }
+
+ recipientInfos = ASN1Set.getInstance(tmp);
+ macAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(index++));
+
+ tmp = seq.getObjectAt(index++);
+
+ if (tmp instanceof ASN1TaggedObject)
+ {
+ digestAlgorithm = AlgorithmIdentifier.getInstance((ASN1TaggedObject)tmp, false);
+ tmp = seq.getObjectAt(index++);
+ }
+
+ encapsulatedContentInfo = ContentInfo.getInstance(tmp);
+
+ tmp = seq.getObjectAt(index++);
+
+ if (tmp instanceof ASN1TaggedObject)
+ {
+ authAttrs = ASN1Set.getInstance((ASN1TaggedObject)tmp, false);
+ tmp = seq.getObjectAt(index++);
+ }
+
+ mac = ASN1OctetString.getInstance(tmp);
+
+ if (seq.size() > index)
+ {
+ unauthAttrs = ASN1Set.getInstance((ASN1TaggedObject)seq.getObjectAt(index), false);
+ }
+ }
+
+ /**
+ * return an AuthenticatedData 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.
+ * @throws IllegalArgumentException if the object held by the
+ * tagged object cannot be converted.
+ */
+ public static AuthenticatedData getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * return an AuthenticatedData object from the given object.
+ *
+ * @param obj the object we want converted.
+ * @throws IllegalArgumentException if the object cannot be converted.
+ */
+ public static AuthenticatedData getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof AuthenticatedData)
+ {
+ return (AuthenticatedData)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new AuthenticatedData((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid AuthenticatedData: " + obj.getClass().getName());
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public OriginatorInfo getOriginatorInfo()
+ {
+ return originatorInfo;
+ }
+
+ public ASN1Set getRecipientInfos()
+ {
+ return recipientInfos;
+ }
+
+ public AlgorithmIdentifier getMacAlgorithm()
+ {
+ return macAlgorithm;
+ }
+
+ public AlgorithmIdentifier getDigestAlgorithm()
+ {
+ return digestAlgorithm;
+ }
+
+ public ContentInfo getEncapsulatedContentInfo()
+ {
+ return encapsulatedContentInfo;
+ }
+
+ public ASN1Set getAuthAttrs()
+ {
+ return authAttrs;
+ }
+
+ public ASN1OctetString getMac()
+ {
+ return mac;
+ }
+
+ public ASN1Set getUnauthAttrs()
+ {
+ return unauthAttrs;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * AuthenticatedData ::= SEQUENCE {
+ * version CMSVersion,
+ * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+ * recipientInfos RecipientInfos,
+ * macAlgorithm MessageAuthenticationCodeAlgorithm,
+ * digestAlgorithm [1] DigestAlgorithmIdentifier OPTIONAL,
+ * encapContentInfo EncapsulatedContentInfo,
+ * authAttrs [2] IMPLICIT AuthAttributes OPTIONAL,
+ * mac MessageAuthenticationCode,
+ * unauthAttrs [3] IMPLICIT UnauthAttributes OPTIONAL }
+ *
+ * AuthAttributes ::= SET SIZE (1..MAX) OF Attribute
+ *
+ * UnauthAttributes ::= SET SIZE (1..MAX) OF Attribute
+ *
+ * MessageAuthenticationCode ::= OCTET STRING
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+
+ if (originatorInfo != null)
+ {
+ v.add(new DERTaggedObject(false, 0, originatorInfo));
+ }
+
+ v.add(recipientInfos);
+ v.add(macAlgorithm);
+
+ if (digestAlgorithm != null)
+ {
+ v.add(new DERTaggedObject(false, 1, digestAlgorithm));
+ }
+
+ v.add(encapsulatedContentInfo);
+
+ if (authAttrs != null)
+ {
+ v.add(new DERTaggedObject(false, 2, authAttrs));
+ }
+
+ v.add(mac);
+
+ if (unauthAttrs != null)
+ {
+ v.add(new DERTaggedObject(false, 3, unauthAttrs));
+ }
+
+ return new BERSequence(v);
+ }
+
+ public static int calculateVersion(OriginatorInfo origInfo)
+ {
+ if (origInfo == null)
+ {
+ return 0;
+ }
+ else
+ {
+ int ver = 0;
+
+ for (Enumeration e = origInfo.getCertificates().getObjects(); e.hasMoreElements();)
+ {
+ Object obj = e.nextElement();
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject tag = (ASN1TaggedObject)obj;
+
+ if (tag.getTagNo() == 2)
+ {
+ ver = 1;
+ }
+ else if (tag.getTagNo() == 3)
+ {
+ ver = 3;
+ break;
+ }
+ }
+ }
+
+ if (origInfo.getCRLs() != null)
+ {
+ for (Enumeration e = origInfo.getCRLs().getObjects(); e.hasMoreElements();)
+ {
+ Object obj = e.nextElement();
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject tag = (ASN1TaggedObject)obj;
+
+ if (tag.getTagNo() == 1)
+ {
+ ver = 3;
+ break;
+ }
+ }
+ }
+ }
+
+ return ver;
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedDataParser.java b/core/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedDataParser.java
new file mode 100644
index 00000000..fd867e22
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedDataParser.java
@@ -0,0 +1,197 @@
+package org.bouncycastle.asn1.cms;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1SequenceParser;
+import org.bouncycastle.asn1.ASN1SetParser;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.ASN1TaggedObjectParser;
+import org.bouncycastle.asn1.BERTags;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * AuthenticatedData ::= SEQUENCE {
+ * version CMSVersion,
+ * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+ * recipientInfos RecipientInfos,
+ * macAlgorithm MessageAuthenticationCodeAlgorithm,
+ * digestAlgorithm [1] DigestAlgorithmIdentifier OPTIONAL,
+ * encapContentInfo EncapsulatedContentInfo,
+ * authAttrs [2] IMPLICIT AuthAttributes OPTIONAL,
+ * mac MessageAuthenticationCode,
+ * unauthAttrs [3] IMPLICIT UnauthAttributes OPTIONAL }
+ *
+ * AuthAttributes ::= SET SIZE (1..MAX) OF Attribute
+ *
+ * UnauthAttributes ::= SET SIZE (1..MAX) OF Attribute
+ *
+ * MessageAuthenticationCode ::= OCTET STRING
+ * </pre>
+ */
+public class AuthenticatedDataParser
+{
+ private ASN1SequenceParser seq;
+ private ASN1Integer version;
+ private ASN1Encodable nextObject;
+ private boolean originatorInfoCalled;
+
+ public AuthenticatedDataParser(
+ ASN1SequenceParser seq)
+ throws IOException
+ {
+ this.seq = seq;
+ this.version = ASN1Integer.getInstance(seq.readObject());
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public OriginatorInfo getOriginatorInfo()
+ throws IOException
+ {
+ originatorInfoCalled = true;
+
+ if (nextObject == null)
+ {
+ nextObject = seq.readObject();
+ }
+
+ if (nextObject instanceof ASN1TaggedObjectParser && ((ASN1TaggedObjectParser)nextObject).getTagNo() == 0)
+ {
+ ASN1SequenceParser originatorInfo = (ASN1SequenceParser) ((ASN1TaggedObjectParser)nextObject).getObjectParser(BERTags.SEQUENCE, false);
+ nextObject = null;
+ return OriginatorInfo.getInstance(originatorInfo.toASN1Primitive());
+ }
+
+ return null;
+ }
+
+ public ASN1SetParser getRecipientInfos()
+ throws IOException
+ {
+ if (!originatorInfoCalled)
+ {
+ getOriginatorInfo();
+ }
+
+ if (nextObject == null)
+ {
+ nextObject = seq.readObject();
+ }
+
+ ASN1SetParser recipientInfos = (ASN1SetParser)nextObject;
+ nextObject = null;
+ return recipientInfos;
+ }
+
+ public AlgorithmIdentifier getMacAlgorithm()
+ throws IOException
+ {
+ if (nextObject == null)
+ {
+ nextObject = seq.readObject();
+ }
+
+ if (nextObject != null)
+ {
+ ASN1SequenceParser o = (ASN1SequenceParser)nextObject;
+ nextObject = null;
+ return AlgorithmIdentifier.getInstance(o.toASN1Primitive());
+ }
+
+ return null;
+ }
+
+ public AlgorithmIdentifier getDigestAlgorithm()
+ throws IOException
+ {
+ if (nextObject == null)
+ {
+ nextObject = seq.readObject();
+ }
+
+ if (nextObject instanceof ASN1TaggedObjectParser)
+ {
+ AlgorithmIdentifier obj = AlgorithmIdentifier.getInstance((ASN1TaggedObject)nextObject.toASN1Primitive(), false);
+ nextObject = null;
+ return obj;
+ }
+
+ return null;
+ }
+
+ public ContentInfoParser getEnapsulatedContentInfo()
+ throws IOException
+ {
+ if (nextObject == null)
+ {
+ nextObject = seq.readObject();
+ }
+
+ if (nextObject != null)
+ {
+ ASN1SequenceParser o = (ASN1SequenceParser)nextObject;
+ nextObject = null;
+ return new ContentInfoParser(o);
+ }
+
+ return null;
+ }
+
+ public ASN1SetParser getAuthAttrs()
+ throws IOException
+ {
+ if (nextObject == null)
+ {
+ nextObject = seq.readObject();
+ }
+
+ if (nextObject instanceof ASN1TaggedObjectParser)
+ {
+ ASN1Encodable o = nextObject;
+ nextObject = null;
+ return (ASN1SetParser)((ASN1TaggedObjectParser)o).getObjectParser(BERTags.SET, false);
+ }
+
+ return null;
+ }
+
+ public ASN1OctetString getMac()
+ throws IOException
+ {
+ if (nextObject == null)
+ {
+ nextObject = seq.readObject();
+ }
+
+ ASN1Encodable o = nextObject;
+ nextObject = null;
+
+ return ASN1OctetString.getInstance(o.toASN1Primitive());
+ }
+
+ public ASN1SetParser getUnauthAttrs()
+ throws IOException
+ {
+ if (nextObject == null)
+ {
+ nextObject = seq.readObject();
+ }
+
+ if (nextObject != null)
+ {
+ ASN1Encodable o = nextObject;
+ nextObject = null;
+ return (ASN1SetParser)((ASN1TaggedObjectParser)o).getObjectParser(BERTags.SET, false);
+ }
+
+ return null;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/CMSAttributes.java b/core/src/main/java/org/bouncycastle/asn1/cms/CMSAttributes.java
new file mode 100644
index 00000000..5e973248
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/CMSAttributes.java
@@ -0,0 +1,13 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+
+public interface CMSAttributes
+{
+ public static final ASN1ObjectIdentifier contentType = PKCSObjectIdentifiers.pkcs_9_at_contentType;
+ public static final ASN1ObjectIdentifier messageDigest = PKCSObjectIdentifiers.pkcs_9_at_messageDigest;
+ public static final ASN1ObjectIdentifier signingTime = PKCSObjectIdentifiers.pkcs_9_at_signingTime;
+ public static final ASN1ObjectIdentifier counterSignature = PKCSObjectIdentifiers.pkcs_9_at_counterSignature;
+ public static final ASN1ObjectIdentifier contentHint = PKCSObjectIdentifiers.id_aa_contentHint;
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/CMSObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/cms/CMSObjectIdentifiers.java
new file mode 100644
index 00000000..6294d976
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/CMSObjectIdentifiers.java
@@ -0,0 +1,28 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+
+public interface CMSObjectIdentifiers
+{
+ static final ASN1ObjectIdentifier data = PKCSObjectIdentifiers.data;
+ static final ASN1ObjectIdentifier signedData = PKCSObjectIdentifiers.signedData;
+ static final ASN1ObjectIdentifier envelopedData = PKCSObjectIdentifiers.envelopedData;
+ static final ASN1ObjectIdentifier signedAndEnvelopedData = PKCSObjectIdentifiers.signedAndEnvelopedData;
+ static final ASN1ObjectIdentifier digestedData = PKCSObjectIdentifiers.digestedData;
+ static final ASN1ObjectIdentifier encryptedData = PKCSObjectIdentifiers.encryptedData;
+ static final ASN1ObjectIdentifier authenticatedData = PKCSObjectIdentifiers.id_ct_authData;
+ static final ASN1ObjectIdentifier compressedData = PKCSObjectIdentifiers.id_ct_compressedData;
+ static final ASN1ObjectIdentifier authEnvelopedData = PKCSObjectIdentifiers.id_ct_authEnvelopedData;
+ static final ASN1ObjectIdentifier timestampedData = PKCSObjectIdentifiers.id_ct_timestampedData;
+
+ /**
+ * The other Revocation Info arc
+ * id-ri OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
+ * dod(6) internet(1) security(5) mechanisms(5) pkix(7) ri(16) }
+ */
+ static final ASN1ObjectIdentifier id_ri = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.16");
+
+ static final ASN1ObjectIdentifier id_ri_ocsp_response = id_ri.branch("2");
+ static final ASN1ObjectIdentifier id_ri_scvp = id_ri.branch("4");
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/CompressedData.java b/core/src/main/java/org/bouncycastle/asn1/cms/CompressedData.java
new file mode 100644
index 00000000..e9d9f679
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/CompressedData.java
@@ -0,0 +1,110 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.BERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * RFC 3274 - CMS Compressed Data.
+ * <pre>
+ * CompressedData ::= SEQUENCE {
+ * version CMSVersion,
+ * compressionAlgorithm CompressionAlgorithmIdentifier,
+ * encapContentInfo EncapsulatedContentInfo
+ * }
+ * </pre>
+ */
+public class CompressedData
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private AlgorithmIdentifier compressionAlgorithm;
+ private ContentInfo encapContentInfo;
+
+ public CompressedData(
+ AlgorithmIdentifier compressionAlgorithm,
+ ContentInfo encapContentInfo)
+ {
+ this.version = new ASN1Integer(0);
+ this.compressionAlgorithm = compressionAlgorithm;
+ this.encapContentInfo = encapContentInfo;
+ }
+
+ private CompressedData(
+ ASN1Sequence seq)
+ {
+ this.version = (ASN1Integer)seq.getObjectAt(0);
+ this.compressionAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
+ this.encapContentInfo = ContentInfo.getInstance(seq.getObjectAt(2));
+
+ }
+
+ /**
+ * return a CompressedData object from a tagged object.
+ *
+ * @param _ato 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 object held by the
+ * tagged object cannot be converted.
+ */
+ public static CompressedData getInstance(
+ ASN1TaggedObject _ato,
+ boolean _explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(_ato, _explicit));
+ }
+
+ /**
+ * return a CompressedData object from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static CompressedData getInstance(
+ Object obj)
+ {
+ if (obj instanceof CompressedData)
+ {
+ return (CompressedData)obj;
+ }
+
+ if (obj != null)
+ {
+ return new CompressedData(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public AlgorithmIdentifier getCompressionAlgorithmIdentifier()
+ {
+ return compressionAlgorithm;
+ }
+
+ public ContentInfo getEncapContentInfo()
+ {
+ return encapContentInfo;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+ v.add(compressionAlgorithm);
+ v.add(encapContentInfo);
+
+ return new BERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/CompressedDataParser.java b/core/src/main/java/org/bouncycastle/asn1/cms/CompressedDataParser.java
new file mode 100644
index 00000000..035e19d6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/CompressedDataParser.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.asn1.cms;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1SequenceParser;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * RFC 3274 - CMS Compressed Data.
+ * <pre>
+ * CompressedData ::= SEQUENCE {
+ * version CMSVersion,
+ * compressionAlgorithm CompressionAlgorithmIdentifier,
+ * encapContentInfo EncapsulatedContentInfo
+ * }
+ * </pre>
+ */
+public class CompressedDataParser
+{
+ private ASN1Integer _version;
+ private AlgorithmIdentifier _compressionAlgorithm;
+ private ContentInfoParser _encapContentInfo;
+
+ public CompressedDataParser(
+ ASN1SequenceParser seq)
+ throws IOException
+ {
+ this._version = (ASN1Integer)seq.readObject();
+ this._compressionAlgorithm = AlgorithmIdentifier.getInstance(seq.readObject().toASN1Primitive());
+ this._encapContentInfo = new ContentInfoParser((ASN1SequenceParser)seq.readObject());
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return _version;
+ }
+
+ public AlgorithmIdentifier getCompressionAlgorithmIdentifier()
+ {
+ return _compressionAlgorithm;
+ }
+
+ public ContentInfoParser getEncapContentInfo()
+ {
+ return _encapContentInfo;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java b/core/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java
new file mode 100644
index 00000000..345cf2ce
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java
@@ -0,0 +1,107 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.BERSequence;
+import org.bouncycastle.asn1.BERTaggedObject;
+
+public class ContentInfo
+ extends ASN1Object
+ implements CMSObjectIdentifiers
+{
+ private ASN1ObjectIdentifier contentType;
+ private ASN1Encodable content;
+
+ public static ContentInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof ContentInfo)
+ {
+ return (ContentInfo)obj;
+ }
+ else if (obj != null)
+ {
+ return new ContentInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static ContentInfo getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * @deprecated use getInstance()
+ */
+ public ContentInfo(
+ ASN1Sequence seq)
+ {
+ if (seq.size() < 1 || seq.size() > 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ contentType = (ASN1ObjectIdentifier)seq.getObjectAt(0);
+
+ if (seq.size() > 1)
+ {
+ ASN1TaggedObject tagged = (ASN1TaggedObject)seq.getObjectAt(1);
+ if (!tagged.isExplicit() || tagged.getTagNo() != 0)
+ {
+ throw new IllegalArgumentException("Bad tag for 'content'");
+ }
+
+ content = tagged.getObject();
+ }
+ }
+
+ public ContentInfo(
+ ASN1ObjectIdentifier contentType,
+ ASN1Encodable content)
+ {
+ this.contentType = contentType;
+ this.content = content;
+ }
+
+ public ASN1ObjectIdentifier getContentType()
+ {
+ return contentType;
+ }
+
+ public ASN1Encodable getContent()
+ {
+ return content;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * ContentInfo ::= SEQUENCE {
+ * contentType ContentType,
+ * content
+ * [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(contentType);
+
+ if (content != null)
+ {
+ v.add(new BERTaggedObject(0, content));
+ }
+
+ return new BERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/ContentInfoParser.java b/core/src/main/java/org/bouncycastle/asn1/cms/ContentInfoParser.java
new file mode 100644
index 00000000..bbc31764
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/ContentInfoParser.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.asn1.cms;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1SequenceParser;
+import org.bouncycastle.asn1.ASN1TaggedObjectParser;
+
+/**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * ContentInfo ::= SEQUENCE {
+ * contentType ContentType,
+ * content
+ * [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
+ * </pre>
+ */
+public class ContentInfoParser
+{
+ private ASN1ObjectIdentifier contentType;
+ private ASN1TaggedObjectParser content;
+
+ public ContentInfoParser(
+ ASN1SequenceParser seq)
+ throws IOException
+ {
+ contentType = (ASN1ObjectIdentifier)seq.readObject();
+ content = (ASN1TaggedObjectParser)seq.readObject();
+ }
+
+ public ASN1ObjectIdentifier getContentType()
+ {
+ return contentType;
+ }
+
+ public ASN1Encodable getContent(
+ int tag)
+ throws IOException
+ {
+ if (content != null)
+ {
+ return content.getObjectParser(tag, true);
+ }
+
+ return null;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/DigestedData.java b/core/src/main/java/org/bouncycastle/asn1/cms/DigestedData.java
new file mode 100644
index 00000000..32b7e404
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/DigestedData.java
@@ -0,0 +1,121 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.BERSequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * RFC 3274 - CMS Digest Data.
+ * <pre>
+ * DigestedData ::= SEQUENCE {
+ * version CMSVersion,
+ * digestAlgorithm DigestAlgorithmIdentifier,
+ * encapContentInfo EncapsulatedContentInfo,
+ * digest Digest }
+ * </pre>
+ */
+public class DigestedData
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private AlgorithmIdentifier digestAlgorithm;
+ private ContentInfo encapContentInfo;
+ private ASN1OctetString digest;
+
+ public DigestedData(
+ AlgorithmIdentifier digestAlgorithm,
+ ContentInfo encapContentInfo,
+ byte[] digest)
+ {
+ this.version = new ASN1Integer(0);
+ this.digestAlgorithm = digestAlgorithm;
+ this.encapContentInfo = encapContentInfo;
+ this.digest = new DEROctetString(digest);
+ }
+
+ private DigestedData(
+ ASN1Sequence seq)
+ {
+ this.version = (ASN1Integer)seq.getObjectAt(0);
+ this.digestAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
+ this.encapContentInfo = ContentInfo.getInstance(seq.getObjectAt(2));
+ this.digest = ASN1OctetString.getInstance(seq.getObjectAt(3));
+ }
+
+ /**
+ * return a CompressedData object from a tagged object.
+ *
+ * @param _ato 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 object held by the
+ * tagged object cannot be converted.
+ */
+ public static DigestedData getInstance(
+ ASN1TaggedObject _ato,
+ boolean _explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(_ato, _explicit));
+ }
+
+ /**
+ * return a CompressedData object from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DigestedData getInstance(
+ Object obj)
+ {
+ if (obj instanceof DigestedData)
+ {
+ return (DigestedData)obj;
+ }
+
+ if (obj != null)
+ {
+ return new DigestedData(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public AlgorithmIdentifier getDigestAlgorithm()
+ {
+ return digestAlgorithm;
+ }
+
+ public ContentInfo getEncapContentInfo()
+ {
+ return encapContentInfo;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+ v.add(digestAlgorithm);
+ v.add(encapContentInfo);
+ v.add(digest);
+
+ return new BERSequence(v);
+ }
+
+ public byte[] getDigest()
+ {
+ return digest.getOctets();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfo.java b/core/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfo.java
new file mode 100644
index 00000000..14265e5f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfo.java
@@ -0,0 +1,109 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.BERSequence;
+import org.bouncycastle.asn1.BERTaggedObject;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class EncryptedContentInfo
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier contentType;
+ private AlgorithmIdentifier contentEncryptionAlgorithm;
+ private ASN1OctetString encryptedContent;
+
+ public EncryptedContentInfo(
+ ASN1ObjectIdentifier contentType,
+ AlgorithmIdentifier contentEncryptionAlgorithm,
+ ASN1OctetString encryptedContent)
+ {
+ this.contentType = contentType;
+ this.contentEncryptionAlgorithm = contentEncryptionAlgorithm;
+ this.encryptedContent = encryptedContent;
+ }
+
+ private EncryptedContentInfo(
+ ASN1Sequence seq)
+ {
+ if (seq.size() < 2)
+ {
+ throw new IllegalArgumentException("Truncated Sequence Found");
+ }
+
+ contentType = (ASN1ObjectIdentifier)seq.getObjectAt(0);
+ contentEncryptionAlgorithm = AlgorithmIdentifier.getInstance(
+ seq.getObjectAt(1));
+ if (seq.size() > 2)
+ {
+ encryptedContent = ASN1OctetString.getInstance(
+ (ASN1TaggedObject)seq.getObjectAt(2), false);
+ }
+ }
+
+ /**
+ * return an EncryptedContentInfo object from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static EncryptedContentInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof EncryptedContentInfo)
+ {
+ return (EncryptedContentInfo)obj;
+ }
+ if (obj != null)
+ {
+ return new EncryptedContentInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public ASN1ObjectIdentifier getContentType()
+ {
+ return contentType;
+ }
+
+ public AlgorithmIdentifier getContentEncryptionAlgorithm()
+ {
+ return contentEncryptionAlgorithm;
+ }
+
+ public ASN1OctetString getEncryptedContent()
+ {
+ return encryptedContent;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * EncryptedContentInfo ::= SEQUENCE {
+ * contentType ContentType,
+ * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
+ * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(contentType);
+ v.add(contentEncryptionAlgorithm);
+
+ if (encryptedContent != null)
+ {
+ v.add(new BERTaggedObject(false, 0, encryptedContent));
+ }
+
+ return new BERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfoParser.java b/core/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfoParser.java
new file mode 100644
index 00000000..1e6f0405
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfoParser.java
@@ -0,0 +1,51 @@
+package org.bouncycastle.asn1.cms;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1SequenceParser;
+import org.bouncycastle.asn1.ASN1TaggedObjectParser;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * <pre>
+ * EncryptedContentInfo ::= SEQUENCE {
+ * contentType ContentType,
+ * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
+ * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
+ * }
+ * </pre>
+ */
+public class EncryptedContentInfoParser
+{
+ private ASN1ObjectIdentifier _contentType;
+ private AlgorithmIdentifier _contentEncryptionAlgorithm;
+ private ASN1TaggedObjectParser _encryptedContent;
+
+ public EncryptedContentInfoParser(
+ ASN1SequenceParser seq)
+ throws IOException
+ {
+ _contentType = (ASN1ObjectIdentifier)seq.readObject();
+ _contentEncryptionAlgorithm = AlgorithmIdentifier.getInstance(seq.readObject().toASN1Primitive());
+ _encryptedContent = (ASN1TaggedObjectParser)seq.readObject();
+ }
+
+ public ASN1ObjectIdentifier getContentType()
+ {
+ return _contentType;
+ }
+
+ public AlgorithmIdentifier getContentEncryptionAlgorithm()
+ {
+ return _contentEncryptionAlgorithm;
+ }
+
+ public ASN1Encodable getEncryptedContent(
+ int tag)
+ throws IOException
+ {
+ return _encryptedContent.getObjectParser(tag, false);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/EncryptedData.java b/core/src/main/java/org/bouncycastle/asn1/cms/EncryptedData.java
new file mode 100644
index 00000000..9d61b333
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/EncryptedData.java
@@ -0,0 +1,94 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.BERSequence;
+import org.bouncycastle.asn1.BERTaggedObject;
+
+public class EncryptedData
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private EncryptedContentInfo encryptedContentInfo;
+ private ASN1Set unprotectedAttrs;
+
+ public static EncryptedData getInstance(Object o)
+ {
+ if (o instanceof EncryptedData)
+ {
+ return (EncryptedData)o;
+ }
+
+ if (o != null)
+ {
+ return new EncryptedData(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public EncryptedData(EncryptedContentInfo encInfo)
+ {
+ this(encInfo, null);
+ }
+
+ public EncryptedData(EncryptedContentInfo encInfo, ASN1Set unprotectedAttrs)
+ {
+ this.version = new ASN1Integer((unprotectedAttrs == null) ? 0 : 2);
+ this.encryptedContentInfo = encInfo;
+ this.unprotectedAttrs = unprotectedAttrs;
+ }
+
+ private EncryptedData(ASN1Sequence seq)
+ {
+ this.version = ASN1Integer.getInstance(seq.getObjectAt(0));
+ this.encryptedContentInfo = EncryptedContentInfo.getInstance(seq.getObjectAt(1));
+
+ if (seq.size() == 3)
+ {
+ this.unprotectedAttrs = ASN1Set.getInstance(seq.getObjectAt(2));
+ }
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public EncryptedContentInfo getEncryptedContentInfo()
+ {
+ return encryptedContentInfo;
+ }
+
+ public ASN1Set getUnprotectedAttrs()
+ {
+ return unprotectedAttrs;
+ }
+
+ /**
+ * <pre>
+ * EncryptedData ::= SEQUENCE {
+ * version CMSVersion,
+ * encryptedContentInfo EncryptedContentInfo,
+ * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+ v.add(encryptedContentInfo);
+ if (unprotectedAttrs != null)
+ {
+ v.add(new BERTaggedObject(false, 1, unprotectedAttrs));
+ }
+
+ return new BERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/EnvelopedData.java b/core/src/main/java/org/bouncycastle/asn1/cms/EnvelopedData.java
new file mode 100644
index 00000000..6d8b484d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/EnvelopedData.java
@@ -0,0 +1,205 @@
+package org.bouncycastle.asn1.cms;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.BERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class EnvelopedData
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private OriginatorInfo originatorInfo;
+ private ASN1Set recipientInfos;
+ private EncryptedContentInfo encryptedContentInfo;
+ private ASN1Set unprotectedAttrs;
+
+ public EnvelopedData(
+ OriginatorInfo originatorInfo,
+ ASN1Set recipientInfos,
+ EncryptedContentInfo encryptedContentInfo,
+ ASN1Set unprotectedAttrs)
+ {
+ version = new ASN1Integer(calculateVersion(originatorInfo, recipientInfos, unprotectedAttrs));
+
+ this.originatorInfo = originatorInfo;
+ this.recipientInfos = recipientInfos;
+ this.encryptedContentInfo = encryptedContentInfo;
+ this.unprotectedAttrs = unprotectedAttrs;
+ }
+
+ public EnvelopedData(
+ OriginatorInfo originatorInfo,
+ ASN1Set recipientInfos,
+ EncryptedContentInfo encryptedContentInfo,
+ Attributes unprotectedAttrs)
+ {
+ version = new ASN1Integer(calculateVersion(originatorInfo, recipientInfos, ASN1Set.getInstance(unprotectedAttrs)));
+
+ this.originatorInfo = originatorInfo;
+ this.recipientInfos = recipientInfos;
+ this.encryptedContentInfo = encryptedContentInfo;
+ this.unprotectedAttrs = ASN1Set.getInstance(unprotectedAttrs);
+ }
+
+ /**
+ * @deprecated use getInstance()
+ */
+ public EnvelopedData(
+ ASN1Sequence seq)
+ {
+ int index = 0;
+
+ version = (ASN1Integer)seq.getObjectAt(index++);
+
+ Object tmp = seq.getObjectAt(index++);
+
+ if (tmp instanceof ASN1TaggedObject)
+ {
+ originatorInfo = OriginatorInfo.getInstance((ASN1TaggedObject)tmp, false);
+ tmp = seq.getObjectAt(index++);
+ }
+
+ recipientInfos = ASN1Set.getInstance(tmp);
+
+ encryptedContentInfo = EncryptedContentInfo.getInstance(seq.getObjectAt(index++));
+
+ if(seq.size() > index)
+ {
+ unprotectedAttrs = ASN1Set.getInstance((ASN1TaggedObject)seq.getObjectAt(index), false);
+ }
+ }
+
+ /**
+ * return an EnvelopedData 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 object held by the
+ * tagged object cannot be converted.
+ */
+ public static EnvelopedData getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * return an EnvelopedData object from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static EnvelopedData getInstance(
+ Object obj)
+ {
+ if (obj instanceof EnvelopedData)
+ {
+ return (EnvelopedData)obj;
+ }
+
+ if (obj != null)
+ {
+ return new EnvelopedData(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public OriginatorInfo getOriginatorInfo()
+ {
+ return originatorInfo;
+ }
+
+ public ASN1Set getRecipientInfos()
+ {
+ return recipientInfos;
+ }
+
+ public EncryptedContentInfo getEncryptedContentInfo()
+ {
+ return encryptedContentInfo;
+ }
+
+ public ASN1Set getUnprotectedAttrs()
+ {
+ return unprotectedAttrs;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * EnvelopedData ::= SEQUENCE {
+ * version CMSVersion,
+ * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+ * recipientInfos RecipientInfos,
+ * encryptedContentInfo EncryptedContentInfo,
+ * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+
+ if (originatorInfo != null)
+ {
+ v.add(new DERTaggedObject(false, 0, originatorInfo));
+ }
+
+ v.add(recipientInfos);
+ v.add(encryptedContentInfo);
+
+ if (unprotectedAttrs != null)
+ {
+ v.add(new DERTaggedObject(false, 1, unprotectedAttrs));
+ }
+
+ return new BERSequence(v);
+ }
+
+ public static int calculateVersion(OriginatorInfo originatorInfo, ASN1Set recipientInfos, ASN1Set unprotectedAttrs)
+ {
+ int version;
+
+ if (originatorInfo != null || unprotectedAttrs != null)
+ {
+ version = 2;
+ }
+ else
+ {
+ version = 0;
+
+ Enumeration e = recipientInfos.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ RecipientInfo ri = RecipientInfo.getInstance(e.nextElement());
+
+ if (ri.getVersion().getValue().intValue() != version)
+ {
+ version = 2;
+ break;
+ }
+ }
+ }
+
+ return version;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/EnvelopedDataParser.java b/core/src/main/java/org/bouncycastle/asn1/cms/EnvelopedDataParser.java
new file mode 100644
index 00000000..73529fd8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/EnvelopedDataParser.java
@@ -0,0 +1,118 @@
+package org.bouncycastle.asn1.cms;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1SequenceParser;
+import org.bouncycastle.asn1.ASN1SetParser;
+import org.bouncycastle.asn1.ASN1TaggedObjectParser;
+import org.bouncycastle.asn1.BERTags;
+
+/**
+ * <pre>
+ * EnvelopedData ::= SEQUENCE {
+ * version CMSVersion,
+ * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+ * recipientInfos RecipientInfos,
+ * encryptedContentInfo EncryptedContentInfo,
+ * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL
+ * }
+ * </pre>
+ */
+public class EnvelopedDataParser
+{
+ private ASN1SequenceParser _seq;
+ private ASN1Integer _version;
+ private ASN1Encodable _nextObject;
+ private boolean _originatorInfoCalled;
+
+ public EnvelopedDataParser(
+ ASN1SequenceParser seq)
+ throws IOException
+ {
+ this._seq = seq;
+ this._version = ASN1Integer.getInstance(seq.readObject());
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return _version;
+ }
+
+ public OriginatorInfo getOriginatorInfo()
+ throws IOException
+ {
+ _originatorInfoCalled = true;
+
+ if (_nextObject == null)
+ {
+ _nextObject = _seq.readObject();
+ }
+
+ if (_nextObject instanceof ASN1TaggedObjectParser && ((ASN1TaggedObjectParser)_nextObject).getTagNo() == 0)
+ {
+ ASN1SequenceParser originatorInfo = (ASN1SequenceParser) ((ASN1TaggedObjectParser)_nextObject).getObjectParser(BERTags.SEQUENCE, false);
+ _nextObject = null;
+ return OriginatorInfo.getInstance(originatorInfo.toASN1Primitive());
+ }
+
+ return null;
+ }
+
+ public ASN1SetParser getRecipientInfos()
+ throws IOException
+ {
+ if (!_originatorInfoCalled)
+ {
+ getOriginatorInfo();
+ }
+
+ if (_nextObject == null)
+ {
+ _nextObject = _seq.readObject();
+ }
+
+ ASN1SetParser recipientInfos = (ASN1SetParser)_nextObject;
+ _nextObject = null;
+ return recipientInfos;
+ }
+
+ public EncryptedContentInfoParser getEncryptedContentInfo()
+ throws IOException
+ {
+ if (_nextObject == null)
+ {
+ _nextObject = _seq.readObject();
+ }
+
+
+ if (_nextObject != null)
+ {
+ ASN1SequenceParser o = (ASN1SequenceParser) _nextObject;
+ _nextObject = null;
+ return new EncryptedContentInfoParser(o);
+ }
+
+ return null;
+ }
+
+ public ASN1SetParser getUnprotectedAttrs()
+ throws IOException
+ {
+ if (_nextObject == null)
+ {
+ _nextObject = _seq.readObject();
+ }
+
+
+ if (_nextObject != null)
+ {
+ ASN1Encodable o = _nextObject;
+ _nextObject = null;
+ return (ASN1SetParser)((ASN1TaggedObjectParser)o).getObjectParser(BERTags.SET, false);
+ }
+
+ return null;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/Evidence.java b/core/src/main/java/org/bouncycastle/asn1/cms/Evidence.java
new file mode 100644
index 00000000..c68ec9a4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/Evidence.java
@@ -0,0 +1,56 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class Evidence
+ extends ASN1Object
+ implements ASN1Choice
+{
+ private TimeStampTokenEvidence tstEvidence;
+
+ public Evidence(TimeStampTokenEvidence tstEvidence)
+ {
+ this.tstEvidence = tstEvidence;
+ }
+
+ private Evidence(ASN1TaggedObject tagged)
+ {
+ if (tagged.getTagNo() == 0)
+ {
+ this.tstEvidence = TimeStampTokenEvidence.getInstance(tagged, false);
+ }
+ }
+
+ public static Evidence getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof Evidence)
+ {
+ return (Evidence)obj;
+ }
+ else if (obj instanceof ASN1TaggedObject)
+ {
+ return new Evidence(ASN1TaggedObject.getInstance(obj));
+ }
+
+ throw new IllegalArgumentException("unknown object in getInstance");
+ }
+
+ public TimeStampTokenEvidence getTstEvidence()
+ {
+ return tstEvidence;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ if (tstEvidence != null)
+ {
+ return new DERTaggedObject(false, 0, tstEvidence);
+ }
+
+ return null;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/IssuerAndSerialNumber.java b/core/src/main/java/org/bouncycastle/asn1/cms/IssuerAndSerialNumber.java
new file mode 100644
index 00000000..ad0dbb19
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/IssuerAndSerialNumber.java
@@ -0,0 +1,111 @@
+package org.bouncycastle.asn1.cms;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.asn1.x509.X509CertificateStructure;
+import org.bouncycastle.asn1.x509.X509Name;
+
+public class IssuerAndSerialNumber
+ extends ASN1Object
+{
+ private X500Name name;
+ private ASN1Integer serialNumber;
+
+ public static IssuerAndSerialNumber getInstance(
+ Object obj)
+ {
+ if (obj instanceof IssuerAndSerialNumber)
+ {
+ return (IssuerAndSerialNumber)obj;
+ }
+ else if (obj != null)
+ {
+ return new IssuerAndSerialNumber(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * @deprecated use getInstance() method.
+ * @param seq
+ */
+ public IssuerAndSerialNumber(
+ ASN1Sequence seq)
+ {
+ this.name = X500Name.getInstance(seq.getObjectAt(0));
+ this.serialNumber = (ASN1Integer)seq.getObjectAt(1);
+ }
+
+ public IssuerAndSerialNumber(
+ Certificate certificate)
+ {
+ this.name = certificate.getIssuer();
+ this.serialNumber = certificate.getSerialNumber();
+ }
+
+ public IssuerAndSerialNumber(
+ X509CertificateStructure certificate)
+ {
+ this.name = certificate.getIssuer();
+ this.serialNumber = certificate.getSerialNumber();
+ }
+
+ public IssuerAndSerialNumber(
+ X500Name name,
+ BigInteger serialNumber)
+ {
+ this.name = name;
+ this.serialNumber = new ASN1Integer(serialNumber);
+ }
+
+ /**
+ * @deprecated use X500Name constructor
+ */
+ public IssuerAndSerialNumber(
+ X509Name name,
+ BigInteger serialNumber)
+ {
+ this.name = X500Name.getInstance(name);
+ this.serialNumber = new ASN1Integer(serialNumber);
+ }
+
+ /**
+ * @deprecated use X500Name constructor
+ */
+ public IssuerAndSerialNumber(
+ X509Name name,
+ ASN1Integer serialNumber)
+ {
+ this.name = X500Name.getInstance(name);
+ this.serialNumber = serialNumber;
+ }
+
+ public X500Name getName()
+ {
+ return name;
+ }
+
+ public ASN1Integer getSerialNumber()
+ {
+ return serialNumber;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(name);
+ v.add(serialNumber);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/KEKIdentifier.java b/core/src/main/java/org/bouncycastle/asn1/cms/KEKIdentifier.java
new file mode 100644
index 00000000..67c68abe
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/KEKIdentifier.java
@@ -0,0 +1,139 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+
+public class KEKIdentifier
+ extends ASN1Object
+{
+ private ASN1OctetString keyIdentifier;
+ private ASN1GeneralizedTime date;
+ private OtherKeyAttribute other;
+
+ public KEKIdentifier(
+ byte[] keyIdentifier,
+ ASN1GeneralizedTime date,
+ OtherKeyAttribute other)
+ {
+ this.keyIdentifier = new DEROctetString(keyIdentifier);
+ this.date = date;
+ this.other = other;
+ }
+
+ private KEKIdentifier(
+ ASN1Sequence seq)
+ {
+ keyIdentifier = (ASN1OctetString)seq.getObjectAt(0);
+
+ switch (seq.size())
+ {
+ case 1:
+ break;
+ case 2:
+ if (seq.getObjectAt(1) instanceof ASN1GeneralizedTime)
+ {
+ date = (ASN1GeneralizedTime)seq.getObjectAt(1);
+ }
+ else
+ {
+ other = OtherKeyAttribute.getInstance(seq.getObjectAt(1));
+ }
+ break;
+ case 3:
+ date = (ASN1GeneralizedTime)seq.getObjectAt(1);
+ other = OtherKeyAttribute.getInstance(seq.getObjectAt(2));
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid KEKIdentifier");
+ }
+ }
+
+ /**
+ * return a KEKIdentifier 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 object held by the
+ * tagged object cannot be converted.
+ */
+ public static KEKIdentifier getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * return a KEKIdentifier object from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static KEKIdentifier getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof KEKIdentifier)
+ {
+ return (KEKIdentifier)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new KEKIdentifier((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid KEKIdentifier: " + obj.getClass().getName());
+ }
+
+ public ASN1OctetString getKeyIdentifier()
+ {
+ return keyIdentifier;
+ }
+
+ public ASN1GeneralizedTime getDate()
+ {
+ return date;
+ }
+
+ public OtherKeyAttribute getOther()
+ {
+ return other;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * KEKIdentifier ::= SEQUENCE {
+ * keyIdentifier OCTET STRING,
+ * date GeneralizedTime OPTIONAL,
+ * other OtherKeyAttribute OPTIONAL
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(keyIdentifier);
+
+ if (date != null)
+ {
+ v.add(date);
+ }
+
+ if (other != null)
+ {
+ v.add(other);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/KEKRecipientInfo.java b/core/src/main/java/org/bouncycastle/asn1/cms/KEKRecipientInfo.java
new file mode 100644
index 00000000..6c67772c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/KEKRecipientInfo.java
@@ -0,0 +1,121 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class KEKRecipientInfo
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private KEKIdentifier kekid;
+ private AlgorithmIdentifier keyEncryptionAlgorithm;
+ private ASN1OctetString encryptedKey;
+
+ public KEKRecipientInfo(
+ KEKIdentifier kekid,
+ AlgorithmIdentifier keyEncryptionAlgorithm,
+ ASN1OctetString encryptedKey)
+ {
+ this.version = new ASN1Integer(4);
+ this.kekid = kekid;
+ this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
+ this.encryptedKey = encryptedKey;
+ }
+
+ public KEKRecipientInfo(
+ ASN1Sequence seq)
+ {
+ version = (ASN1Integer)seq.getObjectAt(0);
+ kekid = KEKIdentifier.getInstance(seq.getObjectAt(1));
+ keyEncryptionAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(2));
+ encryptedKey = (ASN1OctetString)seq.getObjectAt(3);
+ }
+
+ /**
+ * return a KEKRecipientInfo 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 object held by the
+ * tagged object cannot be converted.
+ */
+ public static KEKRecipientInfo getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * return a KEKRecipientInfo object from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static KEKRecipientInfo getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof KEKRecipientInfo)
+ {
+ return (KEKRecipientInfo)obj;
+ }
+
+ if(obj instanceof ASN1Sequence)
+ {
+ return new KEKRecipientInfo((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid KEKRecipientInfo: " + obj.getClass().getName());
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public KEKIdentifier getKekid()
+ {
+ return kekid;
+ }
+
+ public AlgorithmIdentifier getKeyEncryptionAlgorithm()
+ {
+ return keyEncryptionAlgorithm;
+ }
+
+ public ASN1OctetString getEncryptedKey()
+ {
+ return encryptedKey;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * KEKRecipientInfo ::= SEQUENCE {
+ * version CMSVersion, -- always set to 4
+ * kekid KEKIdentifier,
+ * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
+ * encryptedKey EncryptedKey
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+ v.add(kekid);
+ v.add(keyEncryptionAlgorithm);
+ v.add(encryptedKey);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientIdentifier.java b/core/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientIdentifier.java
new file mode 100644
index 00000000..29f455a2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientIdentifier.java
@@ -0,0 +1,103 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class KeyAgreeRecipientIdentifier
+ extends ASN1Object
+ implements ASN1Choice
+{
+ private IssuerAndSerialNumber issuerSerial;
+ private RecipientKeyIdentifier rKeyID;
+
+ /**
+ * return an KeyAgreeRecipientIdentifier 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 object held by the
+ * tagged object cannot be converted.
+ */
+ public static KeyAgreeRecipientIdentifier getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * return an KeyAgreeRecipientIdentifier object from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static KeyAgreeRecipientIdentifier getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof KeyAgreeRecipientIdentifier)
+ {
+ return (KeyAgreeRecipientIdentifier)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new KeyAgreeRecipientIdentifier(IssuerAndSerialNumber.getInstance(obj));
+ }
+
+ if (obj instanceof ASN1TaggedObject && ((ASN1TaggedObject)obj).getTagNo() == 0)
+ {
+ return new KeyAgreeRecipientIdentifier(RecipientKeyIdentifier.getInstance(
+ (ASN1TaggedObject)obj, false));
+ }
+
+ throw new IllegalArgumentException("Invalid KeyAgreeRecipientIdentifier: " + obj.getClass().getName());
+ }
+
+ public KeyAgreeRecipientIdentifier(
+ IssuerAndSerialNumber issuerSerial)
+ {
+ this.issuerSerial = issuerSerial;
+ this.rKeyID = null;
+ }
+
+ public KeyAgreeRecipientIdentifier(
+ RecipientKeyIdentifier rKeyID)
+ {
+ this.issuerSerial = null;
+ this.rKeyID = rKeyID;
+ }
+
+ public IssuerAndSerialNumber getIssuerAndSerialNumber()
+ {
+ return issuerSerial;
+ }
+
+ public RecipientKeyIdentifier getRKeyID()
+ {
+ return rKeyID;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * KeyAgreeRecipientIdentifier ::= CHOICE {
+ * issuerAndSerialNumber IssuerAndSerialNumber,
+ * rKeyId [0] IMPLICIT RecipientKeyIdentifier
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ if (issuerSerial != null)
+ {
+ return issuerSerial.toASN1Primitive();
+ }
+
+ return new DERTaggedObject(false, 0, rKeyID);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientInfo.java b/core/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientInfo.java
new file mode 100644
index 00000000..c6e57447
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientInfo.java
@@ -0,0 +1,153 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class KeyAgreeRecipientInfo
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private OriginatorIdentifierOrKey originator;
+ private ASN1OctetString ukm;
+ private AlgorithmIdentifier keyEncryptionAlgorithm;
+ private ASN1Sequence recipientEncryptedKeys;
+
+ public KeyAgreeRecipientInfo(
+ OriginatorIdentifierOrKey originator,
+ ASN1OctetString ukm,
+ AlgorithmIdentifier keyEncryptionAlgorithm,
+ ASN1Sequence recipientEncryptedKeys)
+ {
+ this.version = new ASN1Integer(3);
+ this.originator = originator;
+ this.ukm = ukm;
+ this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
+ this.recipientEncryptedKeys = recipientEncryptedKeys;
+ }
+
+ public KeyAgreeRecipientInfo(
+ ASN1Sequence seq)
+ {
+ int index = 0;
+
+ version = (ASN1Integer)seq.getObjectAt(index++);
+ originator = OriginatorIdentifierOrKey.getInstance(
+ (ASN1TaggedObject)seq.getObjectAt(index++), true);
+
+ if (seq.getObjectAt(index) instanceof ASN1TaggedObject)
+ {
+ ukm = ASN1OctetString.getInstance(
+ (ASN1TaggedObject)seq.getObjectAt(index++), true);
+ }
+
+ keyEncryptionAlgorithm = AlgorithmIdentifier.getInstance(
+ seq.getObjectAt(index++));
+
+ recipientEncryptedKeys = (ASN1Sequence)seq.getObjectAt(index++);
+ }
+
+ /**
+ * return a KeyAgreeRecipientInfo 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 object held by the
+ * tagged object cannot be converted.
+ */
+ public static KeyAgreeRecipientInfo getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * return a KeyAgreeRecipientInfo object from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static KeyAgreeRecipientInfo getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof KeyAgreeRecipientInfo)
+ {
+ return (KeyAgreeRecipientInfo)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new KeyAgreeRecipientInfo((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException(
+ "Illegal object in KeyAgreeRecipientInfo: " + obj.getClass().getName());
+
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public OriginatorIdentifierOrKey getOriginator()
+ {
+ return originator;
+ }
+
+ public ASN1OctetString getUserKeyingMaterial()
+ {
+ return ukm;
+ }
+
+ public AlgorithmIdentifier getKeyEncryptionAlgorithm()
+ {
+ return keyEncryptionAlgorithm;
+ }
+
+ public ASN1Sequence getRecipientEncryptedKeys()
+ {
+ return recipientEncryptedKeys;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * KeyAgreeRecipientInfo ::= SEQUENCE {
+ * version CMSVersion, -- always set to 3
+ * originator [0] EXPLICIT OriginatorIdentifierOrKey,
+ * ukm [1] EXPLICIT UserKeyingMaterial OPTIONAL,
+ * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
+ * recipientEncryptedKeys RecipientEncryptedKeys
+ * }
+ *
+ * UserKeyingMaterial ::= OCTET STRING
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+ v.add(new DERTaggedObject(true, 0, originator));
+
+ if (ukm != null)
+ {
+ v.add(new DERTaggedObject(true, 1, ukm));
+ }
+
+ v.add(keyEncryptionAlgorithm);
+ v.add(recipientEncryptedKeys);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/KeyTransRecipientInfo.java b/core/src/main/java/org/bouncycastle/asn1/cms/KeyTransRecipientInfo.java
new file mode 100644
index 00000000..8b0a545e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/KeyTransRecipientInfo.java
@@ -0,0 +1,114 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class KeyTransRecipientInfo
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private RecipientIdentifier rid;
+ private AlgorithmIdentifier keyEncryptionAlgorithm;
+ private ASN1OctetString encryptedKey;
+
+ public KeyTransRecipientInfo(
+ RecipientIdentifier rid,
+ AlgorithmIdentifier keyEncryptionAlgorithm,
+ ASN1OctetString encryptedKey)
+ {
+ if (rid.toASN1Primitive() instanceof ASN1TaggedObject)
+ {
+ this.version = new ASN1Integer(2);
+ }
+ else
+ {
+ this.version = new ASN1Integer(0);
+ }
+
+ this.rid = rid;
+ this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
+ this.encryptedKey = encryptedKey;
+ }
+
+ public KeyTransRecipientInfo(
+ ASN1Sequence seq)
+ {
+ this.version = (ASN1Integer)seq.getObjectAt(0);
+ this.rid = RecipientIdentifier.getInstance(seq.getObjectAt(1));
+ this.keyEncryptionAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(2));
+ this.encryptedKey = (ASN1OctetString)seq.getObjectAt(3);
+ }
+
+ /**
+ * return a KeyTransRecipientInfo object from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static KeyTransRecipientInfo getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof KeyTransRecipientInfo)
+ {
+ return (KeyTransRecipientInfo)obj;
+ }
+
+ if(obj instanceof ASN1Sequence)
+ {
+ return new KeyTransRecipientInfo((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException(
+ "Illegal object in KeyTransRecipientInfo: " + obj.getClass().getName());
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public RecipientIdentifier getRecipientIdentifier()
+ {
+ return rid;
+ }
+
+ public AlgorithmIdentifier getKeyEncryptionAlgorithm()
+ {
+ return keyEncryptionAlgorithm;
+ }
+
+ public ASN1OctetString getEncryptedKey()
+ {
+ return encryptedKey;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * KeyTransRecipientInfo ::= SEQUENCE {
+ * version CMSVersion, -- always set to 0 or 2
+ * rid RecipientIdentifier,
+ * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
+ * encryptedKey EncryptedKey
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+ v.add(rid);
+ v.add(keyEncryptionAlgorithm);
+ v.add(encryptedKey);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/MetaData.java b/core/src/main/java/org/bouncycastle/asn1/cms/MetaData.java
new file mode 100644
index 00000000..73db22e5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/MetaData.java
@@ -0,0 +1,120 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1Boolean;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERUTF8String;
+
+public class MetaData
+ extends ASN1Object
+{
+ private ASN1Boolean hashProtected;
+ private DERUTF8String fileName;
+ private DERIA5String mediaType;
+ private Attributes otherMetaData;
+
+ public MetaData(
+ ASN1Boolean hashProtected,
+ DERUTF8String fileName,
+ DERIA5String mediaType,
+ Attributes otherMetaData)
+ {
+ this.hashProtected = hashProtected;
+ this.fileName = fileName;
+ this.mediaType = mediaType;
+ this.otherMetaData = otherMetaData;
+ }
+
+ private MetaData(ASN1Sequence seq)
+ {
+ this.hashProtected = ASN1Boolean.getInstance(seq.getObjectAt(0));
+
+ int index = 1;
+
+ if (index < seq.size() && seq.getObjectAt(index) instanceof DERUTF8String)
+ {
+ this.fileName = DERUTF8String.getInstance(seq.getObjectAt(index++));
+ }
+ if (index < seq.size() && seq.getObjectAt(index) instanceof DERIA5String)
+ {
+ this.mediaType = DERIA5String.getInstance(seq.getObjectAt(index++));
+ }
+ if (index < seq.size())
+ {
+ this.otherMetaData = Attributes.getInstance(seq.getObjectAt(index++));
+ }
+ }
+
+ public static MetaData getInstance(Object obj)
+ {
+ if (obj instanceof MetaData)
+ {
+ return (MetaData)obj;
+ }
+ else if (obj != null)
+ {
+ return new MetaData(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * <pre>
+ * MetaData ::= SEQUENCE {
+ * hashProtected BOOLEAN,
+ * fileName UTF8String OPTIONAL,
+ * mediaType IA5String OPTIONAL,
+ * otherMetaData Attributes OPTIONAL
+ * }
+ * </pre>
+ * @return
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(hashProtected);
+
+ if (fileName != null)
+ {
+ v.add(fileName);
+ }
+
+ if (mediaType != null)
+ {
+ v.add(mediaType);
+ }
+
+ if (otherMetaData != null)
+ {
+ v.add(otherMetaData);
+ }
+
+ return new DERSequence(v);
+ }
+
+ public boolean isHashProtected()
+ {
+ return hashProtected.isTrue();
+ }
+
+ public DERUTF8String getFileName()
+ {
+ return this.fileName;
+ }
+
+ public DERIA5String getMediaType()
+ {
+ return this.mediaType;
+ }
+
+ public Attributes getOtherMetaData()
+ {
+ return otherMetaData;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/OriginatorIdentifierOrKey.java b/core/src/main/java/org/bouncycastle/asn1/cms/OriginatorIdentifierOrKey.java
new file mode 100644
index 00000000..c7c3ecbb
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/OriginatorIdentifierOrKey.java
@@ -0,0 +1,165 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
+
+public class OriginatorIdentifierOrKey
+ extends ASN1Object
+ implements ASN1Choice
+{
+ private ASN1Encodable id;
+
+ public OriginatorIdentifierOrKey(
+ IssuerAndSerialNumber id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * @deprecated use version taking a SubjectKeyIdentifier
+ */
+ public OriginatorIdentifierOrKey(
+ ASN1OctetString id)
+ {
+ this(new SubjectKeyIdentifier(id.getOctets()));
+ }
+
+ public OriginatorIdentifierOrKey(
+ SubjectKeyIdentifier id)
+ {
+ this.id = new DERTaggedObject(false, 0, id);
+ }
+
+ public OriginatorIdentifierOrKey(
+ OriginatorPublicKey id)
+ {
+ this.id = new DERTaggedObject(false, 1, id);
+ }
+
+ /**
+ * @deprecated use more specific version
+ */
+ public OriginatorIdentifierOrKey(
+ ASN1Primitive id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * return an OriginatorIdentifierOrKey object from a tagged object.
+ *
+ * @param o 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 object held by the
+ * tagged object cannot be converted.
+ */
+ public static OriginatorIdentifierOrKey getInstance(
+ ASN1TaggedObject o,
+ boolean explicit)
+ {
+ if (!explicit)
+ {
+ throw new IllegalArgumentException(
+ "Can't implicitly tag OriginatorIdentifierOrKey");
+ }
+
+ return getInstance(o.getObject());
+ }
+
+ /**
+ * return an OriginatorIdentifierOrKey object from the given object.
+ *
+ * @param o the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static OriginatorIdentifierOrKey getInstance(
+ Object o)
+ {
+ if (o == null || o instanceof OriginatorIdentifierOrKey)
+ {
+ return (OriginatorIdentifierOrKey)o;
+ }
+
+ if (o instanceof IssuerAndSerialNumber)
+ {
+ return new OriginatorIdentifierOrKey((IssuerAndSerialNumber)o);
+ }
+
+ if (o instanceof SubjectKeyIdentifier)
+ {
+ return new OriginatorIdentifierOrKey((SubjectKeyIdentifier)o);
+ }
+
+ if (o instanceof OriginatorPublicKey)
+ {
+ return new OriginatorIdentifierOrKey((OriginatorPublicKey)o);
+ }
+
+ if (o instanceof ASN1TaggedObject)
+ {
+ // TODO Add validation
+ return new OriginatorIdentifierOrKey((ASN1TaggedObject)o);
+ }
+
+ throw new IllegalArgumentException("Invalid OriginatorIdentifierOrKey: " + o.getClass().getName());
+ }
+
+ public ASN1Encodable getId()
+ {
+ return id;
+ }
+
+ public IssuerAndSerialNumber getIssuerAndSerialNumber()
+ {
+ if (id instanceof IssuerAndSerialNumber)
+ {
+ return (IssuerAndSerialNumber)id;
+ }
+
+ return null;
+ }
+
+ public SubjectKeyIdentifier getSubjectKeyIdentifier()
+ {
+ if (id instanceof ASN1TaggedObject && ((ASN1TaggedObject)id).getTagNo() == 0)
+ {
+ return SubjectKeyIdentifier.getInstance((ASN1TaggedObject)id, false);
+ }
+
+ return null;
+ }
+
+ public OriginatorPublicKey getOriginatorKey()
+ {
+ if (id instanceof ASN1TaggedObject && ((ASN1TaggedObject)id).getTagNo() == 1)
+ {
+ return OriginatorPublicKey.getInstance((ASN1TaggedObject)id, false);
+ }
+
+ return null;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * OriginatorIdentifierOrKey ::= CHOICE {
+ * issuerAndSerialNumber IssuerAndSerialNumber,
+ * subjectKeyIdentifier [0] SubjectKeyIdentifier,
+ * originatorKey [1] OriginatorPublicKey
+ * }
+ *
+ * SubjectKeyIdentifier ::= OCTET STRING
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return id.toASN1Primitive();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/OriginatorInfo.java b/core/src/main/java/org/bouncycastle/asn1/cms/OriginatorInfo.java
new file mode 100644
index 00000000..d87054bc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/OriginatorInfo.java
@@ -0,0 +1,128 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class OriginatorInfo
+ extends ASN1Object
+{
+ private ASN1Set certs;
+ private ASN1Set crls;
+
+ public OriginatorInfo(
+ ASN1Set certs,
+ ASN1Set crls)
+ {
+ this.certs = certs;
+ this.crls = crls;
+ }
+
+ private OriginatorInfo(
+ ASN1Sequence seq)
+ {
+ switch (seq.size())
+ {
+ case 0: // empty
+ break;
+ case 1:
+ ASN1TaggedObject o = (ASN1TaggedObject)seq.getObjectAt(0);
+ switch (o.getTagNo())
+ {
+ case 0 :
+ certs = ASN1Set.getInstance(o, false);
+ break;
+ case 1 :
+ crls = ASN1Set.getInstance(o, false);
+ break;
+ default:
+ throw new IllegalArgumentException("Bad tag in OriginatorInfo: " + o.getTagNo());
+ }
+ break;
+ case 2:
+ certs = ASN1Set.getInstance((ASN1TaggedObject)seq.getObjectAt(0), false);
+ crls = ASN1Set.getInstance((ASN1TaggedObject)seq.getObjectAt(1), false);
+ break;
+ default:
+ throw new IllegalArgumentException("OriginatorInfo too big");
+ }
+ }
+
+ /**
+ * return an OriginatorInfo 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 object held by the
+ * tagged object cannot be converted.
+ */
+ public static OriginatorInfo getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * return an OriginatorInfo object from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static OriginatorInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof OriginatorInfo)
+ {
+ return (OriginatorInfo)obj;
+ }
+ else if (obj != null)
+ {
+ return new OriginatorInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public ASN1Set getCertificates()
+ {
+ return certs;
+ }
+
+ public ASN1Set getCRLs()
+ {
+ return crls;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * OriginatorInfo ::= SEQUENCE {
+ * certs [0] IMPLICIT CertificateSet OPTIONAL,
+ * crls [1] IMPLICIT CertificateRevocationLists OPTIONAL
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (certs != null)
+ {
+ v.add(new DERTaggedObject(false, 0, certs));
+ }
+
+ if (crls != null)
+ {
+ v.add(new DERTaggedObject(false, 1, crls));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/OriginatorPublicKey.java b/core/src/main/java/org/bouncycastle/asn1/cms/OriginatorPublicKey.java
new file mode 100644
index 00000000..5d95d138
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/OriginatorPublicKey.java
@@ -0,0 +1,100 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+
+public class OriginatorPublicKey
+ extends ASN1Object
+{
+ private AlgorithmIdentifier algorithm;
+ private DERBitString publicKey;
+
+ public OriginatorPublicKey(
+ AlgorithmIdentifier algorithm,
+ byte[] publicKey)
+ {
+ this.algorithm = algorithm;
+ this.publicKey = new DERBitString(publicKey);
+ }
+
+ public OriginatorPublicKey(
+ ASN1Sequence seq)
+ {
+ algorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(0));
+ publicKey = (DERBitString)seq.getObjectAt(1);
+ }
+
+ /**
+ * return an OriginatorPublicKey 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 object held by the
+ * tagged object cannot be converted.
+ */
+ public static OriginatorPublicKey getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * return an OriginatorPublicKey object from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static OriginatorPublicKey getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof OriginatorPublicKey)
+ {
+ return (OriginatorPublicKey)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new OriginatorPublicKey((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid OriginatorPublicKey: " + obj.getClass().getName());
+ }
+
+ public AlgorithmIdentifier getAlgorithm()
+ {
+ return algorithm;
+ }
+
+ public DERBitString getPublicKey()
+ {
+ return publicKey;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * OriginatorPublicKey ::= SEQUENCE {
+ * algorithm AlgorithmIdentifier,
+ * publicKey BIT STRING
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(algorithm);
+ v.add(publicKey);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/OtherKeyAttribute.java b/core/src/main/java/org/bouncycastle/asn1/cms/OtherKeyAttribute.java
new file mode 100644
index 00000000..1336bb65
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/OtherKeyAttribute.java
@@ -0,0 +1,82 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class OtherKeyAttribute
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier keyAttrId;
+ private ASN1Encodable keyAttr;
+
+ /**
+ * return an OtherKeyAttribute object from the given object.
+ *
+ * @param o the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static OtherKeyAttribute getInstance(
+ Object o)
+ {
+ if (o == null || o instanceof OtherKeyAttribute)
+ {
+ return (OtherKeyAttribute)o;
+ }
+
+ if (o instanceof ASN1Sequence)
+ {
+ return new OtherKeyAttribute((ASN1Sequence)o);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory: " + o.getClass().getName());
+ }
+
+ public OtherKeyAttribute(
+ ASN1Sequence seq)
+ {
+ keyAttrId = (ASN1ObjectIdentifier)seq.getObjectAt(0);
+ keyAttr = seq.getObjectAt(1);
+ }
+
+ public OtherKeyAttribute(
+ ASN1ObjectIdentifier keyAttrId,
+ ASN1Encodable keyAttr)
+ {
+ this.keyAttrId = keyAttrId;
+ this.keyAttr = keyAttr;
+ }
+
+ public ASN1ObjectIdentifier getKeyAttrId()
+ {
+ return keyAttrId;
+ }
+
+ public ASN1Encodable getKeyAttr()
+ {
+ return keyAttr;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * OtherKeyAttribute ::= SEQUENCE {
+ * keyAttrId OBJECT IDENTIFIER,
+ * keyAttr ANY DEFINED BY keyAttrId OPTIONAL
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(keyAttrId);
+ v.add(keyAttr);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/OtherRecipientInfo.java b/core/src/main/java/org/bouncycastle/asn1/cms/OtherRecipientInfo.java
new file mode 100644
index 00000000..692c96c2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/OtherRecipientInfo.java
@@ -0,0 +1,102 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+public class OtherRecipientInfo
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier oriType;
+ private ASN1Encodable oriValue;
+
+ public OtherRecipientInfo(
+ ASN1ObjectIdentifier oriType,
+ ASN1Encodable oriValue)
+ {
+ this.oriType = oriType;
+ this.oriValue = oriValue;
+ }
+
+ /**
+ * @deprecated use getInstance().
+ * @param seq
+ */
+ public OtherRecipientInfo(
+ ASN1Sequence seq)
+ {
+ oriType = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+ oriValue = seq.getObjectAt(1);
+ }
+
+ /**
+ * return a OtherRecipientInfo 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 object held by the
+ * tagged object cannot be converted.
+ */
+ public static OtherRecipientInfo getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * return a OtherRecipientInfo object from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static OtherRecipientInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof OtherRecipientInfo)
+ {
+ return (OtherRecipientInfo)obj;
+ }
+
+ if (obj != null)
+ {
+ return new OtherRecipientInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public ASN1ObjectIdentifier getType()
+ {
+ return oriType;
+ }
+
+ public ASN1Encodable getValue()
+ {
+ return oriValue;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * OtherRecipientInfo ::= SEQUENCE {
+ * oriType OBJECT IDENTIFIER,
+ * oriValue ANY DEFINED BY oriType }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(oriType);
+ v.add(oriValue);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/OtherRevocationInfoFormat.java b/core/src/main/java/org/bouncycastle/asn1/cms/OtherRevocationInfoFormat.java
new file mode 100644
index 00000000..ae6518a2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/OtherRevocationInfoFormat.java
@@ -0,0 +1,98 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+public class OtherRevocationInfoFormat
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier otherRevInfoFormat;
+ private ASN1Encodable otherRevInfo;
+
+ public OtherRevocationInfoFormat(
+ ASN1ObjectIdentifier otherRevInfoFormat,
+ ASN1Encodable otherRevInfo)
+ {
+ this.otherRevInfoFormat = otherRevInfoFormat;
+ this.otherRevInfo = otherRevInfo;
+ }
+
+ private OtherRevocationInfoFormat(
+ ASN1Sequence seq)
+ {
+ otherRevInfoFormat = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+ otherRevInfo = seq.getObjectAt(1);
+ }
+
+ /**
+ * return a OtherRevocationInfoFormat 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 object held by the
+ * tagged object cannot be converted.
+ */
+ public static OtherRevocationInfoFormat getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * return a OtherRevocationInfoFormat object from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static OtherRevocationInfoFormat getInstance(
+ Object obj)
+ {
+ if (obj instanceof OtherRevocationInfoFormat)
+ {
+ return (OtherRevocationInfoFormat)obj;
+ }
+
+ if (obj != null)
+ {
+ return new OtherRevocationInfoFormat(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public ASN1ObjectIdentifier getInfoFormat()
+ {
+ return otherRevInfoFormat;
+ }
+
+ public ASN1Encodable getInfo()
+ {
+ return otherRevInfo;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * OtherRevocationInfoFormat ::= SEQUENCE {
+ * otherRevInfoFormat OBJECT IDENTIFIER,
+ * otherRevInfo ANY DEFINED BY otherRevInfoFormat }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(otherRevInfoFormat);
+ v.add(otherRevInfo);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/PasswordRecipientInfo.java b/core/src/main/java/org/bouncycastle/asn1/cms/PasswordRecipientInfo.java
new file mode 100644
index 00000000..f325fcd9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/PasswordRecipientInfo.java
@@ -0,0 +1,143 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class PasswordRecipientInfo
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private AlgorithmIdentifier keyDerivationAlgorithm;
+ private AlgorithmIdentifier keyEncryptionAlgorithm;
+ private ASN1OctetString encryptedKey;
+
+ public PasswordRecipientInfo(
+ AlgorithmIdentifier keyEncryptionAlgorithm,
+ ASN1OctetString encryptedKey)
+ {
+ this.version = new ASN1Integer(0);
+ this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
+ this.encryptedKey = encryptedKey;
+ }
+
+ public PasswordRecipientInfo(
+ AlgorithmIdentifier keyDerivationAlgorithm,
+ AlgorithmIdentifier keyEncryptionAlgorithm,
+ ASN1OctetString encryptedKey)
+ {
+ this.version = new ASN1Integer(0);
+ this.keyDerivationAlgorithm = keyDerivationAlgorithm;
+ this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
+ this.encryptedKey = encryptedKey;
+ }
+
+ public PasswordRecipientInfo(
+ ASN1Sequence seq)
+ {
+ version = (ASN1Integer)seq.getObjectAt(0);
+ if (seq.getObjectAt(1) instanceof ASN1TaggedObject)
+ {
+ keyDerivationAlgorithm = AlgorithmIdentifier.getInstance((ASN1TaggedObject)seq.getObjectAt(1), false);
+ keyEncryptionAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(2));
+ encryptedKey = (ASN1OctetString)seq.getObjectAt(3);
+ }
+ else
+ {
+ keyEncryptionAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
+ encryptedKey = (ASN1OctetString)seq.getObjectAt(2);
+ }
+ }
+
+ /**
+ * return a PasswordRecipientInfo 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 object held by the
+ * tagged object cannot be converted.
+ */
+ public static PasswordRecipientInfo getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * return a PasswordRecipientInfo object from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static PasswordRecipientInfo getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof PasswordRecipientInfo)
+ {
+ return (PasswordRecipientInfo)obj;
+ }
+
+ if(obj instanceof ASN1Sequence)
+ {
+ return new PasswordRecipientInfo((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid PasswordRecipientInfo: " + obj.getClass().getName());
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public AlgorithmIdentifier getKeyDerivationAlgorithm()
+ {
+ return keyDerivationAlgorithm;
+ }
+
+ public AlgorithmIdentifier getKeyEncryptionAlgorithm()
+ {
+ return keyEncryptionAlgorithm;
+ }
+
+ public ASN1OctetString getEncryptedKey()
+ {
+ return encryptedKey;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * PasswordRecipientInfo ::= SEQUENCE {
+ * version CMSVersion, -- Always set to 0
+ * keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier
+ * OPTIONAL,
+ * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
+ * encryptedKey EncryptedKey }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+
+ if (keyDerivationAlgorithm != null)
+ {
+ v.add(new DERTaggedObject(false, 0, keyDerivationAlgorithm));
+ }
+ v.add(keyEncryptionAlgorithm);
+ v.add(encryptedKey);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/RecipientEncryptedKey.java b/core/src/main/java/org/bouncycastle/asn1/cms/RecipientEncryptedKey.java
new file mode 100644
index 00000000..2f2a1732
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/RecipientEncryptedKey.java
@@ -0,0 +1,99 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+
+public class RecipientEncryptedKey
+ extends ASN1Object
+{
+ private KeyAgreeRecipientIdentifier identifier;
+ private ASN1OctetString encryptedKey;
+
+ private RecipientEncryptedKey(
+ ASN1Sequence seq)
+ {
+ identifier = KeyAgreeRecipientIdentifier.getInstance(seq.getObjectAt(0));
+ encryptedKey = (ASN1OctetString)seq.getObjectAt(1);
+ }
+
+ /**
+ * return an RecipientEncryptedKey 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 object held by the
+ * tagged object cannot be converted.
+ */
+ public static RecipientEncryptedKey getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * return a RecipientEncryptedKey object from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static RecipientEncryptedKey getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof RecipientEncryptedKey)
+ {
+ return (RecipientEncryptedKey)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new RecipientEncryptedKey((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid RecipientEncryptedKey: " + obj.getClass().getName());
+ }
+
+ public RecipientEncryptedKey(
+ KeyAgreeRecipientIdentifier id,
+ ASN1OctetString encryptedKey)
+ {
+ this.identifier = id;
+ this.encryptedKey = encryptedKey;
+ }
+
+ public KeyAgreeRecipientIdentifier getIdentifier()
+ {
+ return identifier;
+ }
+
+ public ASN1OctetString getEncryptedKey()
+ {
+ return encryptedKey;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * RecipientEncryptedKey ::= SEQUENCE {
+ * rid KeyAgreeRecipientIdentifier,
+ * encryptedKey EncryptedKey
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(identifier);
+ v.add(encryptedKey);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/RecipientIdentifier.java b/core/src/main/java/org/bouncycastle/asn1/cms/RecipientIdentifier.java
new file mode 100644
index 00000000..8aa992de
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/RecipientIdentifier.java
@@ -0,0 +1,98 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class RecipientIdentifier
+ extends ASN1Object
+ implements ASN1Choice
+{
+ private ASN1Encodable id;
+
+ public RecipientIdentifier(
+ IssuerAndSerialNumber id)
+ {
+ this.id = id;
+ }
+
+ public RecipientIdentifier(
+ ASN1OctetString id)
+ {
+ this.id = new DERTaggedObject(false, 0, id);
+ }
+
+ public RecipientIdentifier(
+ ASN1Primitive id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * return a RecipientIdentifier object from the given object.
+ *
+ * @param o the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static RecipientIdentifier getInstance(
+ Object o)
+ {
+ if (o == null || o instanceof RecipientIdentifier)
+ {
+ return (RecipientIdentifier)o;
+ }
+
+ if (o instanceof IssuerAndSerialNumber)
+ {
+ return new RecipientIdentifier((IssuerAndSerialNumber)o);
+ }
+
+ if (o instanceof ASN1OctetString)
+ {
+ return new RecipientIdentifier((ASN1OctetString)o);
+ }
+
+ if (o instanceof ASN1Primitive)
+ {
+ return new RecipientIdentifier((ASN1Primitive)o);
+ }
+
+ throw new IllegalArgumentException(
+ "Illegal object in RecipientIdentifier: " + o.getClass().getName());
+ }
+
+ public boolean isTagged()
+ {
+ return (id instanceof ASN1TaggedObject);
+ }
+
+ public ASN1Encodable getId()
+ {
+ if (id instanceof ASN1TaggedObject)
+ {
+ return ASN1OctetString.getInstance((ASN1TaggedObject)id, false);
+ }
+
+ return IssuerAndSerialNumber.getInstance(id);
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * RecipientIdentifier ::= CHOICE {
+ * issuerAndSerialNumber IssuerAndSerialNumber,
+ * subjectKeyIdentifier [0] SubjectKeyIdentifier
+ * }
+ *
+ * SubjectKeyIdentifier ::= OCTET STRING
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return id.toASN1Primitive();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/RecipientInfo.java b/core/src/main/java/org/bouncycastle/asn1/cms/RecipientInfo.java
new file mode 100644
index 00000000..7593a7a1
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/RecipientInfo.java
@@ -0,0 +1,154 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class RecipientInfo
+ extends ASN1Object
+ implements ASN1Choice
+{
+ ASN1Encodable info;
+
+ public RecipientInfo(
+ KeyTransRecipientInfo info)
+ {
+ this.info = info;
+ }
+
+ public RecipientInfo(
+ KeyAgreeRecipientInfo info)
+ {
+ this.info = new DERTaggedObject(false, 1, info);
+ }
+
+ public RecipientInfo(
+ KEKRecipientInfo info)
+ {
+ this.info = new DERTaggedObject(false, 2, info);
+ }
+
+ public RecipientInfo(
+ PasswordRecipientInfo info)
+ {
+ this.info = new DERTaggedObject(false, 3, info);
+ }
+
+ public RecipientInfo(
+ OtherRecipientInfo info)
+ {
+ this.info = new DERTaggedObject(false, 4, info);
+ }
+
+ public RecipientInfo(
+ ASN1Primitive info)
+ {
+ this.info = info;
+ }
+
+ public static RecipientInfo getInstance(
+ Object o)
+ {
+ if (o == null || o instanceof RecipientInfo)
+ {
+ return (RecipientInfo)o;
+ }
+ else if (o instanceof ASN1Sequence)
+ {
+ return new RecipientInfo((ASN1Sequence)o);
+ }
+ else if (o instanceof ASN1TaggedObject)
+ {
+ return new RecipientInfo((ASN1TaggedObject)o);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory: "
+ + o.getClass().getName());
+ }
+
+ public ASN1Integer getVersion()
+ {
+ if (info instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject o = (ASN1TaggedObject)info;
+
+ switch (o.getTagNo())
+ {
+ case 1:
+ return KeyAgreeRecipientInfo.getInstance(o, false).getVersion();
+ case 2:
+ return getKEKInfo(o).getVersion();
+ case 3:
+ return PasswordRecipientInfo.getInstance(o, false).getVersion();
+ case 4:
+ return new ASN1Integer(0); // no syntax version for OtherRecipientInfo
+ default:
+ throw new IllegalStateException("unknown tag");
+ }
+ }
+
+ return KeyTransRecipientInfo.getInstance(info).getVersion();
+ }
+
+ public boolean isTagged()
+ {
+ return (info instanceof ASN1TaggedObject);
+ }
+
+ public ASN1Encodable getInfo()
+ {
+ if (info instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject o = (ASN1TaggedObject)info;
+
+ switch (o.getTagNo())
+ {
+ case 1:
+ return KeyAgreeRecipientInfo.getInstance(o, false);
+ case 2:
+ return getKEKInfo(o);
+ case 3:
+ return PasswordRecipientInfo.getInstance(o, false);
+ case 4:
+ return OtherRecipientInfo.getInstance(o, false);
+ default:
+ throw new IllegalStateException("unknown tag");
+ }
+ }
+
+ return KeyTransRecipientInfo.getInstance(info);
+ }
+
+ private KEKRecipientInfo getKEKInfo(ASN1TaggedObject o)
+ {
+ if (o.isExplicit())
+ { // compatibilty with erroneous version
+ return KEKRecipientInfo.getInstance(o, true);
+ }
+ else
+ {
+ return KEKRecipientInfo.getInstance(o, false);
+ }
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * RecipientInfo ::= CHOICE {
+ * ktri KeyTransRecipientInfo,
+ * kari [1] KeyAgreeRecipientInfo,
+ * kekri [2] KEKRecipientInfo,
+ * pwri [3] PasswordRecipientInfo,
+ * ori [4] OtherRecipientInfo }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return info.toASN1Primitive();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/RecipientKeyIdentifier.java b/core/src/main/java/org/bouncycastle/asn1/cms/RecipientKeyIdentifier.java
new file mode 100644
index 00000000..076761b4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/RecipientKeyIdentifier.java
@@ -0,0 +1,156 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERGeneralizedTime;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+
+public class RecipientKeyIdentifier
+ extends ASN1Object
+{
+ private ASN1OctetString subjectKeyIdentifier;
+ private DERGeneralizedTime date;
+ private OtherKeyAttribute other;
+
+ public RecipientKeyIdentifier(
+ ASN1OctetString subjectKeyIdentifier,
+ DERGeneralizedTime date,
+ OtherKeyAttribute other)
+ {
+ this.subjectKeyIdentifier = subjectKeyIdentifier;
+ this.date = date;
+ this.other = other;
+ }
+
+ public RecipientKeyIdentifier(
+ byte[] subjectKeyIdentifier,
+ DERGeneralizedTime date,
+ OtherKeyAttribute other)
+ {
+ this.subjectKeyIdentifier = new DEROctetString(subjectKeyIdentifier);
+ this.date = date;
+ this.other = other;
+ }
+
+ public RecipientKeyIdentifier(
+ byte[] subjectKeyIdentifier)
+ {
+ this(subjectKeyIdentifier, null, null);
+ }
+
+ public RecipientKeyIdentifier(
+ ASN1Sequence seq)
+ {
+ subjectKeyIdentifier = ASN1OctetString.getInstance(
+ seq.getObjectAt(0));
+
+ switch(seq.size())
+ {
+ case 1:
+ break;
+ case 2:
+ if (seq.getObjectAt(1) instanceof DERGeneralizedTime)
+ {
+ date = (DERGeneralizedTime)seq.getObjectAt(1);
+ }
+ else
+ {
+ other = OtherKeyAttribute.getInstance(seq.getObjectAt(2));
+ }
+ break;
+ case 3:
+ date = (DERGeneralizedTime)seq.getObjectAt(1);
+ other = OtherKeyAttribute.getInstance(seq.getObjectAt(2));
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid RecipientKeyIdentifier");
+ }
+ }
+
+ /**
+ * return a RecipientKeyIdentifier object from a tagged object.
+ *
+ * @param _ato 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 object held by the
+ * tagged object cannot be converted.
+ */
+ public static RecipientKeyIdentifier getInstance(ASN1TaggedObject _ato, boolean _explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(_ato, _explicit));
+ }
+
+ /**
+ * return a RecipientKeyIdentifier object from the given object.
+ *
+ * @param _obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static RecipientKeyIdentifier getInstance(Object _obj)
+ {
+ if(_obj == null || _obj instanceof RecipientKeyIdentifier)
+ {
+ return (RecipientKeyIdentifier)_obj;
+ }
+
+ if(_obj instanceof ASN1Sequence)
+ {
+ return new RecipientKeyIdentifier((ASN1Sequence)_obj);
+ }
+
+ throw new IllegalArgumentException("Invalid RecipientKeyIdentifier: " + _obj.getClass().getName());
+ }
+
+ public ASN1OctetString getSubjectKeyIdentifier()
+ {
+ return subjectKeyIdentifier;
+ }
+
+ public DERGeneralizedTime getDate()
+ {
+ return date;
+ }
+
+ public OtherKeyAttribute getOtherKeyAttribute()
+ {
+ return other;
+ }
+
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * RecipientKeyIdentifier ::= SEQUENCE {
+ * subjectKeyIdentifier SubjectKeyIdentifier,
+ * date GeneralizedTime OPTIONAL,
+ * other OtherKeyAttribute OPTIONAL
+ * }
+ *
+ * SubjectKeyIdentifier ::= OCTET STRING
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(subjectKeyIdentifier);
+
+ if (date != null)
+ {
+ v.add(date);
+ }
+
+ if (other != null)
+ {
+ v.add(other);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/SCVPReqRes.java b/core/src/main/java/org/bouncycastle/asn1/cms/SCVPReqRes.java
new file mode 100644
index 00000000..e9b91eb5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/SCVPReqRes.java
@@ -0,0 +1,90 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class SCVPReqRes
+ extends ASN1Object
+{
+ private final ContentInfo request;
+ private final ContentInfo response;
+
+ public static SCVPReqRes getInstance(
+ Object obj)
+ {
+ if (obj instanceof SCVPReqRes)
+ {
+ return (SCVPReqRes)obj;
+ }
+ else if (obj != null)
+ {
+ return new SCVPReqRes(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private SCVPReqRes(
+ ASN1Sequence seq)
+ {
+ if (seq.getObjectAt(0) instanceof ASN1TaggedObject)
+ {
+ this.request = ContentInfo.getInstance(ASN1TaggedObject.getInstance(seq.getObjectAt(0)), true);
+ this.response = ContentInfo.getInstance(seq.getObjectAt(1));
+ }
+ else
+ {
+ this.request = null;
+ this.response = ContentInfo.getInstance(seq.getObjectAt(0));
+ }
+ }
+
+ public SCVPReqRes(ContentInfo response)
+ {
+ this.request = null; // use of this confuses earlier JDKs
+ this.response = response;
+ }
+
+ public SCVPReqRes(ContentInfo request, ContentInfo response)
+ {
+ this.request = request;
+ this.response = response;
+ }
+
+ public ContentInfo getRequest()
+ {
+ return request;
+ }
+
+ public ContentInfo getResponse()
+ {
+ return response;
+ }
+
+ /**
+ * <pre>
+ * SCVPReqRes ::= SEQUENCE {
+ * request [0] EXPLICIT ContentInfo OPTIONAL,
+ * response ContentInfo }
+ * </pre>
+ * @return the ASN.1 primitive representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (request != null)
+ {
+ v.add(new DERTaggedObject(true, 0, request));
+ }
+
+ v.add(response);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/SignedData.java b/core/src/main/java/org/bouncycastle/asn1/cms/SignedData.java
new file mode 100644
index 00000000..fd2718a9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/SignedData.java
@@ -0,0 +1,307 @@
+package org.bouncycastle.asn1.cms;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.BERSequence;
+import org.bouncycastle.asn1.BERSet;
+import org.bouncycastle.asn1.BERTaggedObject;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * a signed data object.
+ */
+public class SignedData
+ extends ASN1Object
+{
+ private static final ASN1Integer VERSION_1 = new ASN1Integer(1);
+ private static final ASN1Integer VERSION_3 = new ASN1Integer(3);
+ private static final ASN1Integer VERSION_4 = new ASN1Integer(4);
+ private static final ASN1Integer VERSION_5 = new ASN1Integer(5);
+
+ private ASN1Integer version;
+ private ASN1Set digestAlgorithms;
+ private ContentInfo contentInfo;
+ private ASN1Set certificates;
+ private ASN1Set crls;
+ private ASN1Set signerInfos;
+ private boolean certsBer;
+ private boolean crlsBer;
+
+ public static SignedData getInstance(
+ Object o)
+ {
+ if (o instanceof SignedData)
+ {
+ return (SignedData)o;
+ }
+ else if (o != null)
+ {
+ return new SignedData(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public SignedData(
+ ASN1Set digestAlgorithms,
+ ContentInfo contentInfo,
+ ASN1Set certificates,
+ ASN1Set crls,
+ ASN1Set signerInfos)
+ {
+ this.version = calculateVersion(contentInfo.getContentType(), certificates, crls, signerInfos);
+ this.digestAlgorithms = digestAlgorithms;
+ this.contentInfo = contentInfo;
+ this.certificates = certificates;
+ this.crls = crls;
+ this.signerInfos = signerInfos;
+ this.crlsBer = crls instanceof BERSet;
+ this.certsBer = certificates instanceof BERSet;
+ }
+
+
+ // RFC3852, section 5.1:
+ // IF ((certificates is present) AND
+ // (any certificates with a type of other are present)) OR
+ // ((crls is present) AND
+ // (any crls with a type of other are present))
+ // THEN version MUST be 5
+ // ELSE
+ // IF (certificates is present) AND
+ // (any version 2 attribute certificates are present)
+ // THEN version MUST be 4
+ // ELSE
+ // IF ((certificates is present) AND
+ // (any version 1 attribute certificates are present)) OR
+ // (any SignerInfo structures are version 3) OR
+ // (encapContentInfo eContentType is other than id-data)
+ // THEN version MUST be 3
+ // ELSE version MUST be 1
+ //
+ private ASN1Integer calculateVersion(
+ ASN1ObjectIdentifier contentOid,
+ ASN1Set certs,
+ ASN1Set crls,
+ ASN1Set signerInfs)
+ {
+ boolean otherCert = false;
+ boolean otherCrl = false;
+ boolean attrCertV1Found = false;
+ boolean attrCertV2Found = false;
+
+ if (certs != null)
+ {
+ for (Enumeration en = certs.getObjects(); en.hasMoreElements();)
+ {
+ Object obj = en.nextElement();
+ if (obj instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(obj);
+
+ if (tagged.getTagNo() == 1)
+ {
+ attrCertV1Found = true;
+ }
+ else if (tagged.getTagNo() == 2)
+ {
+ attrCertV2Found = true;
+ }
+ else if (tagged.getTagNo() == 3)
+ {
+ otherCert = true;
+ }
+ }
+ }
+ }
+
+ if (otherCert)
+ {
+ return new ASN1Integer(5);
+ }
+
+ if (crls != null) // no need to check if otherCert is true
+ {
+ for (Enumeration en = crls.getObjects(); en.hasMoreElements();)
+ {
+ Object obj = en.nextElement();
+ if (obj instanceof ASN1TaggedObject)
+ {
+ otherCrl = true;
+ }
+ }
+ }
+
+ if (otherCrl)
+ {
+ return VERSION_5;
+ }
+
+ if (attrCertV2Found)
+ {
+ return VERSION_4;
+ }
+
+ if (attrCertV1Found)
+ {
+ return VERSION_3;
+ }
+
+ if (checkForVersion3(signerInfs))
+ {
+ return VERSION_3;
+ }
+
+ if (!CMSObjectIdentifiers.data.equals(contentOid))
+ {
+ return VERSION_3;
+ }
+
+ return VERSION_1;
+ }
+
+ private boolean checkForVersion3(ASN1Set signerInfs)
+ {
+ for (Enumeration e = signerInfs.getObjects(); e.hasMoreElements();)
+ {
+ SignerInfo s = SignerInfo.getInstance(e.nextElement());
+
+ if (s.getVersion().getValue().intValue() == 3)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private SignedData(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ version = ASN1Integer.getInstance(e.nextElement());
+ digestAlgorithms = ((ASN1Set)e.nextElement());
+ contentInfo = ContentInfo.getInstance(e.nextElement());
+
+ while (e.hasMoreElements())
+ {
+ ASN1Primitive o = (ASN1Primitive)e.nextElement();
+
+ //
+ // an interesting feature of SignedData is that there appear
+ // to be varying implementations...
+ // for the moment we ignore anything which doesn't fit.
+ //
+ if (o instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject tagged = (ASN1TaggedObject)o;
+
+ switch (tagged.getTagNo())
+ {
+ case 0:
+ certsBer = tagged instanceof BERTaggedObject;
+ certificates = ASN1Set.getInstance(tagged, false);
+ break;
+ case 1:
+ crlsBer = tagged instanceof BERTaggedObject;
+ crls = ASN1Set.getInstance(tagged, false);
+ break;
+ default:
+ throw new IllegalArgumentException("unknown tag value " + tagged.getTagNo());
+ }
+ }
+ else
+ {
+ signerInfos = (ASN1Set)o;
+ }
+ }
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public ASN1Set getDigestAlgorithms()
+ {
+ return digestAlgorithms;
+ }
+
+ public ContentInfo getEncapContentInfo()
+ {
+ return contentInfo;
+ }
+
+ public ASN1Set getCertificates()
+ {
+ return certificates;
+ }
+
+ public ASN1Set getCRLs()
+ {
+ return crls;
+ }
+
+ public ASN1Set getSignerInfos()
+ {
+ return signerInfos;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * SignedData ::= SEQUENCE {
+ * version CMSVersion,
+ * digestAlgorithms DigestAlgorithmIdentifiers,
+ * encapContentInfo EncapsulatedContentInfo,
+ * certificates [0] IMPLICIT CertificateSet OPTIONAL,
+ * crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
+ * signerInfos SignerInfos
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+ v.add(digestAlgorithms);
+ v.add(contentInfo);
+
+ if (certificates != null)
+ {
+ if (certsBer)
+ {
+ v.add(new BERTaggedObject(false, 0, certificates));
+ }
+ else
+ {
+ v.add(new DERTaggedObject(false, 0, certificates));
+ }
+ }
+
+ if (crls != null)
+ {
+ if (crlsBer)
+ {
+ v.add(new BERTaggedObject(false, 1, crls));
+ }
+ else
+ {
+ v.add(new DERTaggedObject(false, 1, crls));
+ }
+ }
+
+ v.add(signerInfos);
+
+ return new BERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/SignedDataParser.java b/core/src/main/java/org/bouncycastle/asn1/cms/SignedDataParser.java
new file mode 100644
index 00000000..6e23b29f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/SignedDataParser.java
@@ -0,0 +1,139 @@
+package org.bouncycastle.asn1.cms;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1SequenceParser;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1SetParser;
+import org.bouncycastle.asn1.ASN1TaggedObjectParser;
+import org.bouncycastle.asn1.BERTags;
+
+/**
+ * <pre>
+ * SignedData ::= SEQUENCE {
+ * version CMSVersion,
+ * digestAlgorithms DigestAlgorithmIdentifiers,
+ * encapContentInfo EncapsulatedContentInfo,
+ * certificates [0] IMPLICIT CertificateSet OPTIONAL,
+ * crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
+ * signerInfos SignerInfos
+ * }
+ * </pre>
+ */
+public class SignedDataParser
+{
+ private ASN1SequenceParser _seq;
+ private ASN1Integer _version;
+ private Object _nextObject;
+ private boolean _certsCalled;
+ private boolean _crlsCalled;
+
+ public static SignedDataParser getInstance(
+ Object o)
+ throws IOException
+ {
+ if (o instanceof ASN1Sequence)
+ {
+ return new SignedDataParser(((ASN1Sequence)o).parser());
+ }
+ if (o instanceof ASN1SequenceParser)
+ {
+ return new SignedDataParser((ASN1SequenceParser)o);
+ }
+
+ throw new IOException("unknown object encountered: " + o.getClass().getName());
+ }
+
+ private SignedDataParser(
+ ASN1SequenceParser seq)
+ throws IOException
+ {
+ this._seq = seq;
+ this._version = (ASN1Integer)seq.readObject();
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return _version;
+ }
+
+ public ASN1SetParser getDigestAlgorithms()
+ throws IOException
+ {
+ Object o = _seq.readObject();
+
+ if (o instanceof ASN1Set)
+ {
+ return ((ASN1Set)o).parser();
+ }
+
+ return (ASN1SetParser)o;
+ }
+
+ public ContentInfoParser getEncapContentInfo()
+ throws IOException
+ {
+ return new ContentInfoParser((ASN1SequenceParser)_seq.readObject());
+ }
+
+ public ASN1SetParser getCertificates()
+ throws IOException
+ {
+ _certsCalled = true;
+ _nextObject = _seq.readObject();
+
+ if (_nextObject instanceof ASN1TaggedObjectParser && ((ASN1TaggedObjectParser)_nextObject).getTagNo() == 0)
+ {
+ ASN1SetParser certs = (ASN1SetParser)((ASN1TaggedObjectParser)_nextObject).getObjectParser(BERTags.SET, false);
+ _nextObject = null;
+
+ return certs;
+ }
+
+ return null;
+ }
+
+ public ASN1SetParser getCrls()
+ throws IOException
+ {
+ if (!_certsCalled)
+ {
+ throw new IOException("getCerts() has not been called.");
+ }
+
+ _crlsCalled = true;
+
+ if (_nextObject == null)
+ {
+ _nextObject = _seq.readObject();
+ }
+
+ if (_nextObject instanceof ASN1TaggedObjectParser && ((ASN1TaggedObjectParser)_nextObject).getTagNo() == 1)
+ {
+ ASN1SetParser crls = (ASN1SetParser)((ASN1TaggedObjectParser)_nextObject).getObjectParser(BERTags.SET, false);
+ _nextObject = null;
+
+ return crls;
+ }
+
+ return null;
+ }
+
+ public ASN1SetParser getSignerInfos()
+ throws IOException
+ {
+ if (!_certsCalled || !_crlsCalled)
+ {
+ throw new IOException("getCerts() and/or getCrls() has not been called.");
+ }
+
+ if (_nextObject == null)
+ {
+ _nextObject = _seq.readObject();
+ }
+
+ return (ASN1SetParser)_nextObject;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/SignerIdentifier.java b/core/src/main/java/org/bouncycastle/asn1/cms/SignerIdentifier.java
new file mode 100644
index 00000000..37b6b311
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/SignerIdentifier.java
@@ -0,0 +1,98 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class SignerIdentifier
+ extends ASN1Object
+ implements ASN1Choice
+{
+ private ASN1Encodable id;
+
+ public SignerIdentifier(
+ IssuerAndSerialNumber id)
+ {
+ this.id = id;
+ }
+
+ public SignerIdentifier(
+ ASN1OctetString id)
+ {
+ this.id = new DERTaggedObject(false, 0, id);
+ }
+
+ public SignerIdentifier(
+ ASN1Primitive id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * return a SignerIdentifier object from the given object.
+ *
+ * @param o the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static SignerIdentifier getInstance(
+ Object o)
+ {
+ if (o == null || o instanceof SignerIdentifier)
+ {
+ return (SignerIdentifier)o;
+ }
+
+ if (o instanceof IssuerAndSerialNumber)
+ {
+ return new SignerIdentifier((IssuerAndSerialNumber)o);
+ }
+
+ if (o instanceof ASN1OctetString)
+ {
+ return new SignerIdentifier((ASN1OctetString)o);
+ }
+
+ if (o instanceof ASN1Primitive)
+ {
+ return new SignerIdentifier((ASN1Primitive)o);
+ }
+
+ throw new IllegalArgumentException(
+ "Illegal object in SignerIdentifier: " + o.getClass().getName());
+ }
+
+ public boolean isTagged()
+ {
+ return (id instanceof ASN1TaggedObject);
+ }
+
+ public ASN1Encodable getId()
+ {
+ if (id instanceof ASN1TaggedObject)
+ {
+ return ASN1OctetString.getInstance((ASN1TaggedObject)id, false);
+ }
+
+ return id;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * SignerIdentifier ::= CHOICE {
+ * issuerAndSerialNumber IssuerAndSerialNumber,
+ * subjectKeyIdentifier [0] SubjectKeyIdentifier
+ * }
+ *
+ * SubjectKeyIdentifier ::= OCTET STRING
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return id.toASN1Primitive();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/SignerInfo.java b/core/src/main/java/org/bouncycastle/asn1/cms/SignerInfo.java
new file mode 100644
index 00000000..8aafd677
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/SignerInfo.java
@@ -0,0 +1,211 @@
+package org.bouncycastle.asn1.cms;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class SignerInfo
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private SignerIdentifier sid;
+ private AlgorithmIdentifier digAlgorithm;
+ private ASN1Set authenticatedAttributes;
+ private AlgorithmIdentifier digEncryptionAlgorithm;
+ private ASN1OctetString encryptedDigest;
+ private ASN1Set unauthenticatedAttributes;
+
+ public static SignerInfo getInstance(
+ Object o)
+ throws IllegalArgumentException
+ {
+ if (o == null || o instanceof SignerInfo)
+ {
+ return (SignerInfo)o;
+ }
+ else if (o instanceof ASN1Sequence)
+ {
+ return new SignerInfo((ASN1Sequence)o);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory: " + o.getClass().getName());
+ }
+
+ public SignerInfo(
+ SignerIdentifier sid,
+ AlgorithmIdentifier digAlgorithm,
+ ASN1Set authenticatedAttributes,
+ AlgorithmIdentifier digEncryptionAlgorithm,
+ ASN1OctetString encryptedDigest,
+ ASN1Set unauthenticatedAttributes)
+ {
+ if (sid.isTagged())
+ {
+ this.version = new ASN1Integer(3);
+ }
+ else
+ {
+ this.version = new ASN1Integer(1);
+ }
+
+ this.sid = sid;
+ this.digAlgorithm = digAlgorithm;
+ this.authenticatedAttributes = authenticatedAttributes;
+ this.digEncryptionAlgorithm = digEncryptionAlgorithm;
+ this.encryptedDigest = encryptedDigest;
+ this.unauthenticatedAttributes = unauthenticatedAttributes;
+ }
+
+ public SignerInfo(
+ SignerIdentifier sid,
+ AlgorithmIdentifier digAlgorithm,
+ Attributes authenticatedAttributes,
+ AlgorithmIdentifier digEncryptionAlgorithm,
+ ASN1OctetString encryptedDigest,
+ Attributes unauthenticatedAttributes)
+ {
+ if (sid.isTagged())
+ {
+ this.version = new ASN1Integer(3);
+ }
+ else
+ {
+ this.version = new ASN1Integer(1);
+ }
+
+ this.sid = sid;
+ this.digAlgorithm = digAlgorithm;
+ this.authenticatedAttributes = ASN1Set.getInstance(authenticatedAttributes);
+ this.digEncryptionAlgorithm = digEncryptionAlgorithm;
+ this.encryptedDigest = encryptedDigest;
+ this.unauthenticatedAttributes = ASN1Set.getInstance(unauthenticatedAttributes);
+ }
+
+ /**
+ * @deprecated use getInstance() method.
+ */
+ public SignerInfo(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ version = (ASN1Integer)e.nextElement();
+ sid = SignerIdentifier.getInstance(e.nextElement());
+ digAlgorithm = AlgorithmIdentifier.getInstance(e.nextElement());
+
+ Object obj = e.nextElement();
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ authenticatedAttributes = ASN1Set.getInstance((ASN1TaggedObject)obj, false);
+
+ digEncryptionAlgorithm = AlgorithmIdentifier.getInstance(e.nextElement());
+ }
+ else
+ {
+ authenticatedAttributes = null;
+ digEncryptionAlgorithm = AlgorithmIdentifier.getInstance(obj);
+ }
+
+ encryptedDigest = DEROctetString.getInstance(e.nextElement());
+
+ if (e.hasMoreElements())
+ {
+ unauthenticatedAttributes = ASN1Set.getInstance((ASN1TaggedObject)e.nextElement(), false);
+ }
+ else
+ {
+ unauthenticatedAttributes = null;
+ }
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public SignerIdentifier getSID()
+ {
+ return sid;
+ }
+
+ public ASN1Set getAuthenticatedAttributes()
+ {
+ return authenticatedAttributes;
+ }
+
+ public AlgorithmIdentifier getDigestAlgorithm()
+ {
+ return digAlgorithm;
+ }
+
+ public ASN1OctetString getEncryptedDigest()
+ {
+ return encryptedDigest;
+ }
+
+ public AlgorithmIdentifier getDigestEncryptionAlgorithm()
+ {
+ return digEncryptionAlgorithm;
+ }
+
+ public ASN1Set getUnauthenticatedAttributes()
+ {
+ return unauthenticatedAttributes;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * SignerInfo ::= SEQUENCE {
+ * version Version,
+ * SignerIdentifier sid,
+ * digestAlgorithm DigestAlgorithmIdentifier,
+ * authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL,
+ * digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
+ * encryptedDigest EncryptedDigest,
+ * unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL
+ * }
+ *
+ * EncryptedDigest ::= OCTET STRING
+ *
+ * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
+ *
+ * DigestEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+ v.add(sid);
+ v.add(digAlgorithm);
+
+ if (authenticatedAttributes != null)
+ {
+ v.add(new DERTaggedObject(false, 0, authenticatedAttributes));
+ }
+
+ v.add(digEncryptionAlgorithm);
+ v.add(encryptedDigest);
+
+ if (unauthenticatedAttributes != null)
+ {
+ v.add(new DERTaggedObject(false, 1, unauthenticatedAttributes));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/Time.java b/core/src/main/java/org/bouncycastle/asn1/cms/Time.java
new file mode 100644
index 00000000..2087248a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/Time.java
@@ -0,0 +1,128 @@
+package org.bouncycastle.asn1.cms;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.SimpleTimeZone;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERGeneralizedTime;
+import org.bouncycastle.asn1.DERUTCTime;
+
+public class Time
+ extends ASN1Object
+ implements ASN1Choice
+{
+ ASN1Primitive time;
+
+ public static Time getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ public Time(
+ ASN1Primitive time)
+ {
+ if (!(time instanceof DERUTCTime)
+ && !(time instanceof DERGeneralizedTime))
+ {
+ throw new IllegalArgumentException("unknown object passed to Time");
+ }
+
+ this.time = time;
+ }
+
+ /**
+ * creates a time object from a given date - if the date is between 1950
+ * and 2049 a UTCTime object is generated, otherwise a GeneralizedTime
+ * is used.
+ */
+ public Time(
+ Date date)
+ {
+ SimpleTimeZone tz = new SimpleTimeZone(0, "Z");
+ SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss");
+
+ dateF.setTimeZone(tz);
+
+ String d = dateF.format(date) + "Z";
+ int year = Integer.parseInt(d.substring(0, 4));
+
+ if (year < 1950 || year > 2049)
+ {
+ time = new DERGeneralizedTime(d);
+ }
+ else
+ {
+ time = new DERUTCTime(d.substring(2));
+ }
+ }
+
+ public static Time getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof Time)
+ {
+ return (Time)obj;
+ }
+ else if (obj instanceof DERUTCTime)
+ {
+ return new Time((DERUTCTime)obj);
+ }
+ else if (obj instanceof DERGeneralizedTime)
+ {
+ return new Time((DERGeneralizedTime)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
+ }
+
+ public String getTime()
+ {
+ if (time instanceof DERUTCTime)
+ {
+ return ((DERUTCTime)time).getAdjustedTime();
+ }
+ else
+ {
+ return ((DERGeneralizedTime)time).getTime();
+ }
+ }
+
+ public Date getDate()
+ {
+ try
+ {
+ if (time instanceof DERUTCTime)
+ {
+ return ((DERUTCTime)time).getAdjustedDate();
+ }
+ else
+ {
+ return ((DERGeneralizedTime)time).getDate();
+ }
+ }
+ catch (ParseException e)
+ { // this should never happen
+ throw new IllegalStateException("invalid date string: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * Time ::= CHOICE {
+ * utcTime UTCTime,
+ * generalTime GeneralizedTime }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return time;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/TimeStampAndCRL.java b/core/src/main/java/org/bouncycastle/asn1/cms/TimeStampAndCRL.java
new file mode 100644
index 00000000..ee1044fc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/TimeStampAndCRL.java
@@ -0,0 +1,82 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.CertificateList;
+
+public class TimeStampAndCRL
+ extends ASN1Object
+{
+ private ContentInfo timeStamp;
+ private CertificateList crl;
+
+ public TimeStampAndCRL(ContentInfo timeStamp)
+ {
+ this.timeStamp = timeStamp;
+ }
+
+ private TimeStampAndCRL(ASN1Sequence seq)
+ {
+ this.timeStamp = ContentInfo.getInstance(seq.getObjectAt(0));
+ if (seq.size() == 2)
+ {
+ this.crl = CertificateList.getInstance(seq.getObjectAt(1));
+ }
+ }
+
+ public static TimeStampAndCRL getInstance(Object obj)
+ {
+ if (obj instanceof TimeStampAndCRL)
+ {
+ return (TimeStampAndCRL)obj;
+ }
+ else if (obj != null)
+ {
+ return new TimeStampAndCRL(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public ContentInfo getTimeStampToken()
+ {
+ return this.timeStamp;
+ }
+
+ /** @deprecated use getCRL() */
+ public CertificateList getCertificateList()
+ {
+ return this.crl;
+ }
+
+ public CertificateList getCRL()
+ {
+ return this.crl;
+ }
+
+ /**
+ * <pre>
+ * TimeStampAndCRL ::= SEQUENCE {
+ * timeStamp TimeStampToken, -- according to RFC 3161
+ * crl CertificateList OPTIONAL -- according to RFC 5280
+ * }
+ * </pre>
+ * @return
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(timeStamp);
+
+ if (crl != null)
+ {
+ v.add(crl);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/TimeStampTokenEvidence.java b/core/src/main/java/org/bouncycastle/asn1/cms/TimeStampTokenEvidence.java
new file mode 100644
index 00000000..6adefbb9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/TimeStampTokenEvidence.java
@@ -0,0 +1,84 @@
+package org.bouncycastle.asn1.cms;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+public class TimeStampTokenEvidence
+ extends ASN1Object
+{
+ private TimeStampAndCRL[] timeStampAndCRLs;
+
+ public TimeStampTokenEvidence(TimeStampAndCRL[] timeStampAndCRLs)
+ {
+ this.timeStampAndCRLs = timeStampAndCRLs;
+ }
+
+ public TimeStampTokenEvidence(TimeStampAndCRL timeStampAndCRL)
+ {
+ this.timeStampAndCRLs = new TimeStampAndCRL[1];
+
+ timeStampAndCRLs[0] = timeStampAndCRL;
+ }
+
+ private TimeStampTokenEvidence(ASN1Sequence seq)
+ {
+ this.timeStampAndCRLs = new TimeStampAndCRL[seq.size()];
+
+ int count = 0;
+
+ for (Enumeration en = seq.getObjects(); en.hasMoreElements();)
+ {
+ timeStampAndCRLs[count++] = TimeStampAndCRL.getInstance(en.nextElement());
+ }
+ }
+
+ public static TimeStampTokenEvidence getInstance(ASN1TaggedObject tagged, boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(tagged, explicit));
+ }
+
+ public static TimeStampTokenEvidence getInstance(Object obj)
+ {
+ if (obj instanceof TimeStampTokenEvidence)
+ {
+ return (TimeStampTokenEvidence)obj;
+ }
+ else if (obj != null)
+ {
+ return new TimeStampTokenEvidence(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public TimeStampAndCRL[] toTimeStampAndCRLArray()
+ {
+ return timeStampAndCRLs;
+ }
+
+ /**
+ * <pre>
+ * TimeStampTokenEvidence ::=
+ * SEQUENCE SIZE(1..MAX) OF TimeStampAndCRL
+ * </pre>
+ * @return
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ for (int i = 0; i != timeStampAndCRLs.length; i++)
+ {
+ v.add(timeStampAndCRLs[i]);
+ }
+
+ return new DERSequence(v);
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/TimeStampedData.java b/core/src/main/java/org/bouncycastle/asn1/cms/TimeStampedData.java
new file mode 100644
index 00000000..ca8b6966
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/TimeStampedData.java
@@ -0,0 +1,121 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.BERSequence;
+import org.bouncycastle.asn1.DERIA5String;
+
+public class TimeStampedData
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private DERIA5String dataUri;
+ private MetaData metaData;
+ private ASN1OctetString content;
+ private Evidence temporalEvidence;
+
+ public TimeStampedData(DERIA5String dataUri, MetaData metaData, ASN1OctetString content, Evidence temporalEvidence)
+ {
+ this.version = new ASN1Integer(1);
+ this.dataUri = dataUri;
+ this.metaData = metaData;
+ this.content = content;
+ this.temporalEvidence = temporalEvidence;
+ }
+
+ private TimeStampedData(ASN1Sequence seq)
+ {
+ this.version = ASN1Integer.getInstance(seq.getObjectAt(0));
+
+ int index = 1;
+ if (seq.getObjectAt(index) instanceof DERIA5String)
+ {
+ this.dataUri = DERIA5String.getInstance(seq.getObjectAt(index++));
+ }
+ if (seq.getObjectAt(index) instanceof MetaData || seq.getObjectAt(index) instanceof ASN1Sequence)
+ {
+ this.metaData = MetaData.getInstance(seq.getObjectAt(index++));
+ }
+ if (seq.getObjectAt(index) instanceof ASN1OctetString)
+ {
+ this.content = ASN1OctetString.getInstance(seq.getObjectAt(index++));
+ }
+ this.temporalEvidence = Evidence.getInstance(seq.getObjectAt(index));
+ }
+
+ public static TimeStampedData getInstance(Object obj)
+ {
+ if (obj instanceof TimeStampedData)
+ {
+ return (TimeStampedData)obj;
+ }
+ else if (obj != null)
+ {
+ return new TimeStampedData(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public DERIA5String getDataUri()
+ {
+ return dataUri;
+ }
+
+ public MetaData getMetaData()
+ {
+ return metaData;
+ }
+
+ public ASN1OctetString getContent()
+ {
+ return content;
+ }
+
+ public Evidence getTemporalEvidence()
+ {
+ return temporalEvidence;
+ }
+
+ /**
+ * <pre>
+ * TimeStampedData ::= SEQUENCE {
+ * version INTEGER { v1(1) },
+ * dataUri IA5String OPTIONAL,
+ * metaData MetaData OPTIONAL,
+ * content OCTET STRING OPTIONAL,
+ * temporalEvidence Evidence
+ * }
+ * </pre>
+ * @return
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+
+ if (dataUri != null)
+ {
+ v.add(dataUri);
+ }
+
+ if (metaData != null)
+ {
+ v.add(metaData);
+ }
+
+ if (content != null)
+ {
+ v.add(content);
+ }
+
+ v.add(temporalEvidence);
+
+ return new BERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/TimeStampedDataParser.java b/core/src/main/java/org/bouncycastle/asn1/cms/TimeStampedDataParser.java
new file mode 100644
index 00000000..0d050eb8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/TimeStampedDataParser.java
@@ -0,0 +1,127 @@
+package org.bouncycastle.asn1.cms;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1OctetStringParser;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1SequenceParser;
+import org.bouncycastle.asn1.BERSequence;
+import org.bouncycastle.asn1.DERIA5String;
+
+public class TimeStampedDataParser
+{
+ private ASN1Integer version;
+ private DERIA5String dataUri;
+ private MetaData metaData;
+ private ASN1OctetStringParser content;
+ private Evidence temporalEvidence;
+ private ASN1SequenceParser parser;
+
+ private TimeStampedDataParser(ASN1SequenceParser parser)
+ throws IOException
+ {
+ this.parser = parser;
+ this.version = ASN1Integer.getInstance(parser.readObject());
+
+ ASN1Encodable obj = parser.readObject();
+
+ if (obj instanceof DERIA5String)
+ {
+ this.dataUri = DERIA5String.getInstance(obj);
+ obj = parser.readObject();
+ }
+ if (obj instanceof MetaData || obj instanceof ASN1SequenceParser)
+ {
+ this.metaData = MetaData.getInstance(obj.toASN1Primitive());
+ obj = parser.readObject();
+ }
+ if (obj instanceof ASN1OctetStringParser)
+ {
+ this.content = (ASN1OctetStringParser)obj;
+ }
+ }
+
+ public static TimeStampedDataParser getInstance(Object obj)
+ throws IOException
+ {
+ if (obj instanceof ASN1Sequence)
+ {
+ return new TimeStampedDataParser(((ASN1Sequence)obj).parser());
+ }
+ if (obj instanceof ASN1SequenceParser)
+ {
+ return new TimeStampedDataParser((ASN1SequenceParser)obj);
+ }
+
+ return null;
+ }
+
+ public DERIA5String getDataUri()
+ {
+ return dataUri;
+ }
+
+ public MetaData getMetaData()
+ {
+ return metaData;
+ }
+
+ public ASN1OctetStringParser getContent()
+ {
+ return content;
+ }
+
+ public Evidence getTemporalEvidence()
+ throws IOException
+ {
+ if (temporalEvidence == null)
+ {
+ temporalEvidence = Evidence.getInstance(parser.readObject().toASN1Primitive());
+ }
+
+ return temporalEvidence;
+ }
+
+ /**
+ * <pre>
+ * TimeStampedData ::= SEQUENCE {
+ * version INTEGER { v1(1) },
+ * dataUri IA5String OPTIONAL,
+ * metaData MetaData OPTIONAL,
+ * content OCTET STRING OPTIONAL,
+ * temporalEvidence Evidence
+ * }
+ * </pre>
+ * @return
+ * @deprecated will be removed
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+
+ if (dataUri != null)
+ {
+ v.add(dataUri);
+ }
+
+ if (metaData != null)
+ {
+ v.add(metaData);
+ }
+
+ if (content != null)
+ {
+ v.add(content);
+ }
+
+ v.add(temporalEvidence);
+
+ return new BERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cms/ecc/MQVuserKeyingMaterial.java b/core/src/main/java/org/bouncycastle/asn1/cms/ecc/MQVuserKeyingMaterial.java
new file mode 100644
index 00000000..7beb6a4a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cms/ecc/MQVuserKeyingMaterial.java
@@ -0,0 +1,112 @@
+package org.bouncycastle.asn1.cms.ecc;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.cms.OriginatorPublicKey;
+
+public class MQVuserKeyingMaterial
+ extends ASN1Object
+{
+ private OriginatorPublicKey ephemeralPublicKey;
+ private ASN1OctetString addedukm;
+
+ public MQVuserKeyingMaterial(
+ OriginatorPublicKey ephemeralPublicKey,
+ ASN1OctetString addedukm)
+ {
+ // TODO Check ephemeralPublicKey not null
+
+ this.ephemeralPublicKey = ephemeralPublicKey;
+ this.addedukm = addedukm;
+ }
+
+ private MQVuserKeyingMaterial(
+ ASN1Sequence seq)
+ {
+ // TODO Check seq has either 1 or 2 elements
+
+ this.ephemeralPublicKey = OriginatorPublicKey.getInstance(
+ seq.getObjectAt(0));
+
+ if (seq.size() > 1)
+ {
+ this.addedukm = ASN1OctetString.getInstance(
+ (ASN1TaggedObject)seq.getObjectAt(1), true);
+ }
+ }
+
+ /**
+ * return an MQVuserKeyingMaterial 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.
+ * @throws IllegalArgumentException if the object held by the
+ * tagged object cannot be converted.
+ */
+ public static MQVuserKeyingMaterial getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * return an MQVuserKeyingMaterial object from the given object.
+ *
+ * @param obj the object we want converted.
+ * @throws IllegalArgumentException if the object cannot be converted.
+ */
+ public static MQVuserKeyingMaterial getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof MQVuserKeyingMaterial)
+ {
+ return (MQVuserKeyingMaterial)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new MQVuserKeyingMaterial((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid MQVuserKeyingMaterial: " + obj.getClass().getName());
+ }
+
+ public OriginatorPublicKey getEphemeralPublicKey()
+ {
+ return ephemeralPublicKey;
+ }
+
+ public ASN1OctetString getAddedukm()
+ {
+ return addedukm;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * MQVuserKeyingMaterial ::= SEQUENCE {
+ * ephemeralPublicKey OriginatorPublicKey,
+ * addedukm [0] EXPLICIT UserKeyingMaterial OPTIONAL }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(ephemeralPublicKey);
+
+ if (addedukm != null)
+ {
+ v.add(new DERTaggedObject(true, 0, addedukm));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/crmf/AttributeTypeAndValue.java b/core/src/main/java/org/bouncycastle/asn1/crmf/AttributeTypeAndValue.java
new file mode 100644
index 00000000..ec7d2831
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/crmf/AttributeTypeAndValue.java
@@ -0,0 +1,80 @@
+package org.bouncycastle.asn1.crmf;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class AttributeTypeAndValue
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier type;
+ private ASN1Encodable value;
+
+ private AttributeTypeAndValue(ASN1Sequence seq)
+ {
+ type = (ASN1ObjectIdentifier)seq.getObjectAt(0);
+ value = (ASN1Encodable)seq.getObjectAt(1);
+ }
+
+ public static AttributeTypeAndValue getInstance(Object o)
+ {
+ if (o instanceof AttributeTypeAndValue)
+ {
+ return (AttributeTypeAndValue)o;
+ }
+
+ if (o != null)
+ {
+ return new AttributeTypeAndValue(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public AttributeTypeAndValue(
+ String oid,
+ ASN1Encodable value)
+ {
+ this(new ASN1ObjectIdentifier(oid), value);
+ }
+
+ public AttributeTypeAndValue(
+ ASN1ObjectIdentifier type,
+ ASN1Encodable value)
+ {
+ this.type = type;
+ this.value = value;
+ }
+
+ public ASN1ObjectIdentifier getType()
+ {
+ return type;
+ }
+
+ public ASN1Encodable getValue()
+ {
+ return value;
+ }
+
+ /**
+ * <pre>
+ * AttributeTypeAndValue ::= SEQUENCE {
+ * type OBJECT IDENTIFIER,
+ * value ANY DEFINED BY type }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(type);
+ v.add(value);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/crmf/CRMFObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/crmf/CRMFObjectIdentifiers.java
new file mode 100644
index 00000000..c36084db
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/crmf/CRMFObjectIdentifiers.java
@@ -0,0 +1,21 @@
+package org.bouncycastle.asn1.crmf;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+
+public interface CRMFObjectIdentifiers
+{
+ static final ASN1ObjectIdentifier id_pkix = new ASN1ObjectIdentifier("1.3.6.1.5.5.7");
+
+ // arc for Internet X.509 PKI protocols and their components
+
+ static final ASN1ObjectIdentifier id_pkip = id_pkix.branch("5");
+
+ static final ASN1ObjectIdentifier id_regCtrl = id_pkip.branch("1");
+ static final ASN1ObjectIdentifier id_regCtrl_regToken = id_regCtrl.branch("1");
+ static final ASN1ObjectIdentifier id_regCtrl_authenticator = id_regCtrl.branch("2");
+ static final ASN1ObjectIdentifier id_regCtrl_pkiPublicationInfo = id_regCtrl.branch("3");
+ static final ASN1ObjectIdentifier id_regCtrl_pkiArchiveOptions = id_regCtrl.branch("4");
+
+ static final ASN1ObjectIdentifier id_ct_encKeyWithID = new ASN1ObjectIdentifier(PKCSObjectIdentifiers.id_ct + ".21");
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/crmf/CertId.java b/core/src/main/java/org/bouncycastle/asn1/crmf/CertId.java
new file mode 100644
index 00000000..bd54c119
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/crmf/CertId.java
@@ -0,0 +1,84 @@
+package org.bouncycastle.asn1.crmf;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.GeneralName;
+
+public class CertId
+ extends ASN1Object
+{
+ private GeneralName issuer;
+ private ASN1Integer serialNumber;
+
+ private CertId(ASN1Sequence seq)
+ {
+ issuer = GeneralName.getInstance(seq.getObjectAt(0));
+ serialNumber = ASN1Integer.getInstance(seq.getObjectAt(1));
+ }
+
+ public static CertId getInstance(Object o)
+ {
+ if (o instanceof CertId)
+ {
+ return (CertId)o;
+ }
+
+ if (o != null)
+ {
+ return new CertId(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public static CertId getInstance(ASN1TaggedObject obj, boolean isExplicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, isExplicit));
+ }
+
+ public CertId(GeneralName issuer, BigInteger serialNumber)
+ {
+ this(issuer, new ASN1Integer(serialNumber));
+ }
+
+ public CertId(GeneralName issuer, ASN1Integer serialNumber)
+ {
+ this.issuer = issuer;
+ this.serialNumber = serialNumber;
+ }
+
+ public GeneralName getIssuer()
+ {
+ return issuer;
+ }
+
+ public ASN1Integer getSerialNumber()
+ {
+ return serialNumber;
+ }
+
+ /**
+ * <pre>
+ * CertId ::= SEQUENCE {
+ * issuer GeneralName,
+ * serialNumber INTEGER }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(issuer);
+ v.add(serialNumber);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/crmf/CertReqMessages.java b/core/src/main/java/org/bouncycastle/asn1/crmf/CertReqMessages.java
new file mode 100644
index 00000000..aa48a189
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/crmf/CertReqMessages.java
@@ -0,0 +1,74 @@
+package org.bouncycastle.asn1.crmf;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class CertReqMessages
+ extends ASN1Object
+{
+ private ASN1Sequence content;
+
+ private CertReqMessages(ASN1Sequence seq)
+ {
+ content = seq;
+ }
+
+ public static CertReqMessages getInstance(Object o)
+ {
+ if (o instanceof CertReqMessages)
+ {
+ return (CertReqMessages)o;
+ }
+
+ if (o != null)
+ {
+ return new CertReqMessages(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public CertReqMessages(
+ CertReqMsg msg)
+ {
+ content = new DERSequence(msg);
+ }
+
+ public CertReqMessages(
+ CertReqMsg[] msgs)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ for (int i = 0; i < msgs.length; i++)
+ {
+ v.add(msgs[i]);
+ }
+ content = new DERSequence(v);
+ }
+
+ public CertReqMsg[] toCertReqMsgArray()
+ {
+ CertReqMsg[] result = new CertReqMsg[content.size()];
+
+ for (int i = 0; i != result.length; i++)
+ {
+ result[i] = CertReqMsg.getInstance(content.getObjectAt(i));
+ }
+
+ return result;
+ }
+
+ /**
+ * <pre>
+ * CertReqMessages ::= SEQUENCE SIZE (1..MAX) OF CertReqMsg
+ * </pre>
+ *
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return content;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/crmf/CertReqMsg.java b/core/src/main/java/org/bouncycastle/asn1/crmf/CertReqMsg.java
new file mode 100644
index 00000000..38936630
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/crmf/CertReqMsg.java
@@ -0,0 +1,145 @@
+package org.bouncycastle.asn1.crmf;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+public class CertReqMsg
+ extends ASN1Object
+{
+ private CertRequest certReq;
+ private ProofOfPossession pop;
+ private ASN1Sequence regInfo;
+
+ private CertReqMsg(ASN1Sequence seq)
+ {
+ Enumeration en = seq.getObjects();
+
+ certReq = CertRequest.getInstance(en.nextElement());
+ while (en.hasMoreElements())
+ {
+ Object o = en.nextElement();
+
+ if (o instanceof ASN1TaggedObject || o instanceof ProofOfPossession)
+ {
+ pop = ProofOfPossession.getInstance(o);
+ }
+ else
+ {
+ regInfo = ASN1Sequence.getInstance(o);
+ }
+ }
+ }
+
+ public static CertReqMsg getInstance(Object o)
+ {
+ if (o instanceof CertReqMsg)
+ {
+ return (CertReqMsg)o;
+ }
+ else if (o != null)
+ {
+ return new CertReqMsg(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ /**
+ * Creates a new CertReqMsg.
+ * @param certReq CertRequest
+ * @param pop may be null
+ * @param regInfo may be null
+ */
+ public CertReqMsg(
+ CertRequest certReq,
+ ProofOfPossession pop,
+ AttributeTypeAndValue[] regInfo)
+ {
+ if (certReq == null)
+ {
+ throw new IllegalArgumentException("'certReq' cannot be null");
+ }
+
+ this.certReq = certReq;
+ this.pop = pop;
+
+ if (regInfo != null)
+ {
+ this.regInfo = new DERSequence(regInfo);
+ }
+ }
+
+ public CertRequest getCertReq()
+ {
+ return certReq;
+ }
+
+
+ /**
+ * @deprecated use getPopo
+ */
+ public ProofOfPossession getPop()
+ {
+ return pop;
+ }
+
+
+ public ProofOfPossession getPopo()
+ {
+ return pop;
+ }
+
+ public AttributeTypeAndValue[] getRegInfo()
+ {
+ if (regInfo == null)
+ {
+ return null;
+ }
+
+ AttributeTypeAndValue[] results = new AttributeTypeAndValue[regInfo.size()];
+
+ for (int i = 0; i != results.length; i++)
+ {
+ results[i] = AttributeTypeAndValue.getInstance(regInfo.getObjectAt(i));
+ }
+
+ return results;
+ }
+
+ /**
+ * <pre>
+ * CertReqMsg ::= SEQUENCE {
+ * certReq CertRequest,
+ * popo ProofOfPossession OPTIONAL,
+ * -- content depends upon key type
+ * regInfo SEQUENCE SIZE(1..MAX) OF AttributeTypeAndValue OPTIONAL }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(certReq);
+
+ addOptional(v, pop);
+ addOptional(v, regInfo);
+
+ return new DERSequence(v);
+ }
+
+ private void addOptional(ASN1EncodableVector v, ASN1Encodable obj)
+ {
+ if (obj != null)
+ {
+ v.add(obj);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/crmf/CertRequest.java b/core/src/main/java/org/bouncycastle/asn1/crmf/CertRequest.java
new file mode 100644
index 00000000..70afe8ef
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/crmf/CertRequest.java
@@ -0,0 +1,97 @@
+package org.bouncycastle.asn1.crmf;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class CertRequest
+ extends ASN1Object
+{
+ private ASN1Integer certReqId;
+ private CertTemplate certTemplate;
+ private Controls controls;
+
+ private CertRequest(ASN1Sequence seq)
+ {
+ certReqId = new ASN1Integer(ASN1Integer.getInstance(seq.getObjectAt(0)).getValue());
+ certTemplate = CertTemplate.getInstance(seq.getObjectAt(1));
+ if (seq.size() > 2)
+ {
+ controls = Controls.getInstance(seq.getObjectAt(2));
+ }
+ }
+
+ public static CertRequest getInstance(Object o)
+ {
+ if (o instanceof CertRequest)
+ {
+ return (CertRequest)o;
+ }
+ else if (o != null)
+ {
+ return new CertRequest(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public CertRequest(
+ int certReqId,
+ CertTemplate certTemplate,
+ Controls controls)
+ {
+ this(new ASN1Integer(certReqId), certTemplate, controls);
+ }
+
+ public CertRequest(
+ ASN1Integer certReqId,
+ CertTemplate certTemplate,
+ Controls controls)
+ {
+ this.certReqId = certReqId;
+ this.certTemplate = certTemplate;
+ this.controls = controls;
+ }
+
+ public ASN1Integer getCertReqId()
+ {
+ return certReqId;
+ }
+
+ public CertTemplate getCertTemplate()
+ {
+ return certTemplate;
+ }
+
+ public Controls getControls()
+ {
+ return controls;
+ }
+
+ /**
+ * <pre>
+ * CertRequest ::= SEQUENCE {
+ * certReqId INTEGER, -- ID for matching request and reply
+ * certTemplate CertTemplate, -- Selected fields of cert to be issued
+ * controls Controls OPTIONAL } -- Attributes affecting issuance
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(certReqId);
+ v.add(certTemplate);
+
+ if (controls != null)
+ {
+ v.add(controls);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/crmf/CertTemplate.java b/core/src/main/java/org/bouncycastle/asn1/crmf/CertTemplate.java
new file mode 100644
index 00000000..73412e9a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/crmf/CertTemplate.java
@@ -0,0 +1,163 @@
+package org.bouncycastle.asn1.crmf;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+
+public class CertTemplate
+ extends ASN1Object
+{
+ private ASN1Sequence seq;
+
+ private ASN1Integer version;
+ private ASN1Integer serialNumber;
+ private AlgorithmIdentifier signingAlg;
+ private X500Name issuer;
+ private OptionalValidity validity;
+ private X500Name subject;
+ private SubjectPublicKeyInfo publicKey;
+ private DERBitString issuerUID;
+ private DERBitString subjectUID;
+ private Extensions extensions;
+
+ private CertTemplate(ASN1Sequence seq)
+ {
+ this.seq = seq;
+
+ Enumeration en = seq.getObjects();
+ while (en.hasMoreElements())
+ {
+ ASN1TaggedObject tObj = (ASN1TaggedObject)en.nextElement();
+
+ switch (tObj.getTagNo())
+ {
+ case 0:
+ version = ASN1Integer.getInstance(tObj, false);
+ break;
+ case 1:
+ serialNumber = ASN1Integer.getInstance(tObj, false);
+ break;
+ case 2:
+ signingAlg = AlgorithmIdentifier.getInstance(tObj, false);
+ break;
+ case 3:
+ issuer = X500Name.getInstance(tObj, true); // CHOICE
+ break;
+ case 4:
+ validity = OptionalValidity.getInstance(ASN1Sequence.getInstance(tObj, false));
+ break;
+ case 5:
+ subject = X500Name.getInstance(tObj, true); // CHOICE
+ break;
+ case 6:
+ publicKey = SubjectPublicKeyInfo.getInstance(tObj, false);
+ break;
+ case 7:
+ issuerUID = DERBitString.getInstance(tObj, false);
+ break;
+ case 8:
+ subjectUID = DERBitString.getInstance(tObj, false);
+ break;
+ case 9:
+ extensions = Extensions.getInstance(tObj, false);
+ break;
+ default:
+ throw new IllegalArgumentException("unknown tag: " + tObj.getTagNo());
+ }
+ }
+ }
+
+ public static CertTemplate getInstance(Object o)
+ {
+ if (o instanceof CertTemplate)
+ {
+ return (CertTemplate)o;
+ }
+ else if (o != null)
+ {
+ return new CertTemplate(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public int getVersion()
+ {
+ return version.getValue().intValue();
+ }
+
+ public ASN1Integer getSerialNumber()
+ {
+ return serialNumber;
+ }
+
+ public AlgorithmIdentifier getSigningAlg()
+ {
+ return signingAlg;
+ }
+
+ public X500Name getIssuer()
+ {
+ return issuer;
+ }
+
+ public OptionalValidity getValidity()
+ {
+ return validity;
+ }
+
+ public X500Name getSubject()
+ {
+ return subject;
+ }
+
+ public SubjectPublicKeyInfo getPublicKey()
+ {
+ return publicKey;
+ }
+
+ public DERBitString getIssuerUID()
+ {
+ return issuerUID;
+ }
+
+ public DERBitString getSubjectUID()
+ {
+ return subjectUID;
+ }
+
+ public Extensions getExtensions()
+ {
+ return extensions;
+ }
+
+ /**
+ * <pre>
+ * CertTemplate ::= SEQUENCE {
+ * version [0] Version OPTIONAL,
+ * serialNumber [1] INTEGER OPTIONAL,
+ * signingAlg [2] AlgorithmIdentifier OPTIONAL,
+ * issuer [3] Name OPTIONAL,
+ * validity [4] OptionalValidity OPTIONAL,
+ * subject [5] Name OPTIONAL,
+ * publicKey [6] SubjectPublicKeyInfo OPTIONAL,
+ * issuerUID [7] UniqueIdentifier OPTIONAL,
+ * subjectUID [8] UniqueIdentifier OPTIONAL,
+ * extensions [9] Extensions OPTIONAL }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return seq;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/crmf/CertTemplateBuilder.java b/core/src/main/java/org/bouncycastle/asn1/crmf/CertTemplateBuilder.java
new file mode 100644
index 00000000..be5cbe6a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/crmf/CertTemplateBuilder.java
@@ -0,0 +1,152 @@
+package org.bouncycastle.asn1.crmf;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x509.X509Extensions;
+
+public class CertTemplateBuilder
+{
+ private ASN1Integer version;
+ private ASN1Integer serialNumber;
+ private AlgorithmIdentifier signingAlg;
+ private X500Name issuer;
+ private OptionalValidity validity;
+ private X500Name subject;
+ private SubjectPublicKeyInfo publicKey;
+ private DERBitString issuerUID;
+ private DERBitString subjectUID;
+ private Extensions extensions;
+
+ /** Sets the X.509 version. Note: for X509v3, use 2 here. */
+ public CertTemplateBuilder setVersion(int ver)
+ {
+ version = new ASN1Integer(ver);
+
+ return this;
+ }
+
+ public CertTemplateBuilder setSerialNumber(ASN1Integer ser)
+ {
+ serialNumber = ser;
+
+ return this;
+ }
+
+ public CertTemplateBuilder setSigningAlg(AlgorithmIdentifier aid)
+ {
+ signingAlg = aid;
+
+ return this;
+ }
+
+ public CertTemplateBuilder setIssuer(X500Name name)
+ {
+ issuer = name;
+
+ return this;
+ }
+
+ public CertTemplateBuilder setValidity(OptionalValidity v)
+ {
+ validity = v;
+
+ return this;
+ }
+
+ public CertTemplateBuilder setSubject(X500Name name)
+ {
+ subject = name;
+
+ return this;
+ }
+
+ public CertTemplateBuilder setPublicKey(SubjectPublicKeyInfo spki)
+ {
+ publicKey = spki;
+
+ return this;
+ }
+
+ /** Sets the issuer unique ID (deprecated in X.509v3) */
+ public CertTemplateBuilder setIssuerUID(DERBitString uid)
+ {
+ issuerUID = uid;
+
+ return this;
+ }
+
+ /** Sets the subject unique ID (deprecated in X.509v3) */
+ public CertTemplateBuilder setSubjectUID(DERBitString uid)
+ {
+ subjectUID = uid;
+
+ return this;
+ }
+
+ /**
+ * @deprecated use method taking Extensions
+ * @param extens
+ * @return
+ */
+ public CertTemplateBuilder setExtensions(X509Extensions extens)
+ {
+ return setExtensions(Extensions.getInstance(extens));
+ }
+
+ public CertTemplateBuilder setExtensions(Extensions extens)
+ {
+ extensions = extens;
+
+ return this;
+ }
+
+ /**
+ * <pre>
+ * CertTemplate ::= SEQUENCE {
+ * version [0] Version OPTIONAL,
+ * serialNumber [1] INTEGER OPTIONAL,
+ * signingAlg [2] AlgorithmIdentifier OPTIONAL,
+ * issuer [3] Name OPTIONAL,
+ * validity [4] OptionalValidity OPTIONAL,
+ * subject [5] Name OPTIONAL,
+ * publicKey [6] SubjectPublicKeyInfo OPTIONAL,
+ * issuerUID [7] UniqueIdentifier OPTIONAL,
+ * subjectUID [8] UniqueIdentifier OPTIONAL,
+ * extensions [9] Extensions OPTIONAL }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public CertTemplate build()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ addOptional(v, 0, false, version);
+ addOptional(v, 1, false, serialNumber);
+ addOptional(v, 2, false, signingAlg);
+ addOptional(v, 3, true, issuer); // CHOICE
+ addOptional(v, 4, false, validity);
+ addOptional(v, 5, true, subject); // CHOICE
+ addOptional(v, 6, false, publicKey);
+ addOptional(v, 7, false, issuerUID);
+ addOptional(v, 8, false, subjectUID);
+ addOptional(v, 9, false, extensions);
+
+ return CertTemplate.getInstance(new DERSequence(v));
+ }
+
+ private void addOptional(ASN1EncodableVector v, int tagNo, boolean isExplicit, ASN1Encodable obj)
+ {
+ if (obj != null)
+ {
+ v.add(new DERTaggedObject(isExplicit, tagNo, obj));
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/crmf/Controls.java b/core/src/main/java/org/bouncycastle/asn1/crmf/Controls.java
new file mode 100644
index 00000000..2e188fe9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/crmf/Controls.java
@@ -0,0 +1,72 @@
+package org.bouncycastle.asn1.crmf;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class Controls
+ extends ASN1Object
+{
+ private ASN1Sequence content;
+
+ private Controls(ASN1Sequence seq)
+ {
+ content = seq;
+ }
+
+ public static Controls getInstance(Object o)
+ {
+ if (o instanceof Controls)
+ {
+ return (Controls)o;
+ }
+
+ if (o != null)
+ {
+ return new Controls(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public Controls(AttributeTypeAndValue atv)
+ {
+ content = new DERSequence(atv);
+ }
+
+ public Controls(AttributeTypeAndValue[] atvs)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ for (int i = 0; i < atvs.length; i++)
+ {
+ v.add(atvs[i]);
+ }
+ content = new DERSequence(v);
+ }
+
+ public AttributeTypeAndValue[] toAttributeTypeAndValueArray()
+ {
+ AttributeTypeAndValue[] result = new AttributeTypeAndValue[content.size()];
+
+ for (int i = 0; i != result.length; i++)
+ {
+ result[i] = AttributeTypeAndValue.getInstance(content.getObjectAt(i));
+ }
+
+ return result;
+ }
+
+ /**
+ * <pre>
+ * Controls ::= SEQUENCE SIZE(1..MAX) OF AttributeTypeAndValue
+ * </pre>
+ *
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return content;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/crmf/EncKeyWithID.java b/core/src/main/java/org/bouncycastle/asn1/crmf/EncKeyWithID.java
new file mode 100644
index 00000000..478a9185
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/crmf/EncKeyWithID.java
@@ -0,0 +1,117 @@
+package org.bouncycastle.asn1.crmf;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERUTF8String;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.GeneralName;
+
+public class EncKeyWithID
+ extends ASN1Object
+{
+ private final PrivateKeyInfo privKeyInfo;
+ private final ASN1Encodable identifier;
+
+ public static EncKeyWithID getInstance(Object o)
+ {
+ if (o instanceof EncKeyWithID)
+ {
+ return (EncKeyWithID)o;
+ }
+ else if (o != null)
+ {
+ return new EncKeyWithID(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ private EncKeyWithID(ASN1Sequence seq)
+ {
+ this.privKeyInfo = PrivateKeyInfo.getInstance(seq.getObjectAt(0));
+
+ if (seq.size() > 1)
+ {
+ if (!(seq.getObjectAt(1) instanceof DERUTF8String))
+ {
+ this.identifier = GeneralName.getInstance(seq.getObjectAt(1));
+ }
+ else
+ {
+ this.identifier = (ASN1Encodable)seq.getObjectAt(1);
+ }
+ }
+ else
+ {
+ this.identifier = null;
+ }
+ }
+
+ public EncKeyWithID(PrivateKeyInfo privKeyInfo)
+ {
+ this.privKeyInfo = privKeyInfo;
+ this.identifier = null;
+ }
+
+ public EncKeyWithID(PrivateKeyInfo privKeyInfo, DERUTF8String str)
+ {
+ this.privKeyInfo = privKeyInfo;
+ this.identifier = str;
+ }
+
+ public EncKeyWithID(PrivateKeyInfo privKeyInfo, GeneralName generalName)
+ {
+ this.privKeyInfo = privKeyInfo;
+ this.identifier = generalName;
+ }
+
+ public PrivateKeyInfo getPrivateKey()
+ {
+ return privKeyInfo;
+ }
+
+ public boolean hasIdentifier()
+ {
+ return identifier != null;
+ }
+
+ public boolean isIdentifierUTF8String()
+ {
+ return identifier instanceof DERUTF8String;
+ }
+
+ public ASN1Encodable getIdentifier()
+ {
+ return identifier;
+ }
+
+ /**
+ * <pre>
+ * EncKeyWithID ::= SEQUENCE {
+ * privateKey PrivateKeyInfo,
+ * identifier CHOICE {
+ * string UTF8String,
+ * generalName GeneralName
+ * } OPTIONAL
+ * }
+ * </pre>
+ * @return
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(privKeyInfo);
+
+ if (identifier != null)
+ {
+ v.add(identifier);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/crmf/EncryptedKey.java b/core/src/main/java/org/bouncycastle/asn1/crmf/EncryptedKey.java
new file mode 100644
index 00000000..10ae47b5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/crmf/EncryptedKey.java
@@ -0,0 +1,81 @@
+package org.bouncycastle.asn1.crmf;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.cms.EnvelopedData;
+
+public class EncryptedKey
+ extends ASN1Object
+ implements ASN1Choice
+{
+ private EnvelopedData envelopedData;
+ private EncryptedValue encryptedValue;
+
+ public static EncryptedKey getInstance(Object o)
+ {
+ if (o instanceof EncryptedKey)
+ {
+ return (EncryptedKey)o;
+ }
+ else if (o instanceof ASN1TaggedObject)
+ {
+ return new EncryptedKey(EnvelopedData.getInstance((ASN1TaggedObject)o, false));
+ }
+ else if (o instanceof EncryptedValue)
+ {
+ return new EncryptedKey((EncryptedValue)o);
+ }
+ else
+ {
+ return new EncryptedKey(EncryptedValue.getInstance(o));
+ }
+ }
+
+ public EncryptedKey(EnvelopedData envelopedData)
+ {
+ this.envelopedData = envelopedData;
+ }
+
+ public EncryptedKey(EncryptedValue encryptedValue)
+ {
+ this.encryptedValue = encryptedValue;
+ }
+
+ public boolean isEncryptedValue()
+ {
+ return encryptedValue != null;
+ }
+
+ public ASN1Encodable getValue()
+ {
+ if (encryptedValue != null)
+ {
+ return encryptedValue;
+ }
+
+ return envelopedData;
+ }
+
+ /**
+ * <pre>
+ * EncryptedKey ::= CHOICE {
+ * encryptedValue EncryptedValue, -- deprecated
+ * envelopedData [0] EnvelopedData }
+ * -- The encrypted private key MUST be placed in the envelopedData
+ * -- encryptedContentInfo encryptedContent OCTET STRING.
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ if (encryptedValue != null)
+ {
+ return encryptedValue.toASN1Primitive();
+ }
+
+ return new DERTaggedObject(false, 0, envelopedData);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/crmf/EncryptedValue.java b/core/src/main/java/org/bouncycastle/asn1/crmf/EncryptedValue.java
new file mode 100644
index 00000000..3aa54579
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/crmf/EncryptedValue.java
@@ -0,0 +1,164 @@
+package org.bouncycastle.asn1.crmf;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class EncryptedValue
+ extends ASN1Object
+{
+ private AlgorithmIdentifier intendedAlg;
+ private AlgorithmIdentifier symmAlg;
+ private DERBitString encSymmKey;
+ private AlgorithmIdentifier keyAlg;
+ private ASN1OctetString valueHint;
+ private DERBitString encValue;
+
+ private EncryptedValue(ASN1Sequence seq)
+ {
+ int index = 0;
+ while (seq.getObjectAt(index) instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject tObj = (ASN1TaggedObject)seq.getObjectAt(index);
+
+ switch (tObj.getTagNo())
+ {
+ case 0:
+ intendedAlg = AlgorithmIdentifier.getInstance(tObj, false);
+ break;
+ case 1:
+ symmAlg = AlgorithmIdentifier.getInstance(tObj, false);
+ break;
+ case 2:
+ encSymmKey = DERBitString.getInstance(tObj, false);
+ break;
+ case 3:
+ keyAlg = AlgorithmIdentifier.getInstance(tObj, false);
+ break;
+ case 4:
+ valueHint = ASN1OctetString.getInstance(tObj, false);
+ break;
+ }
+ index++;
+ }
+
+ encValue = DERBitString.getInstance(seq.getObjectAt(index));
+ }
+
+ public static EncryptedValue getInstance(Object o)
+ {
+ if (o instanceof EncryptedValue)
+ {
+ return (EncryptedValue)o;
+ }
+ else if (o != null)
+ {
+ return new EncryptedValue(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public EncryptedValue(
+ AlgorithmIdentifier intendedAlg,
+ AlgorithmIdentifier symmAlg,
+ DERBitString encSymmKey,
+ AlgorithmIdentifier keyAlg,
+ ASN1OctetString valueHint,
+ DERBitString encValue)
+ {
+ if (encValue == null)
+ {
+ throw new IllegalArgumentException("'encValue' cannot be null");
+ }
+
+ this.intendedAlg = intendedAlg;
+ this.symmAlg = symmAlg;
+ this.encSymmKey = encSymmKey;
+ this.keyAlg = keyAlg;
+ this.valueHint = valueHint;
+ this.encValue = encValue;
+ }
+
+ public AlgorithmIdentifier getIntendedAlg()
+ {
+ return intendedAlg;
+ }
+
+ public AlgorithmIdentifier getSymmAlg()
+ {
+ return symmAlg;
+ }
+
+ public DERBitString getEncSymmKey()
+ {
+ return encSymmKey;
+ }
+
+ public AlgorithmIdentifier getKeyAlg()
+ {
+ return keyAlg;
+ }
+
+ public ASN1OctetString getValueHint()
+ {
+ return valueHint;
+ }
+
+ public DERBitString getEncValue()
+ {
+ return encValue;
+ }
+
+ /**
+ * <pre>
+ * EncryptedValue ::= SEQUENCE {
+ * intendedAlg [0] AlgorithmIdentifier OPTIONAL,
+ * -- the intended algorithm for which the value will be used
+ * symmAlg [1] AlgorithmIdentifier OPTIONAL,
+ * -- the symmetric algorithm used to encrypt the value
+ * encSymmKey [2] BIT STRING OPTIONAL,
+ * -- the (encrypted) symmetric key used to encrypt the value
+ * keyAlg [3] AlgorithmIdentifier OPTIONAL,
+ * -- algorithm used to encrypt the symmetric key
+ * valueHint [4] OCTET STRING OPTIONAL,
+ * -- a brief description or identifier of the encValue content
+ * -- (may be meaningful only to the sending entity, and used only
+ * -- if EncryptedValue might be re-examined by the sending entity
+ * -- in the future)
+ * encValue BIT STRING }
+ * -- the encrypted value itself
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ addOptional(v, 0, intendedAlg);
+ addOptional(v, 1, symmAlg);
+ addOptional(v, 2, encSymmKey);
+ addOptional(v, 3, keyAlg);
+ addOptional(v, 4, valueHint);
+
+ v.add(encValue);
+
+ return new DERSequence(v);
+ }
+
+ private void addOptional(ASN1EncodableVector v, int tagNo, ASN1Encodable obj)
+ {
+ if (obj != null)
+ {
+ v.add(new DERTaggedObject(false, tagNo, obj));
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/crmf/OptionalValidity.java b/core/src/main/java/org/bouncycastle/asn1/crmf/OptionalValidity.java
new file mode 100644
index 00000000..9174b5f7
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/crmf/OptionalValidity.java
@@ -0,0 +1,98 @@
+package org.bouncycastle.asn1.crmf;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.Time;
+
+public class OptionalValidity
+ extends ASN1Object
+{
+ private Time notBefore;
+ private Time notAfter;
+
+ private OptionalValidity(ASN1Sequence seq)
+ {
+ Enumeration en = seq.getObjects();
+ while (en.hasMoreElements())
+ {
+ ASN1TaggedObject tObj = (ASN1TaggedObject)en.nextElement();
+
+ if (tObj.getTagNo() == 0)
+ {
+ notBefore = Time.getInstance(tObj, true);
+ }
+ else
+ {
+ notAfter = Time.getInstance(tObj, true);
+ }
+ }
+ }
+
+ public static OptionalValidity getInstance(Object o)
+ {
+ if (o instanceof OptionalValidity)
+ {
+ return (OptionalValidity)o;
+ }
+
+ if (o != null)
+ {
+ return new OptionalValidity(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public OptionalValidity(Time notBefore, Time notAfter)
+ {
+ if (notBefore == null && notAfter == null)
+ {
+ throw new IllegalArgumentException("at least one of notBefore/notAfter must not be null.");
+ }
+
+ this.notBefore = notBefore;
+ this.notAfter = notAfter;
+ }
+
+ public Time getNotBefore()
+ {
+ return notBefore;
+ }
+
+ public Time getNotAfter()
+ {
+ return notAfter;
+ }
+
+ /**
+ * <pre>
+ * OptionalValidity ::= SEQUENCE {
+ * notBefore [0] Time OPTIONAL,
+ * notAfter [1] Time OPTIONAL } --at least one MUST be present
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (notBefore != null)
+ {
+ v.add(new DERTaggedObject(true, 0, notBefore));
+ }
+
+ if (notAfter != null)
+ {
+ v.add(new DERTaggedObject(true, 1, notAfter));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/crmf/PKIArchiveOptions.java b/core/src/main/java/org/bouncycastle/asn1/crmf/PKIArchiveOptions.java
new file mode 100644
index 00000000..46e0e44c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/crmf/PKIArchiveOptions.java
@@ -0,0 +1,116 @@
+package org.bouncycastle.asn1.crmf;
+
+import org.bouncycastle.asn1.ASN1Boolean;
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class PKIArchiveOptions
+ extends ASN1Object
+ implements ASN1Choice
+{
+ public static final int encryptedPrivKey = 0;
+ public static final int keyGenParameters = 1;
+ public static final int archiveRemGenPrivKey = 2;
+
+ private ASN1Encodable value;
+
+ public static PKIArchiveOptions getInstance(Object o)
+ {
+ if (o == null || o instanceof PKIArchiveOptions)
+ {
+ return (PKIArchiveOptions)o;
+ }
+ else if (o instanceof ASN1TaggedObject)
+ {
+ return new PKIArchiveOptions((ASN1TaggedObject)o);
+ }
+
+ throw new IllegalArgumentException("unknown object: " + o);
+ }
+
+ private PKIArchiveOptions(ASN1TaggedObject tagged)
+ {
+ switch (tagged.getTagNo())
+ {
+ case encryptedPrivKey:
+ value = EncryptedKey.getInstance(tagged.getObject());
+ break;
+ case keyGenParameters:
+ value = ASN1OctetString.getInstance(tagged, false);
+ break;
+ case archiveRemGenPrivKey:
+ value = ASN1Boolean.getInstance(tagged, false);
+ break;
+ default:
+ throw new IllegalArgumentException("unknown tag number: " + tagged.getTagNo());
+ }
+ }
+
+ public PKIArchiveOptions(EncryptedKey encKey)
+ {
+ this.value = encKey;
+ }
+
+ public PKIArchiveOptions(ASN1OctetString keyGenParameters)
+ {
+ this.value = keyGenParameters;
+ }
+
+ public PKIArchiveOptions(boolean archiveRemGenPrivKey)
+ {
+ this.value = ASN1Boolean.getInstance(archiveRemGenPrivKey);
+ }
+
+ public int getType()
+ {
+ if (value instanceof EncryptedKey)
+ {
+ return encryptedPrivKey;
+ }
+
+ if (value instanceof ASN1OctetString)
+ {
+ return keyGenParameters;
+ }
+
+ return archiveRemGenPrivKey;
+ }
+
+ public ASN1Encodable getValue()
+ {
+ return value;
+ }
+
+ /**
+ * <pre>
+ * PKIArchiveOptions ::= CHOICE {
+ * encryptedPrivKey [0] EncryptedKey,
+ * -- the actual value of the private key
+ * keyGenParameters [1] KeyGenParameters,
+ * -- parameters which allow the private key to be re-generated
+ * archiveRemGenPrivKey [2] BOOLEAN }
+ * -- set to TRUE if sender wishes receiver to archive the private
+ * -- key of a key pair that the receiver generates in response to
+ * -- this request; set to FALSE if no archival is desired.
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ if (value instanceof EncryptedKey)
+ {
+ return new DERTaggedObject(true, encryptedPrivKey, value); // choice
+ }
+
+ if (value instanceof ASN1OctetString)
+ {
+ return new DERTaggedObject(false, keyGenParameters, value);
+ }
+
+ return new DERTaggedObject(false, archiveRemGenPrivKey, value);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/crmf/PKIPublicationInfo.java b/core/src/main/java/org/bouncycastle/asn1/crmf/PKIPublicationInfo.java
new file mode 100644
index 00000000..dba0422d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/crmf/PKIPublicationInfo.java
@@ -0,0 +1,81 @@
+package org.bouncycastle.asn1.crmf;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class PKIPublicationInfo
+ extends ASN1Object
+{
+ private ASN1Integer action;
+ private ASN1Sequence pubInfos;
+
+ private PKIPublicationInfo(ASN1Sequence seq)
+ {
+ action = ASN1Integer.getInstance(seq.getObjectAt(0));
+ pubInfos = ASN1Sequence.getInstance(seq.getObjectAt(1));
+ }
+
+ public static PKIPublicationInfo getInstance(Object o)
+ {
+ if (o instanceof PKIPublicationInfo)
+ {
+ return (PKIPublicationInfo)o;
+ }
+
+ if (o != null)
+ {
+ return new PKIPublicationInfo(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public ASN1Integer getAction()
+ {
+ return action;
+ }
+
+ public SinglePubInfo[] getPubInfos()
+ {
+ if (pubInfos == null)
+ {
+ return null;
+ }
+
+ SinglePubInfo[] results = new SinglePubInfo[pubInfos.size()];
+
+ for (int i = 0; i != results.length; i++)
+ {
+ results[i] = SinglePubInfo.getInstance(pubInfos.getObjectAt(i));
+ }
+
+ return results;
+ }
+
+ /**
+ * <pre>
+ * PKIPublicationInfo ::= SEQUENCE {
+ * action INTEGER {
+ * dontPublish (0),
+ * pleasePublish (1) },
+ * pubInfos SEQUENCE SIZE (1..MAX) OF SinglePubInfo OPTIONAL }
+ * -- pubInfos MUST NOT be present if action is "dontPublish"
+ * -- (if action is "pleasePublish" and pubInfos is omitted,
+ * -- "dontCare" is assumed)
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(action);
+ v.add(pubInfos);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/crmf/PKMACValue.java b/core/src/main/java/org/bouncycastle/asn1/crmf/PKMACValue.java
new file mode 100644
index 00000000..ebbf2dcf
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/crmf/PKMACValue.java
@@ -0,0 +1,104 @@
+package org.bouncycastle.asn1.crmf;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.cmp.CMPObjectIdentifiers;
+import org.bouncycastle.asn1.cmp.PBMParameter;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * Password-based MAC value for use with POPOSigningKeyInput.
+ */
+public class PKMACValue
+ extends ASN1Object
+{
+ private AlgorithmIdentifier algId;
+ private DERBitString value;
+
+ private PKMACValue(ASN1Sequence seq)
+ {
+ algId = AlgorithmIdentifier.getInstance(seq.getObjectAt(0));
+ value = DERBitString.getInstance(seq.getObjectAt(1));
+ }
+
+ public static PKMACValue getInstance(Object o)
+ {
+ if (o instanceof PKMACValue)
+ {
+ return (PKMACValue)o;
+ }
+
+ if (o != null)
+ {
+ return new PKMACValue(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public static PKMACValue getInstance(ASN1TaggedObject obj, boolean isExplicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, isExplicit));
+ }
+
+ /**
+ * Creates a new PKMACValue.
+ * @param params parameters for password-based MAC
+ * @param value MAC of the DER-encoded SubjectPublicKeyInfo
+ */
+ public PKMACValue(
+ PBMParameter params,
+ DERBitString value)
+ {
+ this(new AlgorithmIdentifier(
+ CMPObjectIdentifiers.passwordBasedMac, params), value);
+ }
+
+ /**
+ * Creates a new PKMACValue.
+ * @param aid CMPObjectIdentifiers.passwordBasedMAC, with PBMParameter
+ * @param value MAC of the DER-encoded SubjectPublicKeyInfo
+ */
+ public PKMACValue(
+ AlgorithmIdentifier aid,
+ DERBitString value)
+ {
+ this.algId = aid;
+ this.value = value;
+ }
+
+ public AlgorithmIdentifier getAlgId()
+ {
+ return algId;
+ }
+
+ public DERBitString getValue()
+ {
+ return value;
+ }
+
+ /**
+ * <pre>
+ * PKMACValue ::= SEQUENCE {
+ * algId AlgorithmIdentifier,
+ * -- algorithm value shall be PasswordBasedMac 1.2.840.113533.7.66.13
+ * -- parameter value is PBMParameter
+ * value BIT STRING }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(algId);
+ v.add(value);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/crmf/POPOPrivKey.java b/core/src/main/java/org/bouncycastle/asn1/crmf/POPOPrivKey.java
new file mode 100644
index 00000000..8c9db8ac
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/crmf/POPOPrivKey.java
@@ -0,0 +1,104 @@
+package org.bouncycastle.asn1.crmf;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.cms.EnvelopedData;
+
+public class POPOPrivKey
+ extends ASN1Object
+ implements ASN1Choice
+{
+ public static final int thisMessage = 0;
+ public static final int subsequentMessage = 1;
+ public static final int dhMAC = 2;
+ public static final int agreeMAC = 3;
+ public static final int encryptedKey = 4;
+
+ private int tagNo;
+ private ASN1Encodable obj;
+
+ private POPOPrivKey(ASN1TaggedObject obj)
+ {
+ this.tagNo = obj.getTagNo();
+
+ switch (tagNo)
+ {
+ case thisMessage:
+ this.obj = DERBitString.getInstance(obj, false);
+ break;
+ case subsequentMessage:
+ this.obj = SubsequentMessage.valueOf(ASN1Integer.getInstance(obj, false).getValue().intValue());
+ break;
+ case dhMAC:
+ this.obj = DERBitString.getInstance(obj, false);
+ break;
+ case agreeMAC:
+ this.obj = PKMACValue.getInstance(obj, false);
+ break;
+ case encryptedKey:
+ this.obj = EnvelopedData.getInstance(obj, false);
+ break;
+ default:
+ throw new IllegalArgumentException("unknown tag in POPOPrivKey");
+ }
+ }
+
+ public static POPOPrivKey getInstance(Object obj)
+ {
+ if (obj instanceof POPOPrivKey)
+ {
+ return (POPOPrivKey)obj;
+ }
+ if (obj != null)
+ {
+ return new POPOPrivKey(ASN1TaggedObject.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static POPOPrivKey getInstance(ASN1TaggedObject obj, boolean explicit)
+ {
+ return getInstance(ASN1TaggedObject.getInstance(obj, explicit));
+ }
+
+ public POPOPrivKey(SubsequentMessage msg)
+ {
+ this.tagNo = subsequentMessage;
+ this.obj = msg;
+ }
+
+ public int getType()
+ {
+ return tagNo;
+ }
+
+ public ASN1Encodable getValue()
+ {
+ return obj;
+ }
+
+ /**
+ * <pre>
+ * POPOPrivKey ::= CHOICE {
+ * thisMessage [0] BIT STRING, -- Deprecated
+ * -- possession is proven in this message (which contains the private
+ * -- key itself (encrypted for the CA))
+ * subsequentMessage [1] SubsequentMessage,
+ * -- possession will be proven in a subsequent message
+ * dhMAC [2] BIT STRING, -- Deprecated
+ * agreeMAC [3] PKMACValue,
+ * encryptedKey [4] EnvelopedData }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return new DERTaggedObject(false, tagNo, obj);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/crmf/POPOSigningKey.java b/core/src/main/java/org/bouncycastle/asn1/crmf/POPOSigningKey.java
new file mode 100644
index 00000000..43dd05b0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/crmf/POPOSigningKey.java
@@ -0,0 +1,127 @@
+package org.bouncycastle.asn1.crmf;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class POPOSigningKey
+ extends ASN1Object
+{
+ private POPOSigningKeyInput poposkInput;
+ private AlgorithmIdentifier algorithmIdentifier;
+ private DERBitString signature;
+
+ private POPOSigningKey(ASN1Sequence seq)
+ {
+ int index = 0;
+
+ if (seq.getObjectAt(index) instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject tagObj
+ = (ASN1TaggedObject)seq.getObjectAt(index++);
+ if (tagObj.getTagNo() != 0)
+ {
+ throw new IllegalArgumentException(
+ "Unknown POPOSigningKeyInput tag: " + tagObj.getTagNo());
+ }
+ poposkInput = POPOSigningKeyInput.getInstance(tagObj.getObject());
+ }
+ algorithmIdentifier = AlgorithmIdentifier.getInstance(seq.getObjectAt(index++));
+ signature = DERBitString.getInstance(seq.getObjectAt(index));
+ }
+
+ public static POPOSigningKey getInstance(Object o)
+ {
+ if (o instanceof POPOSigningKey)
+ {
+ return (POPOSigningKey)o;
+ }
+
+ if (o != null)
+ {
+ return new POPOSigningKey(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public static POPOSigningKey getInstance(ASN1TaggedObject obj, boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * Creates a new Proof of Possession object for a signing key.
+ *
+ * @param poposkIn the POPOSigningKeyInput structure, or null if the
+ * CertTemplate includes both subject and publicKey values.
+ * @param aid the AlgorithmIdentifier used to sign the proof of possession.
+ * @param signature a signature over the DER-encoded value of poposkIn,
+ * or the DER-encoded value of certReq if poposkIn is null.
+ */
+ public POPOSigningKey(
+ POPOSigningKeyInput poposkIn,
+ AlgorithmIdentifier aid,
+ DERBitString signature)
+ {
+ this.poposkInput = poposkIn;
+ this.algorithmIdentifier = aid;
+ this.signature = signature;
+ }
+
+ public POPOSigningKeyInput getPoposkInput()
+ {
+ return poposkInput;
+ }
+
+ public AlgorithmIdentifier getAlgorithmIdentifier()
+ {
+ return algorithmIdentifier;
+ }
+
+ public DERBitString getSignature()
+ {
+ return signature;
+ }
+
+ /**
+ * <pre>
+ * POPOSigningKey ::= SEQUENCE {
+ * poposkInput [0] POPOSigningKeyInput OPTIONAL,
+ * algorithmIdentifier AlgorithmIdentifier,
+ * signature BIT STRING }
+ * -- The signature (using "algorithmIdentifier") is on the
+ * -- DER-encoded value of poposkInput. NOTE: If the CertReqMsg
+ * -- certReq CertTemplate contains the subject and publicKey values,
+ * -- then poposkInput MUST be omitted and the signature MUST be
+ * -- computed on the DER-encoded value of CertReqMsg certReq. If
+ * -- the CertReqMsg certReq CertTemplate does not contain the public
+ * -- key and subject values, then poposkInput MUST be present and
+ * -- MUST be signed. This strategy ensures that the public key is
+ * -- not present in both the poposkInput and CertReqMsg certReq
+ * -- CertTemplate fields.
+ * </pre>
+ *
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (poposkInput != null)
+ {
+ v.add(new DERTaggedObject(false, 0, poposkInput));
+ }
+
+ v.add(algorithmIdentifier);
+ v.add(signature);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/crmf/POPOSigningKeyInput.java b/core/src/main/java/org/bouncycastle/asn1/crmf/POPOSigningKeyInput.java
new file mode 100644
index 00000000..54d828e8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/crmf/POPOSigningKeyInput.java
@@ -0,0 +1,134 @@
+package org.bouncycastle.asn1.crmf;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+
+public class POPOSigningKeyInput
+ extends ASN1Object
+{
+ private GeneralName sender;
+ private PKMACValue publicKeyMAC;
+ private SubjectPublicKeyInfo publicKey;
+
+ private POPOSigningKeyInput(ASN1Sequence seq)
+ {
+ ASN1Encodable authInfo = (ASN1Encodable)seq.getObjectAt(0);
+
+ if (authInfo instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject tagObj = (ASN1TaggedObject)authInfo;
+ if (tagObj.getTagNo() != 0)
+ {
+ throw new IllegalArgumentException(
+ "Unknown authInfo tag: " + tagObj.getTagNo());
+ }
+ sender = GeneralName.getInstance(tagObj.getObject());
+ }
+ else
+ {
+ publicKeyMAC = PKMACValue.getInstance(authInfo);
+ }
+
+ publicKey = SubjectPublicKeyInfo.getInstance(seq.getObjectAt(1));
+ }
+
+ public static POPOSigningKeyInput getInstance(Object o)
+ {
+ if (o instanceof POPOSigningKeyInput)
+ {
+ return (POPOSigningKeyInput)o;
+ }
+
+ if (o != null)
+ {
+ return new POPOSigningKeyInput(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ /**
+ * Creates a new POPOSigningKeyInput with sender name as authInfo.
+ */
+ public POPOSigningKeyInput(
+ GeneralName sender,
+ SubjectPublicKeyInfo spki)
+ {
+ this.sender = sender;
+ this.publicKey = spki;
+ }
+
+ /**
+ * Creates a new POPOSigningKeyInput using password-based MAC.
+ */
+ public POPOSigningKeyInput(
+ PKMACValue pkmac,
+ SubjectPublicKeyInfo spki)
+ {
+ this.publicKeyMAC = pkmac;
+ this.publicKey = spki;
+ }
+
+ /**
+ * Returns the sender field, or null if authInfo is publicKeyMAC
+ */
+ public GeneralName getSender()
+ {
+ return sender;
+ }
+
+ /**
+ * Returns the publicKeyMAC field, or null if authInfo is sender
+ */
+ public PKMACValue getPublicKeyMAC()
+ {
+ return publicKeyMAC;
+ }
+
+ public SubjectPublicKeyInfo getPublicKey()
+ {
+ return publicKey;
+ }
+
+ /**
+ * <pre>
+ * POPOSigningKeyInput ::= SEQUENCE {
+ * authInfo CHOICE {
+ * sender [0] GeneralName,
+ * -- used only if an authenticated identity has been
+ * -- established for the sender (e.g., a DN from a
+ * -- previously-issued and currently-valid certificate
+ * publicKeyMAC PKMACValue },
+ * -- used if no authenticated GeneralName currently exists for
+ * -- the sender; publicKeyMAC contains a password-based MAC
+ * -- on the DER-encoded value of publicKey
+ * publicKey SubjectPublicKeyInfo } -- from CertTemplate
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (sender != null)
+ {
+ v.add(new DERTaggedObject(false, 0, sender));
+ }
+ else
+ {
+ v.add(publicKeyMAC);
+ }
+
+ v.add(publicKey);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/crmf/ProofOfPossession.java b/core/src/main/java/org/bouncycastle/asn1/crmf/ProofOfPossession.java
new file mode 100644
index 00000000..8ff23423
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/crmf/ProofOfPossession.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.asn1.crmf;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class ProofOfPossession
+ extends ASN1Object
+ implements ASN1Choice
+{
+ public static final int TYPE_RA_VERIFIED = 0;
+ public static final int TYPE_SIGNING_KEY = 1;
+ public static final int TYPE_KEY_ENCIPHERMENT = 2;
+ public static final int TYPE_KEY_AGREEMENT = 3;
+
+ private int tagNo;
+ private ASN1Encodable obj;
+
+ private ProofOfPossession(ASN1TaggedObject tagged)
+ {
+ tagNo = tagged.getTagNo();
+ switch (tagNo)
+ {
+ case 0:
+ obj = DERNull.INSTANCE;
+ break;
+ case 1:
+ obj = POPOSigningKey.getInstance(tagged, false);
+ break;
+ case 2:
+ case 3:
+ obj = POPOPrivKey.getInstance(tagged, true);
+ break;
+ default:
+ throw new IllegalArgumentException("unknown tag: " + tagNo);
+ }
+ }
+
+ public static ProofOfPossession getInstance(Object o)
+ {
+ if (o == null || o instanceof ProofOfPossession)
+ {
+ return (ProofOfPossession)o;
+ }
+
+ if (o instanceof ASN1TaggedObject)
+ {
+ return new ProofOfPossession((ASN1TaggedObject)o);
+ }
+
+ throw new IllegalArgumentException("Invalid object: " + o.getClass().getName());
+ }
+
+ /** Creates a ProofOfPossession with type raVerified. */
+ public ProofOfPossession()
+ {
+ tagNo = TYPE_RA_VERIFIED;
+ obj = DERNull.INSTANCE;
+ }
+
+ /** Creates a ProofOfPossession for a signing key. */
+ public ProofOfPossession(POPOSigningKey poposk)
+ {
+ tagNo = TYPE_SIGNING_KEY;
+ obj = poposk;
+ }
+
+ /**
+ * Creates a ProofOfPossession for key encipherment or agreement.
+ * @param type one of TYPE_KEY_ENCIPHERMENT or TYPE_KEY_AGREEMENT
+ */
+ public ProofOfPossession(int type, POPOPrivKey privkey)
+ {
+ tagNo = type;
+ obj = privkey;
+ }
+
+ public int getType()
+ {
+ return tagNo;
+ }
+
+ public ASN1Encodable getObject()
+ {
+ return obj;
+ }
+
+ /**
+ * <pre>
+ * ProofOfPossession ::= CHOICE {
+ * raVerified [0] NULL,
+ * -- used if the RA has already verified that the requester is in
+ * -- possession of the private key
+ * signature [1] POPOSigningKey,
+ * keyEncipherment [2] POPOPrivKey,
+ * keyAgreement [3] POPOPrivKey }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return new DERTaggedObject(false, tagNo, obj);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/crmf/SinglePubInfo.java b/core/src/main/java/org/bouncycastle/asn1/crmf/SinglePubInfo.java
new file mode 100644
index 00000000..0237b3a6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/crmf/SinglePubInfo.java
@@ -0,0 +1,72 @@
+package org.bouncycastle.asn1.crmf;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.GeneralName;
+
+public class SinglePubInfo
+ extends ASN1Object
+{
+ private ASN1Integer pubMethod;
+ private GeneralName pubLocation;
+
+ private SinglePubInfo(ASN1Sequence seq)
+ {
+ pubMethod = ASN1Integer.getInstance(seq.getObjectAt(0));
+
+ if (seq.size() == 2)
+ {
+ pubLocation = GeneralName.getInstance(seq.getObjectAt(1));
+ }
+ }
+
+ public static SinglePubInfo getInstance(Object o)
+ {
+ if (o instanceof SinglePubInfo)
+ {
+ return (SinglePubInfo)o;
+ }
+
+ if (o != null)
+ {
+ return new SinglePubInfo(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public GeneralName getPubLocation()
+ {
+ return pubLocation;
+ }
+
+ /**
+ * <pre>
+ * SinglePubInfo ::= SEQUENCE {
+ * pubMethod INTEGER {
+ * dontCare (0),
+ * x500 (1),
+ * web (2),
+ * ldap (3) },
+ * pubLocation GeneralName OPTIONAL }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(pubMethod);
+
+ if (pubLocation != null)
+ {
+ v.add(pubLocation);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/crmf/SubsequentMessage.java b/core/src/main/java/org/bouncycastle/asn1/crmf/SubsequentMessage.java
new file mode 100644
index 00000000..46917228
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/crmf/SubsequentMessage.java
@@ -0,0 +1,29 @@
+package org.bouncycastle.asn1.crmf;
+
+import org.bouncycastle.asn1.ASN1Integer;
+
+public class SubsequentMessage
+ extends ASN1Integer
+{
+ public static final SubsequentMessage encrCert = new SubsequentMessage(0);
+ public static final SubsequentMessage challengeResp = new SubsequentMessage(1);
+
+ private SubsequentMessage(int value)
+ {
+ super(value);
+ }
+
+ public static SubsequentMessage valueOf(int value)
+ {
+ if (value == 0)
+ {
+ return encrCert;
+ }
+ if (value == 1)
+ {
+ return challengeResp;
+ }
+
+ throw new IllegalArgumentException("unknown value: " + value);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java
new file mode 100644
index 00000000..fb5ae79b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.asn1.cryptopro;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface CryptoProObjectIdentifiers
+{
+ // GOST Algorithms OBJECT IDENTIFIERS :
+ // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2)}
+ static final ASN1ObjectIdentifier GOST_id = new ASN1ObjectIdentifier("1.2.643.2.2");
+
+ static final ASN1ObjectIdentifier gostR3411 = GOST_id.branch("9");
+ static final ASN1ObjectIdentifier gostR3411Hmac = GOST_id.branch("10");
+
+ static final ASN1ObjectIdentifier gostR28147_cbc = new ASN1ObjectIdentifier(GOST_id+".21");
+
+ static final ASN1ObjectIdentifier id_Gost28147_89_CryptoPro_A_ParamSet = GOST_id.branch("31.1");
+
+ static final ASN1ObjectIdentifier gostR3410_94 = new ASN1ObjectIdentifier(GOST_id+".20");
+ static final ASN1ObjectIdentifier gostR3410_2001 = new ASN1ObjectIdentifier(GOST_id+".19");
+ static final ASN1ObjectIdentifier gostR3411_94_with_gostR3410_94 = new ASN1ObjectIdentifier(GOST_id+".4");
+ static final ASN1ObjectIdentifier gostR3411_94_with_gostR3410_2001 = new ASN1ObjectIdentifier(GOST_id+".3");
+
+ // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) hashes(30) }
+ static final ASN1ObjectIdentifier gostR3411_94_CryptoProParamSet = new ASN1ObjectIdentifier(GOST_id+".30.1");
+
+ // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) signs(32) }
+ static final ASN1ObjectIdentifier gostR3410_94_CryptoPro_A = new ASN1ObjectIdentifier(GOST_id+".32.2");
+ static final ASN1ObjectIdentifier gostR3410_94_CryptoPro_B = new ASN1ObjectIdentifier(GOST_id+".32.3");
+ static final ASN1ObjectIdentifier gostR3410_94_CryptoPro_C = new ASN1ObjectIdentifier(GOST_id+".32.4");
+ static final ASN1ObjectIdentifier gostR3410_94_CryptoPro_D = new ASN1ObjectIdentifier(GOST_id+".32.5");
+
+ // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) exchanges(33) }
+ static final ASN1ObjectIdentifier gostR3410_94_CryptoPro_XchA = new ASN1ObjectIdentifier(GOST_id+".33.1");
+ static final ASN1ObjectIdentifier gostR3410_94_CryptoPro_XchB = new ASN1ObjectIdentifier(GOST_id+".33.2");
+ static final ASN1ObjectIdentifier gostR3410_94_CryptoPro_XchC = new ASN1ObjectIdentifier(GOST_id+".33.3");
+
+ //{ iso(1) member-body(2)ru(643) rans(2) cryptopro(2) ecc-signs(35) }
+ static final ASN1ObjectIdentifier gostR3410_2001_CryptoPro_A = new ASN1ObjectIdentifier(GOST_id+".35.1");
+ static final ASN1ObjectIdentifier gostR3410_2001_CryptoPro_B = new ASN1ObjectIdentifier(GOST_id+".35.2");
+ static final ASN1ObjectIdentifier gostR3410_2001_CryptoPro_C = new ASN1ObjectIdentifier(GOST_id+".35.3");
+
+ // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) ecc-exchanges(36) }
+ static final ASN1ObjectIdentifier gostR3410_2001_CryptoPro_XchA = new ASN1ObjectIdentifier(GOST_id+".36.0");
+ static final ASN1ObjectIdentifier gostR3410_2001_CryptoPro_XchB = new ASN1ObjectIdentifier(GOST_id+".36.1");
+
+ static final ASN1ObjectIdentifier gost_ElSgDH3410_default = new ASN1ObjectIdentifier(GOST_id+".36.0");
+ static final ASN1ObjectIdentifier gost_ElSgDH3410_1 = new ASN1ObjectIdentifier(GOST_id+".36.1");
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cryptopro/ECGOST3410NamedCurves.java b/core/src/main/java/org/bouncycastle/asn1/cryptopro/ECGOST3410NamedCurves.java
new file mode 100644
index 00000000..e2035051
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cryptopro/ECGOST3410NamedCurves.java
@@ -0,0 +1,168 @@
+package org.bouncycastle.asn1.cryptopro;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+
+/**
+ * table of the available named parameters for GOST 3410-2001.
+ */
+public class ECGOST3410NamedCurves
+{
+ static final Hashtable objIds = new Hashtable();
+ static final Hashtable params = new Hashtable();
+ static final Hashtable names = new Hashtable();
+
+ static
+ {
+ BigInteger mod_p = new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639319");
+ BigInteger mod_q = new BigInteger("115792089237316195423570985008687907853073762908499243225378155805079068850323");
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ mod_p, // p
+ new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639316"), // a
+ new BigInteger("166")); // b
+
+ ECDomainParameters ecParams = new ECDomainParameters(
+ curve,
+ new ECPoint.Fp(curve,
+ new ECFieldElement.Fp(curve.getQ(),new BigInteger("1")), // x
+ new ECFieldElement.Fp(curve.getQ(),new BigInteger("64033881142927202683649881450433473985931760268884941288852745803908878638612"))), // y
+ mod_q);
+
+ params.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_A, ecParams);
+
+ mod_p = new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639319");
+ mod_q = new BigInteger("115792089237316195423570985008687907853073762908499243225378155805079068850323");
+
+ curve = new ECCurve.Fp(
+ mod_p, // p
+ new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639316"),
+ new BigInteger("166"));
+
+ ecParams = new ECDomainParameters(
+ curve,
+ new ECPoint.Fp(curve,
+ new ECFieldElement.Fp(curve.getQ(),new BigInteger("1")), // x
+ new ECFieldElement.Fp(curve.getQ(),new BigInteger("64033881142927202683649881450433473985931760268884941288852745803908878638612"))), // y
+ mod_q);
+
+ params.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchA, ecParams);
+
+ mod_p = new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564823193"); //p
+ mod_q = new BigInteger("57896044618658097711785492504343953927102133160255826820068844496087732066703"); //q
+
+ curve = new ECCurve.Fp(
+ mod_p, // p
+ new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564823190"), // a
+ new BigInteger("28091019353058090096996979000309560759124368558014865957655842872397301267595")); // b
+
+ ecParams = new ECDomainParameters(
+ curve,
+ new ECPoint.Fp(curve,
+ new ECFieldElement.Fp(mod_p,new BigInteger("1")), // x
+ new ECFieldElement.Fp(mod_p,new BigInteger("28792665814854611296992347458380284135028636778229113005756334730996303888124"))), // y
+ mod_q); // q
+
+ params.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_B, ecParams);
+
+ mod_p = new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502619");
+ mod_q = new BigInteger("70390085352083305199547718019018437840920882647164081035322601458352298396601");
+
+ curve = new ECCurve.Fp(
+ mod_p, // p
+ new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502616"),
+ new BigInteger("32858"));
+
+ ecParams = new ECDomainParameters(
+ curve,
+ new ECPoint.Fp(curve,
+ new ECFieldElement.Fp(mod_p,new BigInteger("0")),
+ new ECFieldElement.Fp(mod_p,new BigInteger("29818893917731240733471273240314769927240550812383695689146495261604565990247"))),
+ mod_q);
+
+ params.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchB, ecParams);
+
+ mod_p = new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502619"); //p
+ mod_q = new BigInteger("70390085352083305199547718019018437840920882647164081035322601458352298396601"); //q
+ curve = new ECCurve.Fp(
+ mod_p, // p
+ new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502616"), // a
+ new BigInteger("32858")); // b
+
+ ecParams = new ECDomainParameters(
+ curve,
+ new ECPoint.Fp(curve,
+ new ECFieldElement.Fp(mod_p,new BigInteger("0")), // x
+ new ECFieldElement.Fp(mod_p,new BigInteger("29818893917731240733471273240314769927240550812383695689146495261604565990247"))), // y
+ mod_q); // q
+
+ params.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_C, ecParams);
+
+ objIds.put("GostR3410-2001-CryptoPro-A", CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_A);
+ objIds.put("GostR3410-2001-CryptoPro-B", CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_B);
+ objIds.put("GostR3410-2001-CryptoPro-C", CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_C);
+ objIds.put("GostR3410-2001-CryptoPro-XchA", CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchA);
+ objIds.put("GostR3410-2001-CryptoPro-XchB", CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchB);
+
+ names.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_A, "GostR3410-2001-CryptoPro-A");
+ names.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_B, "GostR3410-2001-CryptoPro-B");
+ names.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_C, "GostR3410-2001-CryptoPro-C");
+ names.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchA, "GostR3410-2001-CryptoPro-XchA");
+ names.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchB, "GostR3410-2001-CryptoPro-XchB");
+ }
+
+ /**
+ * return the ECDomainParameters object for the given OID, null if it
+ * isn't present.
+ *
+ * @param oid an object identifier representing a named parameters, if present.
+ */
+ public static ECDomainParameters getByOID(
+ ASN1ObjectIdentifier oid)
+ {
+ return (ECDomainParameters)params.get(oid);
+ }
+
+ /**
+ * returns an enumeration containing the name strings for parameters
+ * contained in this structure.
+ */
+ public static Enumeration getNames()
+ {
+ return objIds.keys();
+ }
+
+ public static ECDomainParameters getByName(
+ String name)
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)objIds.get(name);
+
+ if (oid != null)
+ {
+ return (ECDomainParameters)params.get(oid);
+ }
+
+ return null;
+ }
+
+ /**
+ * return the named curve name represented by the given object identifier.
+ */
+ public static String getName(
+ ASN1ObjectIdentifier oid)
+ {
+ return (String)names.get(oid);
+ }
+
+ public static ASN1ObjectIdentifier getOID(String name)
+ {
+ return (ASN1ObjectIdentifier)objIds.get(name);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cryptopro/ECGOST3410ParamSetParameters.java b/core/src/main/java/org/bouncycastle/asn1/cryptopro/ECGOST3410ParamSetParameters.java
new file mode 100644
index 00000000..189eabdc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cryptopro/ECGOST3410ParamSetParameters.java
@@ -0,0 +1,99 @@
+package org.bouncycastle.asn1.cryptopro;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+public class ECGOST3410ParamSetParameters
+ extends ASN1Object
+{
+ ASN1Integer p, q, a, b, x, y;
+
+ public static ECGOST3410ParamSetParameters getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static ECGOST3410ParamSetParameters getInstance(
+ Object obj)
+ {
+ if(obj == null || obj instanceof ECGOST3410ParamSetParameters)
+ {
+ return (ECGOST3410ParamSetParameters)obj;
+ }
+
+ if(obj instanceof ASN1Sequence)
+ {
+ return new ECGOST3410ParamSetParameters((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid GOST3410Parameter: " + obj.getClass().getName());
+ }
+
+ public ECGOST3410ParamSetParameters(
+ BigInteger a,
+ BigInteger b,
+ BigInteger p,
+ BigInteger q,
+ int x,
+ BigInteger y)
+ {
+ this.a = new ASN1Integer(a);
+ this.b = new ASN1Integer(b);
+ this.p = new ASN1Integer(p);
+ this.q = new ASN1Integer(q);
+ this.x = new ASN1Integer(x);
+ this.y = new ASN1Integer(y);
+ }
+
+ public ECGOST3410ParamSetParameters(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ a = (ASN1Integer)e.nextElement();
+ b = (ASN1Integer)e.nextElement();
+ p = (ASN1Integer)e.nextElement();
+ q = (ASN1Integer)e.nextElement();
+ x = (ASN1Integer)e.nextElement();
+ y = (ASN1Integer)e.nextElement();
+ }
+
+ public BigInteger getP()
+ {
+ return p.getPositiveValue();
+ }
+
+ public BigInteger getQ()
+ {
+ return q.getPositiveValue();
+ }
+
+ public BigInteger getA()
+ {
+ return a.getPositiveValue();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(a);
+ v.add(b);
+ v.add(p);
+ v.add(q);
+ v.add(x);
+ v.add(y);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cryptopro/GOST28147Parameters.java b/core/src/main/java/org/bouncycastle/asn1/cryptopro/GOST28147Parameters.java
new file mode 100644
index 00000000..a0459c19
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cryptopro/GOST28147Parameters.java
@@ -0,0 +1,72 @@
+package org.bouncycastle.asn1.cryptopro;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+public class GOST28147Parameters
+ extends ASN1Object
+{
+ ASN1OctetString iv;
+ ASN1ObjectIdentifier paramSet;
+
+ public static GOST28147Parameters getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static GOST28147Parameters getInstance(
+ Object obj)
+ {
+ if(obj == null || obj instanceof GOST28147Parameters)
+ {
+ return (GOST28147Parameters)obj;
+ }
+
+ if(obj instanceof ASN1Sequence)
+ {
+ return new GOST28147Parameters((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid GOST3410Parameter: " + obj.getClass().getName());
+ }
+
+ public GOST28147Parameters(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ iv = (ASN1OctetString)e.nextElement();
+ paramSet = (ASN1ObjectIdentifier)e.nextElement();
+ }
+
+ /**
+ * <pre>
+ * Gost28147-89-Parameters ::=
+ * SEQUENCE {
+ * iv Gost28147-89-IV,
+ * encryptionParamSet OBJECT IDENTIFIER
+ * }
+ *
+ * Gost28147-89-IV ::= OCTET STRING (SIZE (8))
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(iv);
+ v.add(paramSet);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410NamedParameters.java b/core/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410NamedParameters.java
new file mode 100644
index 00000000..6c398b5d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410NamedParameters.java
@@ -0,0 +1,116 @@
+package org.bouncycastle.asn1.cryptopro;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+/**
+ * table of the available named parameters for GOST 3410-94.
+ */
+public class GOST3410NamedParameters
+{
+ static final Hashtable objIds = new Hashtable();
+ static final Hashtable params = new Hashtable();
+ static final Hashtable names = new Hashtable();
+
+ static private GOST3410ParamSetParameters cryptoProA = new GOST3410ParamSetParameters(
+ 1024,
+ new BigInteger("127021248288932417465907042777176443525787653508916535812817507265705031260985098497423188333483401180925999995120988934130659205614996724254121049274349357074920312769561451689224110579311248812610229678534638401693520013288995000362260684222750813532307004517341633685004541062586971416883686778842537820383"),
+ new BigInteger("68363196144955700784444165611827252895102170888761442055095051287550314083023"),
+ new BigInteger("100997906755055304772081815535925224869841082572053457874823515875577147990529272777244152852699298796483356699682842027972896052747173175480590485607134746852141928680912561502802222185647539190902656116367847270145019066794290930185446216399730872221732889830323194097355403213400972588322876850946740663962")
+// validationAlgorithm {
+// algorithm
+// id-GostR3410-94-bBis,
+// parameters
+// GostR3410-94-ValidationBisParameters: {
+// x0 1376285941,
+// c 3996757427
+// }
+// }
+
+ );
+
+ static private GOST3410ParamSetParameters cryptoProB = new GOST3410ParamSetParameters(
+ 1024,
+ new BigInteger("139454871199115825601409655107690713107041707059928031797758001454375765357722984094124368522288239833039114681648076688236921220737322672160740747771700911134550432053804647694904686120113087816240740184800477047157336662926249423571248823968542221753660143391485680840520336859458494803187341288580489525163"),
+ new BigInteger("79885141663410976897627118935756323747307951916507639758300472692338873533959"),
+ new BigInteger("42941826148615804143873447737955502392672345968607143066798112994089471231420027060385216699563848719957657284814898909770759462613437669456364882730370838934791080835932647976778601915343474400961034231316672578686920482194932878633360203384797092684342247621055760235016132614780652761028509445403338652341")
+// validationAlgorithm {
+// algorithm
+// id-GostR3410-94-bBis,
+// parameters
+// GostR3410-94-ValidationBisParameters: {
+// x0 1536654555,
+// c 1855361757,
+// d 14408629386140014567655
+//4902939282056547857802241461782996702017713059974755104394739915140
+//6115284791024439062735788342744854120601660303926203867703556828005
+//8957203818114895398976594425537561271800850306
+// }
+// }
+//}
+ );
+
+ static private GOST3410ParamSetParameters cryptoProXchA = new GOST3410ParamSetParameters(
+ 1024,
+ new BigInteger("142011741597563481196368286022318089743276138395243738762872573441927459393512718973631166078467600360848946623567625795282774719212241929071046134208380636394084512691828894000571524625445295769349356752728956831541775441763139384457191755096847107846595662547942312293338483924514339614727760681880609734239"),
+ new BigInteger("91771529896554605945588149018382750217296858393520724172743325725474374979801"),
+ new BigInteger("133531813272720673433859519948319001217942375967847486899482359599369642528734712461590403327731821410328012529253871914788598993103310567744136196364803064721377826656898686468463277710150809401182608770201615324990468332931294920912776241137878030224355746606283971659376426832674269780880061631528163475887")
+ );
+
+ static
+ {
+ params.put(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_A, cryptoProA);
+ params.put(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_B, cryptoProB);
+// params.put(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_C, cryptoProC);
+// params.put(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_D, cryptoProD);
+ params.put(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_XchA, cryptoProXchA);
+// params.put(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_XchB, cryptoProXchA);
+// params.put(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_XchC, cryptoProXchA);
+
+ objIds.put("GostR3410-94-CryptoPro-A", CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_A);
+ objIds.put("GostR3410-94-CryptoPro-B", CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_B);
+ objIds.put("GostR3410-94-CryptoPro-XchA", CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_XchA);
+ }
+
+ /**
+ * return the GOST3410ParamSetParameters object for the given OID, null if it
+ * isn't present.
+ *
+ * @param oid an object identifier representing a named parameters, if present.
+ */
+ public static GOST3410ParamSetParameters getByOID(
+ ASN1ObjectIdentifier oid)
+ {
+ return (GOST3410ParamSetParameters)params.get(oid);
+ }
+
+ /**
+ * returns an enumeration containing the name strings for parameters
+ * contained in this structure.
+ */
+ public static Enumeration getNames()
+ {
+ return objIds.keys();
+ }
+
+ public static GOST3410ParamSetParameters getByName(
+ String name)
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)objIds.get(name);
+
+ if (oid != null)
+ {
+ return (GOST3410ParamSetParameters)params.get(oid);
+ }
+
+ return null;
+ }
+
+ public static ASN1ObjectIdentifier getOID(String name)
+ {
+ return (ASN1ObjectIdentifier)objIds.get(name);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410ParamSetParameters.java b/core/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410ParamSetParameters.java
new file mode 100644
index 00000000..35e9b735
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410ParamSetParameters.java
@@ -0,0 +1,105 @@
+package org.bouncycastle.asn1.cryptopro;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+public class GOST3410ParamSetParameters
+ extends ASN1Object
+{
+ int keySize;
+ ASN1Integer p, q, a;
+
+ public static GOST3410ParamSetParameters getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static GOST3410ParamSetParameters getInstance(
+ Object obj)
+ {
+ if(obj == null || obj instanceof GOST3410ParamSetParameters)
+ {
+ return (GOST3410ParamSetParameters)obj;
+ }
+
+ if(obj instanceof ASN1Sequence)
+ {
+ return new GOST3410ParamSetParameters((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid GOST3410Parameter: " + obj.getClass().getName());
+ }
+
+ public GOST3410ParamSetParameters(
+ int keySize,
+ BigInteger p,
+ BigInteger q,
+ BigInteger a)
+ {
+ this.keySize = keySize;
+ this.p = new ASN1Integer(p);
+ this.q = new ASN1Integer(q);
+ this.a = new ASN1Integer(a);
+ }
+
+ public GOST3410ParamSetParameters(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ keySize = ((ASN1Integer)e.nextElement()).getValue().intValue();
+ p = (ASN1Integer)e.nextElement();
+ q = (ASN1Integer)e.nextElement();
+ a = (ASN1Integer)e.nextElement();
+ }
+
+ /**
+ * @deprecated use getKeySize
+ */
+ public int getLKeySize()
+ {
+ return keySize;
+ }
+
+ public int getKeySize()
+ {
+ return keySize;
+ }
+
+ public BigInteger getP()
+ {
+ return p.getPositiveValue();
+ }
+
+ public BigInteger getQ()
+ {
+ return q.getPositiveValue();
+ }
+
+ public BigInteger getA()
+ {
+ return a.getPositiveValue();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new ASN1Integer(keySize));
+ v.add(p);
+ v.add(q);
+ v.add(a);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410PublicKeyAlgParameters.java b/core/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410PublicKeyAlgParameters.java
new file mode 100644
index 00000000..0307f50f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410PublicKeyAlgParameters.java
@@ -0,0 +1,101 @@
+package org.bouncycastle.asn1.cryptopro;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+public class GOST3410PublicKeyAlgParameters
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier publicKeyParamSet;
+ private ASN1ObjectIdentifier digestParamSet;
+ private ASN1ObjectIdentifier encryptionParamSet;
+
+ public static GOST3410PublicKeyAlgParameters getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static GOST3410PublicKeyAlgParameters getInstance(
+ Object obj)
+ {
+ if (obj instanceof GOST3410PublicKeyAlgParameters)
+ {
+ return (GOST3410PublicKeyAlgParameters)obj;
+ }
+
+ if(obj != null)
+ {
+ return new GOST3410PublicKeyAlgParameters(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public GOST3410PublicKeyAlgParameters(
+ ASN1ObjectIdentifier publicKeyParamSet,
+ ASN1ObjectIdentifier digestParamSet)
+ {
+ this.publicKeyParamSet = publicKeyParamSet;
+ this.digestParamSet = digestParamSet;
+ this.encryptionParamSet = null;
+ }
+
+ public GOST3410PublicKeyAlgParameters(
+ ASN1ObjectIdentifier publicKeyParamSet,
+ ASN1ObjectIdentifier digestParamSet,
+ ASN1ObjectIdentifier encryptionParamSet)
+ {
+ this.publicKeyParamSet = publicKeyParamSet;
+ this.digestParamSet = digestParamSet;
+ this.encryptionParamSet = encryptionParamSet;
+ }
+
+ public GOST3410PublicKeyAlgParameters(
+ ASN1Sequence seq)
+ {
+ this.publicKeyParamSet = (ASN1ObjectIdentifier)seq.getObjectAt(0);
+ this.digestParamSet = (ASN1ObjectIdentifier)seq.getObjectAt(1);
+
+ if (seq.size() > 2)
+ {
+ this.encryptionParamSet = (ASN1ObjectIdentifier)seq.getObjectAt(2);
+ }
+ }
+
+ public ASN1ObjectIdentifier getPublicKeyParamSet()
+ {
+ return publicKeyParamSet;
+ }
+
+ public ASN1ObjectIdentifier getDigestParamSet()
+ {
+ return digestParamSet;
+ }
+
+ public ASN1ObjectIdentifier getEncryptionParamSet()
+ {
+ return encryptionParamSet;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(publicKeyParamSet);
+ v.add(digestParamSet);
+
+ if (encryptionParamSet != null)
+ {
+ v.add(encryptionParamSet);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/dvcs/CertEtcToken.java b/core/src/main/java/org/bouncycastle/asn1/dvcs/CertEtcToken.java
new file mode 100644
index 00000000..3f69c52f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/dvcs/CertEtcToken.java
@@ -0,0 +1,171 @@
+package org.bouncycastle.asn1.dvcs;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.cmp.PKIStatusInfo;
+import org.bouncycastle.asn1.cms.ContentInfo;
+import org.bouncycastle.asn1.ess.ESSCertID;
+import org.bouncycastle.asn1.ocsp.CertID;
+import org.bouncycastle.asn1.ocsp.CertStatus;
+import org.bouncycastle.asn1.ocsp.OCSPResponse;
+import org.bouncycastle.asn1.smime.SMIMECapabilities;
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.asn1.x509.CertificateList;
+import org.bouncycastle.asn1.x509.Extension;
+
+/**
+ * <pre>
+ * CertEtcToken ::= CHOICE {
+ * certificate [0] IMPLICIT Certificate ,
+ * esscertid [1] ESSCertId ,
+ * pkistatus [2] IMPLICIT PKIStatusInfo ,
+ * assertion [3] ContentInfo ,
+ * crl [4] IMPLICIT CertificateList,
+ * ocspcertstatus [5] CertStatus,
+ * oscpcertid [6] IMPLICIT CertId ,
+ * oscpresponse [7] IMPLICIT OCSPResponse,
+ * capabilities [8] SMIMECapabilities,
+ * extension Extension
+ * }
+ * </pre>
+ */
+public class CertEtcToken
+ extends ASN1Object
+ implements ASN1Choice
+{
+ public static final int TAG_CERTIFICATE = 0;
+ public static final int TAG_ESSCERTID = 1;
+ public static final int TAG_PKISTATUS = 2;
+ public static final int TAG_ASSERTION = 3;
+ public static final int TAG_CRL = 4;
+ public static final int TAG_OCSPCERTSTATUS = 5;
+ public static final int TAG_OCSPCERTID = 6;
+ public static final int TAG_OCSPRESPONSE = 7;
+ public static final int TAG_CAPABILITIES = 8;
+
+ private static final boolean[] explicit = new boolean[]
+ {
+ false, true, false, true, false, true, false, false, true
+ };
+
+ private int tagNo;
+ private ASN1Encodable value;
+ private Extension extension;
+
+ public CertEtcToken(int tagNo, ASN1Encodable value)
+ {
+ this.tagNo = tagNo;
+ this.value = value;
+ }
+
+ public CertEtcToken(Extension extension)
+ {
+ this.tagNo = -1;
+ this.extension = extension;
+ }
+
+ private CertEtcToken(ASN1TaggedObject choice)
+ {
+ this.tagNo = choice.getTagNo();
+
+ switch (tagNo)
+ {
+ case TAG_CERTIFICATE:
+ value = Certificate.getInstance(choice, false);
+ break;
+ case TAG_ESSCERTID:
+ value = ESSCertID.getInstance(choice.getObject());
+ break;
+ case TAG_PKISTATUS:
+ value = PKIStatusInfo.getInstance(choice, false);
+ break;
+ case TAG_ASSERTION:
+ value = ContentInfo.getInstance(choice.getObject());
+ break;
+ case TAG_CRL:
+ value = CertificateList.getInstance(choice, false);
+ break;
+ case TAG_OCSPCERTSTATUS:
+ value = CertStatus.getInstance(choice.getObject());
+ break;
+ case TAG_OCSPCERTID:
+ value = CertID.getInstance(choice, false);
+ break;
+ case TAG_OCSPRESPONSE:
+ value = OCSPResponse.getInstance(choice, false);
+ break;
+ case TAG_CAPABILITIES:
+ value = SMIMECapabilities.getInstance(choice.getObject());
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown tag: " + tagNo);
+ }
+ }
+
+ public static CertEtcToken getInstance(Object obj)
+ {
+ if (obj instanceof CertEtcToken)
+ {
+ return (CertEtcToken)obj;
+ }
+ else if (obj instanceof ASN1TaggedObject)
+ {
+ return new CertEtcToken((ASN1TaggedObject)obj);
+ }
+ else if (obj != null)
+ {
+ return new CertEtcToken(Extension.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ if (extension == null)
+ {
+ return new DERTaggedObject(explicit[tagNo], tagNo, value);
+ }
+ else
+ {
+ return extension.toASN1Primitive();
+ }
+ }
+
+ public int getTagNo()
+ {
+ return tagNo;
+ }
+
+ public ASN1Encodable getValue()
+ {
+ return value;
+ }
+
+ public Extension getExtension()
+ {
+ return extension;
+ }
+
+ public String toString()
+ {
+ return "CertEtcToken {\n" + value + "}\n";
+ }
+
+ public static CertEtcToken[] arrayFromSequence(ASN1Sequence seq)
+ {
+ CertEtcToken[] tmp = new CertEtcToken[seq.size()];
+
+ for (int i = 0; i != tmp.length; i++)
+ {
+ tmp[i] = CertEtcToken.getInstance(seq.getObjectAt(i));
+ }
+
+ return tmp;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSCertInfo.java b/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSCertInfo.java
new file mode 100644
index 00000000..b64b31c4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSCertInfo.java
@@ -0,0 +1,302 @@
+package org.bouncycastle.asn1.dvcs;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.cmp.PKIStatusInfo;
+import org.bouncycastle.asn1.x509.DigestInfo;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.PolicyInformation;
+
+/**
+ * <pre>
+ * DVCSCertInfo::= SEQUENCE {
+ * version Integer DEFAULT 1 ,
+ * dvReqInfo DVCSRequestInformation,
+ * messageImprint DigestInfo,
+ * serialNumber Integer,
+ * responseTime DVCSTime,
+ * dvStatus [0] PKIStatusInfo OPTIONAL,
+ * policy [1] PolicyInformation OPTIONAL,
+ * reqSignature [2] SignerInfos OPTIONAL,
+ * certs [3] SEQUENCE SIZE (1..MAX) OF
+ * TargetEtcChain OPTIONAL,
+ * extensions Extensions OPTIONAL
+ * }
+ * </pre>
+ */
+
+public class DVCSCertInfo
+ extends ASN1Object
+{
+
+ private int version = DEFAULT_VERSION;
+ private DVCSRequestInformation dvReqInfo;
+ private DigestInfo messageImprint;
+ private ASN1Integer serialNumber;
+ private DVCSTime responseTime;
+ private PKIStatusInfo dvStatus;
+ private PolicyInformation policy;
+ private ASN1Set reqSignature;
+ private ASN1Sequence certs;
+ private Extensions extensions;
+
+ private static final int DEFAULT_VERSION = 1;
+ private static final int TAG_DV_STATUS = 0;
+ private static final int TAG_POLICY = 1;
+ private static final int TAG_REQ_SIGNATURE = 2;
+ private static final int TAG_CERTS = 3;
+
+ public DVCSCertInfo(
+ DVCSRequestInformation dvReqInfo,
+ DigestInfo messageImprint,
+ ASN1Integer serialNumber,
+ DVCSTime responseTime)
+ {
+ this.dvReqInfo = dvReqInfo;
+ this.messageImprint = messageImprint;
+ this.serialNumber = serialNumber;
+ this.responseTime = responseTime;
+ }
+
+ private DVCSCertInfo(ASN1Sequence seq)
+ {
+ int i = 0;
+ ASN1Encodable x = seq.getObjectAt(i++);
+ try
+ {
+ ASN1Integer encVersion = ASN1Integer.getInstance(x);
+ this.version = encVersion.getValue().intValue();
+ x = seq.getObjectAt(i++);
+ }
+ catch (IllegalArgumentException e)
+ {
+ }
+
+ this.dvReqInfo = DVCSRequestInformation.getInstance(x);
+ x = seq.getObjectAt(i++);
+ this.messageImprint = DigestInfo.getInstance(x);
+ x = seq.getObjectAt(i++);
+ this.serialNumber = ASN1Integer.getInstance(x);
+ x = seq.getObjectAt(i++);
+ this.responseTime = DVCSTime.getInstance(x);
+
+ while (i < seq.size())
+ {
+
+ x = seq.getObjectAt(i++);
+
+ try
+ {
+ ASN1TaggedObject t = ASN1TaggedObject.getInstance(x);
+ int tagNo = t.getTagNo();
+
+ switch (tagNo)
+ {
+ case TAG_DV_STATUS:
+ this.dvStatus = PKIStatusInfo.getInstance(t, false);
+ break;
+ case TAG_POLICY:
+ this.policy = PolicyInformation.getInstance(ASN1Sequence.getInstance(t, false));
+ break;
+ case TAG_REQ_SIGNATURE:
+ this.reqSignature = ASN1Set.getInstance(t, false);
+ break;
+ case TAG_CERTS:
+ this.certs = ASN1Sequence.getInstance(t, false);
+ break;
+ }
+
+ continue;
+
+ }
+ catch (IllegalArgumentException e)
+ {
+ }
+
+ try
+ {
+ this.extensions = Extensions.getInstance(x);
+ }
+ catch (IllegalArgumentException e)
+ {
+ }
+
+ }
+
+ }
+
+ public static DVCSCertInfo getInstance(Object obj)
+ {
+ if (obj instanceof DVCSCertInfo)
+ {
+ return (DVCSCertInfo)obj;
+ }
+ else if (obj != null)
+ {
+ return new DVCSCertInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static DVCSCertInfo getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (version != DEFAULT_VERSION)
+ {
+ v.add(new ASN1Integer(version));
+ }
+ v.add(dvReqInfo);
+ v.add(messageImprint);
+ v.add(serialNumber);
+ v.add(responseTime);
+ if (dvStatus != null)
+ {
+ v.add(new DERTaggedObject(false, TAG_DV_STATUS, dvStatus));
+ }
+ if (policy != null)
+ {
+ v.add(new DERTaggedObject(false, TAG_POLICY, policy));
+ }
+ if (reqSignature != null)
+ {
+ v.add(new DERTaggedObject(false, TAG_REQ_SIGNATURE, reqSignature));
+ }
+ if (certs != null)
+ {
+ v.add(new DERTaggedObject(false, TAG_CERTS, certs));
+ }
+ if (extensions != null)
+ {
+ v.add(extensions);
+ }
+
+ return new DERSequence(v);
+ }
+
+ public String toString()
+ {
+ StringBuffer s = new StringBuffer();
+
+ s.append("DVCSCertInfo {\n");
+
+ if (version != DEFAULT_VERSION)
+ {
+ s.append("version: " + version + "\n");
+ }
+ s.append("dvReqInfo: " + dvReqInfo + "\n");
+ s.append("messageImprint: " + messageImprint + "\n");
+ s.append("serialNumber: " + serialNumber + "\n");
+ s.append("responseTime: " + responseTime + "\n");
+ if (dvStatus != null)
+ {
+ s.append("dvStatus: " + dvStatus + "\n");
+ }
+ if (policy != null)
+ {
+ s.append("policy: " + policy + "\n");
+ }
+ if (reqSignature != null)
+ {
+ s.append("reqSignature: " + reqSignature + "\n");
+ }
+ if (certs != null)
+ {
+ s.append("certs: " + certs + "\n");
+ }
+ if (extensions != null)
+ {
+ s.append("extensions: " + extensions + "\n");
+ }
+
+ s.append("}\n");
+ return s.toString();
+ }
+
+ public int getVersion()
+ {
+ return version;
+ }
+
+ private void setVersion(int version)
+ {
+ this.version = version;
+ }
+
+ public DVCSRequestInformation getDvReqInfo()
+ {
+ return dvReqInfo;
+ }
+
+ private void setDvReqInfo(DVCSRequestInformation dvReqInfo)
+ {
+ this.dvReqInfo = dvReqInfo;
+ }
+
+ public DigestInfo getMessageImprint()
+ {
+ return messageImprint;
+ }
+
+ private void setMessageImprint(DigestInfo messageImprint)
+ {
+ this.messageImprint = messageImprint;
+ }
+
+ public ASN1Integer getSerialNumber()
+ {
+ return serialNumber;
+ }
+
+ public DVCSTime getResponseTime()
+ {
+ return responseTime;
+ }
+
+ public PKIStatusInfo getDvStatus()
+ {
+ return dvStatus;
+ }
+
+ public PolicyInformation getPolicy()
+ {
+ return policy;
+ }
+
+ public ASN1Set getReqSignature()
+ {
+ return reqSignature;
+ }
+
+ public TargetEtcChain[] getCerts()
+ {
+ if (certs != null)
+ {
+ return TargetEtcChain.arrayFromSequence(certs);
+ }
+
+ return null;
+ }
+
+ public Extensions getExtensions()
+ {
+ return extensions;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSCertInfoBuilder.java b/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSCertInfoBuilder.java
new file mode 100644
index 00000000..5da097f5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSCertInfoBuilder.java
@@ -0,0 +1,151 @@
+package org.bouncycastle.asn1.dvcs;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.cmp.PKIStatusInfo;
+import org.bouncycastle.asn1.x509.DigestInfo;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.PolicyInformation;
+
+/**
+ * <pre>
+ * DVCSCertInfo::= SEQUENCE {
+ * version Integer DEFAULT 1 ,
+ * dvReqInfo DVCSRequestInformation,
+ * messageImprint DigestInfo,
+ * serialNumber Integer,
+ * responseTime DVCSTime,
+ * dvStatus [0] PKIStatusInfo OPTIONAL,
+ * policy [1] PolicyInformation OPTIONAL,
+ * reqSignature [2] SignerInfos OPTIONAL,
+ * certs [3] SEQUENCE SIZE (1..MAX) OF
+ * TargetEtcChain OPTIONAL,
+ * extensions Extensions OPTIONAL
+ * }
+ * </pre>
+ */
+
+public class DVCSCertInfoBuilder
+{
+
+ private int version = DEFAULT_VERSION;
+ private DVCSRequestInformation dvReqInfo;
+ private DigestInfo messageImprint;
+ private ASN1Integer serialNumber;
+ private DVCSTime responseTime;
+ private PKIStatusInfo dvStatus;
+ private PolicyInformation policy;
+ private ASN1Set reqSignature;
+ private ASN1Sequence certs;
+ private Extensions extensions;
+
+ private static final int DEFAULT_VERSION = 1;
+ private static final int TAG_DV_STATUS = 0;
+ private static final int TAG_POLICY = 1;
+ private static final int TAG_REQ_SIGNATURE = 2;
+ private static final int TAG_CERTS = 3;
+
+ public DVCSCertInfoBuilder(
+ DVCSRequestInformation dvReqInfo,
+ DigestInfo messageImprint,
+ ASN1Integer serialNumber,
+ DVCSTime responseTime)
+ {
+ this.dvReqInfo = dvReqInfo;
+ this.messageImprint = messageImprint;
+ this.serialNumber = serialNumber;
+ this.responseTime = responseTime;
+ }
+
+ public DVCSCertInfo build()
+ {
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (version != DEFAULT_VERSION)
+ {
+ v.add(new ASN1Integer(version));
+ }
+ v.add(dvReqInfo);
+ v.add(messageImprint);
+ v.add(serialNumber);
+ v.add(responseTime);
+ if (dvStatus != null)
+ {
+ v.add(new DERTaggedObject(false, TAG_DV_STATUS, dvStatus));
+ }
+ if (policy != null)
+ {
+ v.add(new DERTaggedObject(false, TAG_POLICY, policy));
+ }
+ if (reqSignature != null)
+ {
+ v.add(new DERTaggedObject(false, TAG_REQ_SIGNATURE, reqSignature));
+ }
+ if (certs != null)
+ {
+ v.add(new DERTaggedObject(false, TAG_CERTS, certs));
+ }
+ if (extensions != null)
+ {
+ v.add(extensions);
+ }
+
+ return DVCSCertInfo.getInstance(new DERSequence(v));
+ }
+
+ public void setVersion(int version)
+ {
+ this.version = version;
+ }
+
+ public void setDvReqInfo(DVCSRequestInformation dvReqInfo)
+ {
+ this.dvReqInfo = dvReqInfo;
+ }
+
+ public void setMessageImprint(DigestInfo messageImprint)
+ {
+ this.messageImprint = messageImprint;
+ }
+
+ public void setSerialNumber(ASN1Integer serialNumber)
+ {
+ this.serialNumber = serialNumber;
+ }
+
+ public void setResponseTime(DVCSTime responseTime)
+ {
+ this.responseTime = responseTime;
+ }
+
+ public void setDvStatus(PKIStatusInfo dvStatus)
+ {
+ this.dvStatus = dvStatus;
+ }
+
+ public void setPolicy(PolicyInformation policy)
+ {
+ this.policy = policy;
+ }
+
+ public void setReqSignature(ASN1Set reqSignature)
+ {
+ this.reqSignature = reqSignature;
+ }
+
+ public void setCerts(TargetEtcChain[] certs)
+ {
+ this.certs = new DERSequence(certs);
+ }
+
+ public void setExtensions(Extensions extensions)
+ {
+ this.extensions = extensions;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSErrorNotice.java b/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSErrorNotice.java
new file mode 100644
index 00000000..8dd69a9e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSErrorNotice.java
@@ -0,0 +1,96 @@
+package org.bouncycastle.asn1.dvcs;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.cmp.PKIStatusInfo;
+import org.bouncycastle.asn1.x509.GeneralName;
+
+/**
+ * <pre>
+ * DVCSErrorNotice ::= SEQUENCE {
+ * transactionStatus PKIStatusInfo ,
+ * transactionIdentifier GeneralName OPTIONAL
+ * }
+ * </pre>
+ */
+public class DVCSErrorNotice
+ extends ASN1Object
+{
+ private PKIStatusInfo transactionStatus;
+ private GeneralName transactionIdentifier;
+
+ public DVCSErrorNotice(PKIStatusInfo status)
+ {
+ this(status, null);
+ }
+
+ public DVCSErrorNotice(PKIStatusInfo status, GeneralName transactionIdentifier)
+ {
+ this.transactionStatus = status;
+ this.transactionIdentifier = transactionIdentifier;
+ }
+
+ private DVCSErrorNotice(ASN1Sequence seq)
+ {
+ this.transactionStatus = PKIStatusInfo.getInstance(seq.getObjectAt(0));
+ if (seq.size() > 1)
+ {
+ this.transactionIdentifier = GeneralName.getInstance(seq.getObjectAt(1));
+ }
+ }
+
+ public static DVCSErrorNotice getInstance(Object obj)
+ {
+ if (obj instanceof DVCSErrorNotice)
+ {
+ return (DVCSErrorNotice)obj;
+ }
+ else if (obj != null)
+ {
+ return new DVCSErrorNotice(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static DVCSErrorNotice getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(transactionStatus);
+ if (transactionIdentifier != null)
+ {
+ v.add(transactionIdentifier);
+ }
+ return new DERSequence(v);
+ }
+
+ public String toString()
+ {
+ return "DVCSErrorNotice {\n" +
+ "transactionStatus: " + transactionStatus + "\n" +
+ (transactionIdentifier != null ? "transactionIdentifier: " + transactionIdentifier + "\n" : "") +
+ "}\n";
+ }
+
+
+ public PKIStatusInfo getTransactionStatus()
+ {
+ return transactionStatus;
+ }
+
+ public GeneralName getTransactionIdentifier()
+ {
+ return transactionIdentifier;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSObjectIdentifiers.java
new file mode 100644
index 00000000..1a88c34a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSObjectIdentifiers.java
@@ -0,0 +1,36 @@
+package org.bouncycastle.asn1.dvcs;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface DVCSObjectIdentifiers
+{
+
+ // id-pkix OBJECT IDENTIFIER ::= {iso(1)
+ // identified-organization(3) dod(6)
+ // internet(1) security(5) mechanisms(5) pkix(7)}
+ //
+ // id-smime OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+ // us(840) rsadsi(113549) pkcs(1) pkcs-9(9) 16 }
+ public static final ASN1ObjectIdentifier id_pkix = new ASN1ObjectIdentifier("1.3.6.1.5.5.7");
+ public static final ASN1ObjectIdentifier id_smime = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16");
+
+ // -- Authority Information Access for DVCS
+ //
+ // id-ad-dvcs OBJECT IDENTIFIER ::= {id-pkix id-ad(48) 4}
+ public static final ASN1ObjectIdentifier id_ad_dvcs = id_pkix.branch("48.4");
+
+ // -- Key Purpose for DVCS
+ //
+ // id-kp-dvcs OBJECT IDENTIFIER ::= {id-pkix id-kp(3) 10}
+ public static final ASN1ObjectIdentifier id_kp_dvcs = id_pkix.branch("3.10");
+
+ // id-ct-DVCSRequestData OBJECT IDENTIFIER ::= { id-smime ct(1) 7 }
+ // id-ct-DVCSResponseData OBJECT IDENTIFIER ::= { id-smime ct(1) 8 }
+ public static final ASN1ObjectIdentifier id_ct_DVCSRequestData = id_smime.branch("1.7");
+ public static final ASN1ObjectIdentifier id_ct_DVCSResponseData = id_smime.branch("1.8");
+
+ // -- Data validation certificate attribute
+ //
+ // id-aa-dvcs-dvc OBJECT IDENTIFIER ::= { id-smime aa(2) 29 }
+ public static final ASN1ObjectIdentifier id_aa_dvcs_dvc = id_smime.branch("2.29");
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSRequest.java b/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSRequest.java
new file mode 100644
index 00000000..b9506e79
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSRequest.java
@@ -0,0 +1,107 @@
+package org.bouncycastle.asn1.dvcs;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.GeneralName;
+
+/**
+ * <pre>
+ * DVCSRequest ::= SEQUENCE {
+ * requestInformation DVCSRequestInformation,
+ * data Data,
+ * transactionIdentifier GeneralName OPTIONAL
+ * }
+ * </pre>
+ */
+
+public class DVCSRequest
+ extends ASN1Object
+{
+
+ private DVCSRequestInformation requestInformation;
+ private Data data;
+ private GeneralName transactionIdentifier;
+
+ public DVCSRequest(DVCSRequestInformation requestInformation, Data data)
+ {
+ this(requestInformation, data, null);
+ }
+
+ public DVCSRequest(DVCSRequestInformation requestInformation, Data data, GeneralName transactionIdentifier)
+ {
+ this.requestInformation = requestInformation;
+ this.data = data;
+ this.transactionIdentifier = transactionIdentifier;
+ }
+
+ private DVCSRequest(ASN1Sequence seq)
+ {
+ requestInformation = DVCSRequestInformation.getInstance(seq.getObjectAt(0));
+ data = Data.getInstance(seq.getObjectAt(1));
+ if (seq.size() > 2)
+ {
+ transactionIdentifier = GeneralName.getInstance(seq.getObjectAt(2));
+ }
+ }
+
+ public static DVCSRequest getInstance(Object obj)
+ {
+ if (obj instanceof DVCSRequest)
+ {
+ return (DVCSRequest)obj;
+ }
+ else if (obj != null)
+ {
+ return new DVCSRequest(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static DVCSRequest getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(requestInformation);
+ v.add(data);
+ if (transactionIdentifier != null)
+ {
+ v.add(transactionIdentifier);
+ }
+ return new DERSequence(v);
+ }
+
+ public String toString()
+ {
+ return "DVCSRequest {\n" +
+ "requestInformation: " + requestInformation + "\n" +
+ "data: " + data + "\n" +
+ (transactionIdentifier != null ? "transactionIdentifier: " + transactionIdentifier + "\n" : "") +
+ "}\n";
+ }
+
+ public Data getData()
+ {
+ return data;
+ }
+
+ public DVCSRequestInformation getRequestInformation()
+ {
+ return requestInformation;
+ }
+
+ public GeneralName getTransactionIdentifier()
+ {
+ return transactionIdentifier;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSRequestInformation.java b/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSRequestInformation.java
new file mode 100644
index 00000000..8d28f93d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSRequestInformation.java
@@ -0,0 +1,271 @@
+package org.bouncycastle.asn1.dvcs;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.PolicyInformation;
+
+/**
+ * <pre>
+ * DVCSRequestInformation ::= SEQUENCE {
+ * version INTEGER DEFAULT 1 ,
+ * service ServiceType,
+ * nonce Nonce OPTIONAL,
+ * requestTime DVCSTime OPTIONAL,
+ * requester [0] GeneralNames OPTIONAL,
+ * requestPolicy [1] PolicyInformation OPTIONAL,
+ * dvcs [2] GeneralNames OPTIONAL,
+ * dataLocations [3] GeneralNames OPTIONAL,
+ * extensions [4] IMPLICIT Extensions OPTIONAL
+ * }
+ * </pre>
+ */
+
+public class DVCSRequestInformation
+ extends ASN1Object
+{
+ private int version = DEFAULT_VERSION;
+ private ServiceType service;
+ private BigInteger nonce;
+ private DVCSTime requestTime;
+ private GeneralNames requester;
+ private PolicyInformation requestPolicy;
+ private GeneralNames dvcs;
+ private GeneralNames dataLocations;
+ private Extensions extensions;
+
+ private static final int DEFAULT_VERSION = 1;
+ private static final int TAG_REQUESTER = 0;
+ private static final int TAG_REQUEST_POLICY = 1;
+ private static final int TAG_DVCS = 2;
+ private static final int TAG_DATA_LOCATIONS = 3;
+ private static final int TAG_EXTENSIONS = 4;
+
+ private DVCSRequestInformation(ASN1Sequence seq)
+ {
+ int i = 0;
+
+ if (seq.getObjectAt(0) instanceof ASN1Integer)
+ {
+ ASN1Integer encVersion = ASN1Integer.getInstance(seq.getObjectAt(i++));
+ this.version = encVersion.getValue().intValue();
+ }
+ else
+ {
+ this.version = 1;
+ }
+
+ this.service = ServiceType.getInstance(seq.getObjectAt(i++));
+
+ while (i < seq.size())
+ {
+ ASN1Encodable x = seq.getObjectAt(i);
+
+ if (x instanceof ASN1Integer)
+ {
+ this.nonce = ASN1Integer.getInstance(x).getValue();
+ }
+ else if (x instanceof ASN1GeneralizedTime)
+ {
+ this.requestTime = DVCSTime.getInstance(x);
+ }
+ else if (x instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject t = ASN1TaggedObject.getInstance(x);
+ int tagNo = t.getTagNo();
+
+ switch (tagNo)
+ {
+ case TAG_REQUESTER:
+ this.requester = GeneralNames.getInstance(t, false);
+ break;
+ case TAG_REQUEST_POLICY:
+ this.requestPolicy = PolicyInformation.getInstance(ASN1Sequence.getInstance(t, false));
+ break;
+ case TAG_DVCS:
+ this.dvcs = GeneralNames.getInstance(t, false);
+ break;
+ case TAG_DATA_LOCATIONS:
+ this.dataLocations = GeneralNames.getInstance(t, false);
+ break;
+ case TAG_EXTENSIONS:
+ this.extensions = Extensions.getInstance(t, false);
+ break;
+ }
+ }
+ else
+ {
+ this.requestTime = DVCSTime.getInstance(x);
+ }
+
+ i++;
+ }
+ }
+
+ public static DVCSRequestInformation getInstance(Object obj)
+ {
+ if (obj instanceof DVCSRequestInformation)
+ {
+ return (DVCSRequestInformation)obj;
+ }
+ else if (obj != null)
+ {
+ return new DVCSRequestInformation(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static DVCSRequestInformation getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (version != DEFAULT_VERSION)
+ {
+ v.add(new ASN1Integer(version));
+ }
+ v.add(service);
+ if (nonce != null)
+ {
+ v.add(new ASN1Integer(nonce));
+ }
+ if (requestTime != null)
+ {
+ v.add(requestTime);
+ }
+
+ int[] tags = new int[]{
+ TAG_REQUESTER,
+ TAG_REQUEST_POLICY,
+ TAG_DVCS,
+ TAG_DATA_LOCATIONS,
+ TAG_EXTENSIONS
+ };
+ ASN1Encodable[] taggedObjects = new ASN1Encodable[]{
+ requester,
+ requestPolicy,
+ dvcs,
+ dataLocations,
+ extensions
+ };
+ for (int i = 0; i < tags.length; i++)
+ {
+ int tag = tags[i];
+ ASN1Encodable taggedObject = taggedObjects[i];
+ if (taggedObject != null)
+ {
+ v.add(new DERTaggedObject(false, tag, taggedObject));
+ }
+ }
+
+ return new DERSequence(v);
+ }
+
+ public String toString()
+ {
+
+ StringBuffer s = new StringBuffer();
+
+ s.append("DVCSRequestInformation {\n");
+
+ if (version != DEFAULT_VERSION)
+ {
+ s.append("version: " + version + "\n");
+ }
+ s.append("service: " + service + "\n");
+ if (nonce != null)
+ {
+ s.append("nonce: " + nonce + "\n");
+ }
+ if (requestTime != null)
+ {
+ s.append("requestTime: " + requestTime + "\n");
+ }
+ if (requester != null)
+ {
+ s.append("requester: " + requester + "\n");
+ }
+ if (requestPolicy != null)
+ {
+ s.append("requestPolicy: " + requestPolicy + "\n");
+ }
+ if (dvcs != null)
+ {
+ s.append("dvcs: " + dvcs + "\n");
+ }
+ if (dataLocations != null)
+ {
+ s.append("dataLocations: " + dataLocations + "\n");
+ }
+ if (extensions != null)
+ {
+ s.append("extensions: " + extensions + "\n");
+ }
+
+ s.append("}\n");
+ return s.toString();
+ }
+
+ public int getVersion()
+ {
+ return version;
+ }
+
+ public ServiceType getService()
+ {
+ return service;
+ }
+
+ public BigInteger getNonce()
+ {
+ return nonce;
+ }
+
+ public DVCSTime getRequestTime()
+ {
+ return requestTime;
+ }
+
+ public GeneralNames getRequester()
+ {
+ return requester;
+ }
+
+ public PolicyInformation getRequestPolicy()
+ {
+ return requestPolicy;
+ }
+
+ public GeneralNames getDVCS()
+ {
+ return dvcs;
+ }
+
+ public GeneralNames getDataLocations()
+ {
+ return dataLocations;
+ }
+
+ public Extensions getExtensions()
+ {
+ return extensions;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSRequestInformationBuilder.java b/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSRequestInformationBuilder.java
new file mode 100644
index 00000000..9b73c0a6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSRequestInformationBuilder.java
@@ -0,0 +1,224 @@
+package org.bouncycastle.asn1.dvcs;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.PolicyInformation;
+import org.bouncycastle.util.BigIntegers;
+
+/**
+ * <pre>
+ * DVCSRequestInformation ::= SEQUENCE {
+ * version INTEGER DEFAULT 1 ,
+ * service ServiceType,
+ * nonce Nonce OPTIONAL,
+ * requestTime DVCSTime OPTIONAL,
+ * requester [0] GeneralNames OPTIONAL,
+ * requestPolicy [1] PolicyInformation OPTIONAL,
+ * dvcs [2] GeneralNames OPTIONAL,
+ * dataLocations [3] GeneralNames OPTIONAL,
+ * extensions [4] IMPLICIT Extensions OPTIONAL
+ * }
+ * </pre>
+ */
+public class DVCSRequestInformationBuilder
+{
+ private int version = DEFAULT_VERSION;
+
+ private final ServiceType service;
+ private DVCSRequestInformation initialInfo;
+
+ private BigInteger nonce;
+ private DVCSTime requestTime;
+ private GeneralNames requester;
+ private PolicyInformation requestPolicy;
+ private GeneralNames dvcs;
+ private GeneralNames dataLocations;
+ private Extensions extensions;
+
+ private static final int DEFAULT_VERSION = 1;
+ private static final int TAG_REQUESTER = 0;
+ private static final int TAG_REQUEST_POLICY = 1;
+ private static final int TAG_DVCS = 2;
+ private static final int TAG_DATA_LOCATIONS = 3;
+ private static final int TAG_EXTENSIONS = 4;
+
+ public DVCSRequestInformationBuilder(ServiceType service)
+ {
+ this.service = service;
+ }
+
+ public DVCSRequestInformationBuilder(DVCSRequestInformation initialInfo)
+ {
+ this.initialInfo = initialInfo;
+ this.service = initialInfo.getService();
+ this.version = initialInfo.getVersion();
+ this.nonce = initialInfo.getNonce();
+ this.requestTime = initialInfo.getRequestTime();
+ this.requestPolicy = initialInfo.getRequestPolicy();
+ this.dvcs = initialInfo.getDVCS();
+ this.dataLocations = initialInfo.getDataLocations();
+ }
+
+ public DVCSRequestInformation build()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (version != DEFAULT_VERSION)
+ {
+ v.add(new ASN1Integer(version));
+ }
+ v.add(service);
+ if (nonce != null)
+ {
+ v.add(new ASN1Integer(nonce));
+ }
+ if (requestTime != null)
+ {
+ v.add(requestTime);
+ }
+
+ int[] tags = new int[]{
+ TAG_REQUESTER,
+ TAG_REQUEST_POLICY,
+ TAG_DVCS,
+ TAG_DATA_LOCATIONS,
+ TAG_EXTENSIONS
+ };
+ ASN1Encodable[] taggedObjects = new ASN1Encodable[]{
+ requester,
+ requestPolicy,
+ dvcs,
+ dataLocations,
+ extensions
+ };
+ for (int i = 0; i < tags.length; i++)
+ {
+ int tag = tags[i];
+ ASN1Encodable taggedObject = taggedObjects[i];
+ if (taggedObject != null)
+ {
+ v.add(new DERTaggedObject(false, tag, taggedObject));
+ }
+ }
+
+ return DVCSRequestInformation.getInstance(new DERSequence(v));
+ }
+
+ public void setVersion(int version)
+ {
+ if (initialInfo != null)
+ {
+ throw new IllegalStateException("cannot change version in existing DVCSRequestInformation");
+ }
+
+ this.version = version;
+ }
+
+ public void setNonce(BigInteger nonce)
+ {
+ // RFC 3029, 9.1: The DVCS MAY modify the fields
+ // 'dvcs', 'requester', 'dataLocations', and 'nonce' of the ReqInfo structure
+
+ // RFC 3029, 9.1: The only modification
+ // allowed to a 'nonce' is the inclusion of a new field if it was not
+ // present, or to concatenate other data to the end (right) of an
+ // existing value.
+ if (initialInfo != null)
+ {
+ if (initialInfo.getNonce() == null)
+ {
+ this.nonce = nonce;
+ }
+ else
+ {
+ byte[] initialBytes = initialInfo.getNonce().toByteArray();
+ byte[] newBytes = BigIntegers.asUnsignedByteArray(nonce);
+ byte[] nonceBytes = new byte[initialBytes.length + newBytes.length];
+
+ System.arraycopy(initialBytes, 0, nonceBytes, 0, initialBytes.length);
+ System.arraycopy(newBytes, 0, nonceBytes, initialBytes.length, newBytes.length);
+
+ this.nonce = new BigInteger(nonceBytes);
+ }
+ }
+
+ this.nonce = nonce;
+ }
+
+ public void setRequestTime(DVCSTime requestTime)
+ {
+ if (initialInfo != null)
+ {
+ throw new IllegalStateException("cannot change request time in existing DVCSRequestInformation");
+ }
+
+ this.requestTime = requestTime;
+ }
+
+ public void setRequester(GeneralName requester)
+ {
+ this.setRequester(new GeneralNames(requester));
+ }
+
+ public void setRequester(GeneralNames requester)
+ {
+ // RFC 3029, 9.1: The DVCS MAY modify the fields
+ // 'dvcs', 'requester', 'dataLocations', and 'nonce' of the ReqInfo structure
+
+ this.requester = requester;
+ }
+
+ public void setRequestPolicy(PolicyInformation requestPolicy)
+ {
+ if (initialInfo != null)
+ {
+ throw new IllegalStateException("cannot change request policy in existing DVCSRequestInformation");
+ }
+
+ this.requestPolicy = requestPolicy;
+ }
+
+ public void setDVCS(GeneralName dvcs)
+ {
+ this.setDVCS(new GeneralNames(dvcs));
+ }
+
+ public void setDVCS(GeneralNames dvcs)
+ {
+ // RFC 3029, 9.1: The DVCS MAY modify the fields
+ // 'dvcs', 'requester', 'dataLocations', and 'nonce' of the ReqInfo structure
+
+ this.dvcs = dvcs;
+ }
+
+ public void setDataLocations(GeneralName dataLocation)
+ {
+ this.setDataLocations(new GeneralNames(dataLocation));
+ }
+
+ public void setDataLocations(GeneralNames dataLocations)
+ {
+ // RFC 3029, 9.1: The DVCS MAY modify the fields
+ // 'dvcs', 'requester', 'dataLocations', and 'nonce' of the ReqInfo structure
+
+ this.dataLocations = dataLocations;
+ }
+
+ public void setExtensions(Extensions extensions)
+ {
+ if (initialInfo != null)
+ {
+ throw new IllegalStateException("cannot change extensions in existing DVCSRequestInformation");
+ }
+
+ this.extensions = extensions;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSResponse.java b/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSResponse.java
new file mode 100644
index 00000000..3617e21d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSResponse.java
@@ -0,0 +1,117 @@
+package org.bouncycastle.asn1.dvcs;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * <pre>
+ * DVCSResponse ::= CHOICE
+ * {
+ * dvCertInfo DVCSCertInfo ,
+ * dvErrorNote [0] DVCSErrorNotice
+ * }
+ * </pre>
+ */
+
+public class DVCSResponse
+ extends ASN1Object
+ implements ASN1Choice
+{
+ private DVCSCertInfo dvCertInfo;
+ private DVCSErrorNotice dvErrorNote;
+
+ public DVCSResponse(DVCSCertInfo dvCertInfo)
+ {
+ this.dvCertInfo = dvCertInfo;
+ }
+
+ public DVCSResponse(DVCSErrorNotice dvErrorNote)
+ {
+ this.dvErrorNote = dvErrorNote;
+ }
+
+ public static DVCSResponse getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof DVCSResponse)
+ {
+ return (DVCSResponse)obj;
+ }
+ else
+ {
+ if (obj instanceof byte[])
+ {
+ try
+ {
+ return getInstance(ASN1Primitive.fromByteArray((byte[])obj));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("failed to construct sequence from byte[]: " + e.getMessage());
+ }
+ }
+ if (obj instanceof ASN1Sequence)
+ {
+ DVCSCertInfo dvCertInfo = DVCSCertInfo.getInstance(obj);
+
+ return new DVCSResponse(dvCertInfo);
+ }
+ if (obj instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject t = ASN1TaggedObject.getInstance(obj);
+ DVCSErrorNotice dvErrorNote = DVCSErrorNotice.getInstance(t, false);
+
+ return new DVCSResponse(dvErrorNote);
+ }
+ }
+
+ throw new IllegalArgumentException("Couldn't convert from object to DVCSResponse: " + obj.getClass().getName());
+ }
+
+ public static DVCSResponse getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public DVCSCertInfo getCertInfo()
+ {
+ return dvCertInfo;
+ }
+
+ public DVCSErrorNotice getErrorNotice()
+ {
+ return dvErrorNote;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ if (dvCertInfo != null)
+ {
+ return dvCertInfo.toASN1Primitive();
+ }
+ else
+ {
+ return new DERTaggedObject(0, dvErrorNote);
+ }
+ }
+
+ public String toString()
+ {
+ if (dvCertInfo != null)
+ {
+ return "DVCSResponse {\ndvCertInfo: " + dvCertInfo.toString() + "}\n";
+ }
+ if (dvErrorNote != null)
+ {
+ return "DVCSResponse {\ndvErrorNote: " + dvErrorNote.toString() + "}\n";
+ }
+ return null;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSTime.java b/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSTime.java
new file mode 100644
index 00000000..aeb3c2cf
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/dvcs/DVCSTime.java
@@ -0,0 +1,111 @@
+package org.bouncycastle.asn1.dvcs;
+
+import java.util.Date;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.cms.ContentInfo;
+
+/**
+ * <pre>
+ * DVCSTime ::= CHOICE {
+ * genTime GeneralizedTime,
+ * timeStampToken ContentInfo
+ * }
+ * </pre>
+ */
+public class DVCSTime
+ extends ASN1Object
+ implements ASN1Choice
+{
+ private ASN1GeneralizedTime genTime;
+ private ContentInfo timeStampToken;
+ private Date time;
+
+ // constructors:
+
+ public DVCSTime(Date time)
+ {
+ this(new ASN1GeneralizedTime(time));
+ }
+
+ public DVCSTime(ASN1GeneralizedTime genTime)
+ {
+ this.genTime = genTime;
+ }
+
+ public DVCSTime(ContentInfo timeStampToken)
+ {
+ this.timeStampToken = timeStampToken;
+ }
+
+ public static DVCSTime getInstance(Object obj)
+ {
+ if (obj instanceof DVCSTime)
+ {
+ return (DVCSTime)obj;
+ }
+ else if (obj instanceof ASN1GeneralizedTime)
+ {
+ return new DVCSTime(ASN1GeneralizedTime.getInstance(obj));
+ }
+ else if (obj != null)
+ {
+ return new DVCSTime(ContentInfo.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static DVCSTime getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject()); // must be explicitly tagged
+ }
+
+
+ // selectors:
+
+ public ASN1GeneralizedTime getGenTime()
+ {
+ return genTime;
+ }
+
+ public ContentInfo getTimeStampToken()
+ {
+ return timeStampToken;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+
+ if (genTime != null)
+ {
+ return genTime;
+ }
+
+ if (timeStampToken != null)
+ {
+ return timeStampToken.toASN1Primitive();
+ }
+
+ return null;
+ }
+
+ public String toString()
+ {
+ if (genTime != null)
+ {
+ return genTime.toString();
+ }
+ if (timeStampToken != null)
+ {
+ return timeStampToken.toString();
+ }
+ return null;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/dvcs/Data.java b/core/src/main/java/org/bouncycastle/asn1/dvcs/Data.java
new file mode 100644
index 00000000..9c661f1f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/dvcs/Data.java
@@ -0,0 +1,149 @@
+package org.bouncycastle.asn1.dvcs;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.DigestInfo;
+
+/**
+ * <pre>
+ * Data ::= CHOICE {
+ * message OCTET STRING ,
+ * messageImprint DigestInfo,
+ * certs [0] SEQUENCE SIZE (1..MAX) OF
+ * TargetEtcChain
+ * }
+ * </pre>
+ */
+
+public class Data
+ extends ASN1Object
+ implements ASN1Choice
+{
+ private ASN1OctetString message;
+ private DigestInfo messageImprint;
+ private ASN1Sequence certs;
+
+ public Data(byte[] messageBytes)
+ {
+ this.message = new DEROctetString(messageBytes);
+ }
+
+ public Data(ASN1OctetString message)
+ {
+ this.message = message;
+ }
+
+ public Data(DigestInfo messageImprint)
+ {
+ this.messageImprint = messageImprint;
+ }
+
+ public Data(TargetEtcChain cert)
+ {
+ this.certs = new DERSequence(cert);
+ }
+
+ public Data(TargetEtcChain[] certs)
+ {
+ this.certs = new DERSequence(certs);
+ }
+
+ private Data(ASN1Sequence certs)
+ {
+ this.certs = certs;
+ }
+
+ public static Data getInstance(Object obj)
+ {
+ if (obj instanceof Data)
+ {
+ return (Data)obj;
+ }
+ else if (obj instanceof ASN1OctetString)
+ {
+ return new Data((ASN1OctetString)obj);
+ }
+ else if (obj instanceof ASN1Sequence)
+ {
+ return new Data(DigestInfo.getInstance(obj));
+ }
+ else if (obj instanceof ASN1TaggedObject)
+ {
+ return new Data(ASN1Sequence.getInstance((ASN1TaggedObject)obj, false));
+ }
+ throw new IllegalArgumentException("Unknown object submitted to getInstance: " + obj.getClass().getName());
+ }
+
+ public static Data getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ if (message != null)
+ {
+ return message.toASN1Primitive();
+ }
+ if (messageImprint != null)
+ {
+ return messageImprint.toASN1Primitive();
+ }
+ else
+ {
+ return new DERTaggedObject(false, 0, certs);
+ }
+ }
+
+ public String toString()
+ {
+ if (message != null)
+ {
+ return "Data {\n" + message + "}\n";
+ }
+ if (messageImprint != null)
+ {
+ return "Data {\n" + messageImprint + "}\n";
+ }
+ else
+ {
+ return "Data {\n" + certs + "}\n";
+ }
+ }
+
+ public ASN1OctetString getMessage()
+ {
+ return message;
+ }
+
+ public DigestInfo getMessageImprint()
+ {
+ return messageImprint;
+ }
+
+ public TargetEtcChain[] getCerts()
+ {
+ if (certs == null)
+ {
+ return null;
+ }
+
+ TargetEtcChain[] tmp = new TargetEtcChain[certs.size()];
+
+ for (int i = 0; i != tmp.length; i++)
+ {
+ tmp[i] = TargetEtcChain.getInstance(certs.getObjectAt(i));
+ }
+
+ return tmp;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/dvcs/PathProcInput.java b/core/src/main/java/org/bouncycastle/asn1/dvcs/PathProcInput.java
new file mode 100644
index 00000000..3123f40c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/dvcs/PathProcInput.java
@@ -0,0 +1,180 @@
+package org.bouncycastle.asn1.dvcs;
+
+import org.bouncycastle.asn1.ASN1Boolean;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.PolicyInformation;
+
+/**
+ * <pre>
+ * PathProcInput ::= SEQUENCE {
+ * acceptablePolicySet SEQUENCE SIZE (1..MAX) OF
+ * PolicyInformation,
+ * inhibitPolicyMapping BOOLEAN DEFAULT FALSE,
+ * explicitPolicyReqd [0] BOOLEAN DEFAULT FALSE ,
+ * inhibitAnyPolicy [1] BOOLEAN DEFAULT FALSE
+ * }
+ * </pre>
+ */
+public class PathProcInput
+ extends ASN1Object
+{
+
+ private PolicyInformation[] acceptablePolicySet;
+ private boolean inhibitPolicyMapping = false;
+ private boolean explicitPolicyReqd = false;
+ private boolean inhibitAnyPolicy = false;
+
+ public PathProcInput(PolicyInformation[] acceptablePolicySet)
+ {
+ this.acceptablePolicySet = acceptablePolicySet;
+ }
+
+ public PathProcInput(PolicyInformation[] acceptablePolicySet, boolean inhibitPolicyMapping, boolean explicitPolicyReqd, boolean inhibitAnyPolicy)
+ {
+ this.acceptablePolicySet = acceptablePolicySet;
+ this.inhibitPolicyMapping = inhibitPolicyMapping;
+ this.explicitPolicyReqd = explicitPolicyReqd;
+ this.inhibitAnyPolicy = inhibitAnyPolicy;
+ }
+
+ private static PolicyInformation[] fromSequence(ASN1Sequence seq)
+ {
+ PolicyInformation[] tmp = new PolicyInformation[seq.size()];
+
+ for (int i = 0; i != tmp.length; i++)
+ {
+ tmp[i] = PolicyInformation.getInstance(seq.getObjectAt(i));
+ }
+
+ return tmp;
+ }
+
+ public static PathProcInput getInstance(Object obj)
+ {
+ if (obj instanceof PathProcInput)
+ {
+ return (PathProcInput)obj;
+ }
+ else if (obj != null)
+ {
+ ASN1Sequence seq = ASN1Sequence.getInstance(obj);
+ ASN1Sequence policies = ASN1Sequence.getInstance(seq.getObjectAt(0));
+ PathProcInput result = new PathProcInput(fromSequence(policies));
+
+ for (int i = 1; i < seq.size(); i++)
+ {
+ Object o = seq.getObjectAt(i);
+
+ if (o instanceof ASN1Boolean)
+ {
+ ASN1Boolean x = ASN1Boolean.getInstance(o);
+ result.setInhibitPolicyMapping(x.isTrue());
+ }
+ else if (o instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject t = ASN1TaggedObject.getInstance(o);
+ ASN1Boolean x;
+ switch (t.getTagNo())
+ {
+ case 0:
+ x = ASN1Boolean.getInstance(t, false);
+ result.setExplicitPolicyReqd(x.isTrue());
+ break;
+ case 1:
+ x = ASN1Boolean.getInstance(t, false);
+ result.setInhibitAnyPolicy(x.isTrue());
+ }
+ }
+ }
+ return result;
+ }
+
+ return null;
+ }
+
+ public static PathProcInput getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ ASN1EncodableVector pV = new ASN1EncodableVector();
+
+ for (int i = 0; i != acceptablePolicySet.length; i++)
+ {
+ pV.add(acceptablePolicySet[i]);
+ }
+
+ v.add(new DERSequence(pV));
+
+ if (inhibitPolicyMapping)
+ {
+ v.add(new ASN1Boolean(inhibitPolicyMapping));
+ }
+ if (explicitPolicyReqd)
+ {
+ v.add(new DERTaggedObject(false, 0, new ASN1Boolean(explicitPolicyReqd)));
+ }
+ if (inhibitAnyPolicy)
+ {
+ v.add(new DERTaggedObject(false, 1, new ASN1Boolean(inhibitAnyPolicy)));
+ }
+
+ return new DERSequence(v);
+ }
+
+ public String toString()
+ {
+ return "PathProcInput: {\n" +
+ "acceptablePolicySet: " + acceptablePolicySet + "\n" +
+ "inhibitPolicyMapping: " + inhibitPolicyMapping + "\n" +
+ "explicitPolicyReqd: " + explicitPolicyReqd + "\n" +
+ "inhibitAnyPolicy: " + inhibitAnyPolicy + "\n" +
+ "}\n";
+ }
+
+ public PolicyInformation[] getAcceptablePolicySet()
+ {
+ return acceptablePolicySet;
+ }
+
+ public boolean isInhibitPolicyMapping()
+ {
+ return inhibitPolicyMapping;
+ }
+
+ private void setInhibitPolicyMapping(boolean inhibitPolicyMapping)
+ {
+ this.inhibitPolicyMapping = inhibitPolicyMapping;
+ }
+
+ public boolean isExplicitPolicyReqd()
+ {
+ return explicitPolicyReqd;
+ }
+
+ private void setExplicitPolicyReqd(boolean explicitPolicyReqd)
+ {
+ this.explicitPolicyReqd = explicitPolicyReqd;
+ }
+
+ public boolean isInhibitAnyPolicy()
+ {
+ return inhibitAnyPolicy;
+ }
+
+ private void setInhibitAnyPolicy(boolean inhibitAnyPolicy)
+ {
+ this.inhibitAnyPolicy = inhibitAnyPolicy;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/dvcs/ServiceType.java b/core/src/main/java/org/bouncycastle/asn1/dvcs/ServiceType.java
new file mode 100644
index 00000000..d6ee94f2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/dvcs/ServiceType.java
@@ -0,0 +1,92 @@
+package org.bouncycastle.asn1.dvcs;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1Enumerated;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+
+
+/**
+ * ServiceType ::= ENUMERATED { cpd(1), vsd(2), cpkc(3), ccpd(4) }
+ */
+
+public class ServiceType
+ extends ASN1Object
+{
+ /**
+ * Identifier of CPD service (Certify Possession of Data).
+ */
+ public static final ServiceType CPD = new ServiceType(1);
+
+ /**
+ * Identifier of VSD service (Verify Signed Document).
+ */
+ public static final ServiceType VSD = new ServiceType(2);
+
+ /**
+ * Identifier of VPKC service (Verify Public Key Certificates (also referred to as CPKC)).
+ */
+ public static final ServiceType VPKC = new ServiceType(3);
+
+ /**
+ * Identifier of CCPD service (Certify Claim of Possession of Data).
+ */
+ public static final ServiceType CCPD = new ServiceType(4);
+
+ private ASN1Enumerated value;
+
+ public ServiceType(int value)
+ {
+ this.value = new ASN1Enumerated(value);
+ }
+
+ private ServiceType(ASN1Enumerated value)
+ {
+ this.value = value;
+ }
+
+ public static ServiceType getInstance(Object obj)
+ {
+ if (obj instanceof ServiceType)
+ {
+ return (ServiceType)obj;
+ }
+ else if (obj != null)
+ {
+ return new ServiceType(ASN1Enumerated.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static ServiceType getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Enumerated.getInstance(obj, explicit));
+ }
+
+ public BigInteger getValue()
+ {
+ return value.getValue();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return value;
+ }
+
+ public String toString()
+ {
+ int num = value.getValue().intValue();
+ return "" + num + (
+ num == CPD.getValue().intValue() ? "(CPD)" :
+ num == VSD.getValue().intValue() ? "(VSD)" :
+ num == VPKC.getValue().intValue() ? "(VPKC)" :
+ num == CCPD.getValue().intValue() ? "(CCPD)" :
+ "?");
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/dvcs/TargetEtcChain.java b/core/src/main/java/org/bouncycastle/asn1/dvcs/TargetEtcChain.java
new file mode 100644
index 00000000..ec3caadf
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/dvcs/TargetEtcChain.java
@@ -0,0 +1,191 @@
+package org.bouncycastle.asn1.dvcs;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * <pre>
+ * TargetEtcChain ::= SEQUENCE {
+ * target CertEtcToken,
+ * chain SEQUENCE SIZE (1..MAX) OF
+ * CertEtcToken OPTIONAL,
+ * pathProcInput [0] PathProcInput OPTIONAL
+ * }
+ * </pre>
+ */
+
+public class TargetEtcChain
+ extends ASN1Object
+{
+ private CertEtcToken target;
+ private ASN1Sequence chain;
+ private PathProcInput pathProcInput;
+
+ public TargetEtcChain(CertEtcToken target)
+ {
+ this(target, null, null);
+ }
+
+ public TargetEtcChain(CertEtcToken target, CertEtcToken[] chain)
+ {
+ this(target, chain, null);
+ }
+
+ public TargetEtcChain(CertEtcToken target, PathProcInput pathProcInput)
+ {
+ this(target, null, pathProcInput);
+ }
+
+ public TargetEtcChain(CertEtcToken target, CertEtcToken[] chain, PathProcInput pathProcInput)
+ {
+ this.target = target;
+
+ if (chain != null)
+ {
+ this.chain = new DERSequence(chain);
+ }
+
+ this.pathProcInput = pathProcInput;
+ }
+
+ private TargetEtcChain(ASN1Sequence seq)
+ {
+ int i = 0;
+ ASN1Encodable obj = seq.getObjectAt(i++);
+ this.target = CertEtcToken.getInstance(obj);
+
+ try
+ {
+ obj = seq.getObjectAt(i++);
+ this.chain = ASN1Sequence.getInstance(obj);
+ }
+ catch (IllegalArgumentException e)
+ {
+ }
+ catch (IndexOutOfBoundsException e)
+ {
+ return;
+ }
+
+ try
+ {
+ obj = seq.getObjectAt(i++);
+ ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(obj);
+ switch (tagged.getTagNo())
+ {
+ case 0:
+ this.pathProcInput = PathProcInput.getInstance(tagged, false);
+ break;
+ }
+ }
+ catch (IllegalArgumentException e)
+ {
+ }
+ catch (IndexOutOfBoundsException e)
+ {
+ }
+ }
+
+ public static TargetEtcChain getInstance(Object obj)
+ {
+ if (obj instanceof TargetEtcChain)
+ {
+ return (TargetEtcChain)obj;
+ }
+ else if (obj != null)
+ {
+ return new TargetEtcChain(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static TargetEtcChain getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(target);
+ if (chain != null)
+ {
+ v.add(chain);
+ }
+ if (pathProcInput != null)
+ {
+ v.add(new DERTaggedObject(false, 0, pathProcInput));
+ }
+
+ return new DERSequence(v);
+ }
+
+ public String toString()
+ {
+ StringBuffer s = new StringBuffer();
+ s.append("TargetEtcChain {\n");
+ s.append("target: " + target + "\n");
+ if (chain != null)
+ {
+ s.append("chain: " + chain + "\n");
+ }
+ if (pathProcInput != null)
+ {
+ s.append("pathProcInput: " + pathProcInput + "\n");
+ }
+ s.append("}\n");
+ return s.toString();
+ }
+
+
+ public CertEtcToken getTarget()
+ {
+ return target;
+ }
+
+ public CertEtcToken[] getChain()
+ {
+ if (chain != null)
+ {
+ return CertEtcToken.arrayFromSequence(chain);
+ }
+
+ return null;
+ }
+
+ private void setChain(ASN1Sequence chain)
+ {
+ this.chain = chain;
+ }
+
+ public PathProcInput getPathProcInput()
+ {
+ return pathProcInput;
+ }
+
+ private void setPathProcInput(PathProcInput pathProcInput)
+ {
+ this.pathProcInput = pathProcInput;
+ }
+
+ public static TargetEtcChain[] arrayFromSequence(ASN1Sequence seq)
+ {
+ TargetEtcChain[] tmp = new TargetEtcChain[seq.size()];
+
+ for (int i = 0; i != tmp.length; i++)
+ {
+ tmp[i] = TargetEtcChain.getInstance(seq.getObjectAt(i));
+ }
+
+ return tmp;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/eac/BidirectionalMap.java b/core/src/main/java/org/bouncycastle/asn1/eac/BidirectionalMap.java
new file mode 100644
index 00000000..3cf14503
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/eac/BidirectionalMap.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.asn1.eac;
+
+import java.util.Hashtable;
+
+public class BidirectionalMap
+ extends Hashtable
+{
+ private static final long serialVersionUID = -7457289971962812909L;
+
+ Hashtable reverseMap = new Hashtable();
+
+ public Object getReverse(Object o)
+ {
+ return reverseMap.get(o);
+ }
+
+ public Object put(Object key, Object o)
+ {
+ reverseMap.put(o, key);
+ return super.put(key, o);
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/eac/CVCertificate.java b/core/src/main/java/org/bouncycastle/asn1/eac/CVCertificate.java
new file mode 100644
index 00000000..845925c7
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/eac/CVCertificate.java
@@ -0,0 +1,317 @@
+package org.bouncycastle.asn1.eac;
+
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1ParsingException;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DERApplicationSpecific;
+import org.bouncycastle.asn1.DEROctetString;
+
+
+/**
+ * an iso7816Certificate structure.
+ * <p/>
+ * <pre>
+ * Certificate ::= SEQUENCE {
+ * CertificateBody Iso7816CertificateBody,
+ * signature DER Application specific
+ * }
+ * </pre>
+ */
+public class CVCertificate
+ extends ASN1Object
+{
+ private CertificateBody certificateBody;
+ private byte[] signature;
+ private int valid;
+ private static int bodyValid = 0x01;
+ private static int signValid = 0x02;
+ public static final byte version_1 = 0x0;
+
+ public static String ReferenceEncoding = "ISO-8859-1";
+
+ /**
+ * Sets the values of the certificate (body and signature).
+ *
+ * @param appSpe is a DERApplicationSpecific object containing body and signature.
+ * @throws IOException if tags or value are incorrect.
+ */
+ private void setPrivateData(DERApplicationSpecific appSpe)
+ throws IOException
+ {
+ valid = 0;
+ if (appSpe.getApplicationTag() == EACTags.CARDHOLDER_CERTIFICATE)
+ {
+ ASN1InputStream content = new ASN1InputStream(appSpe.getContents());
+ ASN1Primitive tmpObj;
+ while ((tmpObj = content.readObject()) != null)
+ {
+ DERApplicationSpecific aSpe;
+ if (tmpObj instanceof DERApplicationSpecific)
+ {
+ aSpe = (DERApplicationSpecific)tmpObj;
+ switch (aSpe.getApplicationTag())
+ {
+ case EACTags.CERTIFICATE_CONTENT_TEMPLATE:
+ certificateBody = CertificateBody.getInstance(aSpe);
+ valid |= bodyValid;
+ break;
+ case EACTags.STATIC_INTERNAL_AUTHENTIFICATION_ONE_STEP:
+ signature = aSpe.getContents();
+ valid |= signValid;
+ break;
+ default:
+ throw new IOException("Invalid tag, not an Iso7816CertificateStructure :" + aSpe.getApplicationTag());
+ }
+ }
+ else
+ {
+ throw new IOException("Invalid Object, not an Iso7816CertificateStructure");
+ }
+ }
+ }
+ else
+ {
+ throw new IOException("not a CARDHOLDER_CERTIFICATE :" + appSpe.getApplicationTag());
+ }
+ }
+
+ /**
+ * Create an iso7816Certificate structure from an ASN1InputStream.
+ *
+ * @param aIS the byte stream to parse.
+ * @return the Iso7816CertificateStructure represented by the byte stream.
+ * @throws IOException if there is a problem parsing the data.
+ */
+ public CVCertificate(ASN1InputStream aIS)
+ throws IOException
+ {
+ initFrom(aIS);
+ }
+
+ private void initFrom(ASN1InputStream aIS)
+ throws IOException
+ {
+ ASN1Primitive obj;
+ while ((obj = aIS.readObject()) != null)
+ {
+ if (obj instanceof DERApplicationSpecific)
+ {
+ setPrivateData((DERApplicationSpecific)obj);
+ }
+ else
+ {
+ throw new IOException("Invalid Input Stream for creating an Iso7816CertificateStructure");
+ }
+ }
+ }
+
+ /**
+ * Create an iso7816Certificate structure from a DERApplicationSpecific.
+ *
+ * @param appSpe the DERApplicationSpecific object.
+ * @return the Iso7816CertificateStructure represented by the DERApplicationSpecific object.
+ * @throws IOException if there is a problem parsing the data.
+ */
+ private CVCertificate(DERApplicationSpecific appSpe)
+ throws IOException
+ {
+ setPrivateData(appSpe);
+ }
+
+ /**
+ * Create an iso7816Certificate structure from a body and its signature.
+ *
+ * @param body the Iso7816CertificateBody object containing the body.
+ * @param signature the byte array containing the signature
+ * @return the Iso7816CertificateStructure
+ * @throws IOException if there is a problem parsing the data.
+ */
+ public CVCertificate(CertificateBody body, byte[] signature)
+ throws IOException
+ {
+ certificateBody = body;
+ this.signature = signature;
+ // patch remi
+ valid |= bodyValid;
+ valid |= signValid;
+ }
+
+ /**
+ * Create an iso7816Certificate structure from an object.
+ *
+ * @param obj the Object to extract the certificate from.
+ * @return the Iso7816CertificateStructure represented by the byte stream.
+ * @throws IOException if there is a problem parsing the data.
+ */
+ public static CVCertificate getInstance(Object obj)
+ {
+ if (obj instanceof CVCertificate)
+ {
+ return (CVCertificate)obj;
+ }
+ else if (obj != null)
+ {
+ try
+ {
+ return new CVCertificate(DERApplicationSpecific.getInstance(obj));
+ }
+ catch (IOException e)
+ {
+ throw new ASN1ParsingException("unable to parse data: " + e.getMessage(), e);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Gives the signature of the whole body. Type of signature is given in
+ * the Iso7816CertificateBody.Iso7816PublicKey.ASN1ObjectIdentifier
+ *
+ * @return the signature of the body.
+ */
+ public byte[] getSignature()
+ {
+ return signature;
+ }
+
+ /**
+ * Gives the body of the certificate.
+ *
+ * @return the body.
+ */
+ public CertificateBody getBody()
+ {
+ return certificateBody;
+ }
+
+ /**
+ * @see org.bouncycastle.asn1.ASN1Object#toASN1Primitive()
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (valid != (signValid | bodyValid))
+ {
+ return null;
+ }
+ v.add(certificateBody);
+
+ try
+ {
+ v.add(new DERApplicationSpecific(false, EACTags.STATIC_INTERNAL_AUTHENTIFICATION_ONE_STEP, new DEROctetString(signature)));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("unable to convert signature!");
+ }
+
+ return new DERApplicationSpecific(EACTags.CARDHOLDER_CERTIFICATE, v);
+ }
+
+ /**
+ * @return the Holder authorization and role (CVCA, DV, IS).
+ */
+ public ASN1ObjectIdentifier getHolderAuthorization()
+ throws IOException
+ {
+ CertificateHolderAuthorization cha = certificateBody.getCertificateHolderAuthorization();
+ return cha.getOid();
+ }
+
+ /**
+ * @return the date of the certificate generation
+ */
+ public PackedDate getEffectiveDate()
+ throws IOException
+ {
+ return certificateBody.getCertificateEffectiveDate();
+ }
+
+
+ /**
+ * @return the type of certificate (request or profile)
+ * value is either Iso7816CertificateBody.profileType
+ * or Iso7816CertificateBody.requestType. Any other value
+ * is not valid.
+ */
+ public int getCertificateType()
+ {
+ return this.certificateBody.getCertificateType();
+ }
+
+ /**
+ * @return the date of the certificate generation
+ */
+ public PackedDate getExpirationDate()
+ throws IOException
+ {
+ return certificateBody.getCertificateExpirationDate();
+ }
+
+
+ /**
+ * return a bits field coded on one byte. For signification of the
+ * several bit see Iso7816CertificateHolderAuthorization
+ *
+ * @return role and access rigth
+ * @throws IOException
+ * @see CertificateHolderAuthorization
+ */
+ public int getRole()
+ throws IOException
+ {
+ CertificateHolderAuthorization cha = certificateBody.getCertificateHolderAuthorization();
+ return cha.getAccessRights();
+ }
+
+ /**
+ * @return the Authority Reference field of the certificate
+ * @throws IOException
+ */
+ public CertificationAuthorityReference getAuthorityReference()
+ throws IOException
+ {
+ return certificateBody.getCertificationAuthorityReference();
+ }
+
+ /**
+ * @return the Holder Reference Field of the certificate
+ * @throws IOException
+ */
+ public CertificateHolderReference getHolderReference()
+ throws IOException
+ {
+ return certificateBody.getCertificateHolderReference();
+ }
+
+ /**
+ * @return the bits corresponding to the role intented for the certificate
+ * See Iso7816CertificateHolderAuthorization static int for values
+ * @throws IOException
+ */
+ public int getHolderAuthorizationRole()
+ throws IOException
+ {
+ int rights = certificateBody.getCertificateHolderAuthorization().getAccessRights();
+ return rights & 0xC0;
+ }
+
+ /**
+ * @return the bits corresponding the authorizations contained in the certificate
+ * See Iso7816CertificateHolderAuthorization static int for values
+ * @throws IOException
+ */
+ public Flags getHolderAuthorizationRights()
+ throws IOException
+ {
+ return new Flags(certificateBody.getCertificateHolderAuthorization().getAccessRights() & 0x1F);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/eac/CVCertificateRequest.java b/core/src/main/java/org/bouncycastle/asn1/eac/CVCertificateRequest.java
new file mode 100644
index 00000000..dcbc8f1d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/eac/CVCertificateRequest.java
@@ -0,0 +1,170 @@
+package org.bouncycastle.asn1.eac;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1ParsingException;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.BERTags;
+import org.bouncycastle.asn1.DERApplicationSpecific;
+import org.bouncycastle.asn1.DEROctetString;
+
+//import java.math.BigInteger;
+
+
+public class CVCertificateRequest
+ extends ASN1Object
+{
+ private CertificateBody certificateBody;
+
+ private byte[] innerSignature = null;
+ private byte[] outerSignature = null;
+
+ private int valid;
+
+ private static int bodyValid = 0x01;
+ private static int signValid = 0x02;
+
+ private CVCertificateRequest(DERApplicationSpecific request)
+ throws IOException
+ {
+ if (request.getApplicationTag() == EACTags.AUTHENTIFICATION_DATA)
+ {
+ ASN1Sequence seq = ASN1Sequence.getInstance(request.getObject(BERTags.SEQUENCE));
+
+ initCertBody(DERApplicationSpecific.getInstance(seq.getObjectAt(0)));
+
+ outerSignature = DERApplicationSpecific.getInstance(seq.getObjectAt(seq.size() - 1)).getContents();
+ }
+ else
+ {
+ initCertBody(request);
+ }
+ }
+
+ private void initCertBody(DERApplicationSpecific request)
+ throws IOException
+ {
+ if (request.getApplicationTag() == EACTags.CARDHOLDER_CERTIFICATE)
+ {
+ ASN1Sequence seq = ASN1Sequence.getInstance(request.getObject(BERTags.SEQUENCE));
+ for (Enumeration en = seq.getObjects(); en.hasMoreElements();)
+ {
+ DERApplicationSpecific obj = DERApplicationSpecific.getInstance(en.nextElement());
+ switch (obj.getApplicationTag())
+ {
+ case EACTags.CERTIFICATE_CONTENT_TEMPLATE:
+ certificateBody = CertificateBody.getInstance(obj);
+ valid |= bodyValid;
+ break;
+ case EACTags.STATIC_INTERNAL_AUTHENTIFICATION_ONE_STEP:
+ innerSignature = obj.getContents();
+ valid |= signValid;
+ break;
+ default:
+ throw new IOException("Invalid tag, not an CV Certificate Request element:" + obj.getApplicationTag());
+ }
+ }
+ }
+ else
+ {
+ throw new IOException("not a CARDHOLDER_CERTIFICATE in request:" + request.getApplicationTag());
+ }
+ }
+
+ public static CVCertificateRequest getInstance(Object obj)
+ {
+ if (obj instanceof CVCertificateRequest)
+ {
+ return (CVCertificateRequest)obj;
+ }
+ else if (obj != null)
+ {
+ try
+ {
+ return new CVCertificateRequest(DERApplicationSpecific.getInstance(obj));
+ }
+ catch (IOException e)
+ {
+ throw new ASN1ParsingException("unable to parse data: " + e.getMessage(), e);
+ }
+ }
+
+ return null;
+ }
+
+ ASN1ObjectIdentifier signOid = null;
+ ASN1ObjectIdentifier keyOid = null;
+
+ public static byte[] ZeroArray = new byte[]{0};
+
+
+ String strCertificateHolderReference;
+
+ byte[] encodedAuthorityReference;
+
+ int ProfileId;
+
+ /**
+ * Returns the body of the certificate template
+ *
+ * @return the body.
+ */
+ public CertificateBody getCertificateBody()
+ {
+ return certificateBody;
+ }
+
+ /**
+ * Return the public key data object carried in the request
+ * @return the public key
+ */
+ public PublicKeyDataObject getPublicKey()
+ {
+ return certificateBody.getPublicKey();
+ }
+
+ public byte[] getInnerSignature()
+ {
+ return innerSignature;
+ }
+
+ public byte[] getOuterSignature()
+ {
+ return outerSignature;
+ }
+
+ byte[] certificate = null;
+ protected String overSignerReference = null;
+
+ public boolean hasOuterSignature()
+ {
+ return outerSignature != null;
+ }
+
+ byte[] encoded;
+
+ PublicKeyDataObject iso7816PubKey = null;
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(certificateBody);
+
+ try
+ {
+ v.add(new DERApplicationSpecific(false, EACTags.STATIC_INTERNAL_AUTHENTIFICATION_ONE_STEP, new DEROctetString(innerSignature)));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("unable to convert signature!");
+ }
+
+ return new DERApplicationSpecific(EACTags.CARDHOLDER_CERTIFICATE, v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/eac/CertificateBody.java b/core/src/main/java/org/bouncycastle/asn1/eac/CertificateBody.java
new file mode 100644
index 00000000..87d6554c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/eac/CertificateBody.java
@@ -0,0 +1,475 @@
+package org.bouncycastle.asn1.eac;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.BERTags;
+import org.bouncycastle.asn1.DERApplicationSpecific;
+import org.bouncycastle.asn1.DEROctetString;
+
+
+/**
+ * an Iso7816CertificateBody structure.
+ * <p/>
+ * <pre>
+ * CertificateBody ::= SEQUENCE {
+ * // version of the certificate format. Must be 0 (version 1)
+ * CertificateProfileIdentifer DERApplicationSpecific,
+ * //uniquely identifies the issuinng CA's signature key pair
+ * // contains the iso3166-1 alpha2 encoded country code, the
+ * // name of issuer and the sequence number of the key pair.
+ * CertificationAuthorityReference DERApplicationSpecific,
+ * // stores the encoded public key
+ * PublicKey Iso7816PublicKey,
+ * //associates the public key contained in the certificate with a unique name
+ * // contains the iso3166-1 alpha2 encoded country code, the
+ * // name of the holder and the sequence number of the key pair.
+ * certificateHolderReference DERApplicationSpecific,
+ * // Encodes the role of the holder (i.e. CVCA, DV, IS) and assigns read/write
+ * // access rights to data groups storing sensitive data
+ * certificateHolderAuthorization Iso7816CertificateHolderAuthorization,
+ * // the date of the certificate generation
+ * CertificateEffectiveDate DERApplicationSpecific,
+ * // the date after wich the certificate expires
+ * certificateExpirationDate DERApplicationSpecific
+ * }
+ * </pre>
+ */
+public class CertificateBody
+ extends ASN1Object
+{
+ ASN1InputStream seq;
+ private DERApplicationSpecific certificateProfileIdentifier;// version of the certificate format. Must be 0 (version 1)
+ private DERApplicationSpecific certificationAuthorityReference;//uniquely identifies the issuinng CA's signature key pair
+ private PublicKeyDataObject publicKey;// stores the encoded public key
+ private DERApplicationSpecific certificateHolderReference;//associates the public key contained in the certificate with a unique name
+ private CertificateHolderAuthorization certificateHolderAuthorization;// Encodes the role of the holder (i.e. CVCA, DV, IS) and assigns read/write access rights to data groups storing sensitive data
+ private DERApplicationSpecific certificateEffectiveDate;// the date of the certificate generation
+ private DERApplicationSpecific certificateExpirationDate;// the date after wich the certificate expires
+ private int certificateType = 0;// bit field of initialized data. This will tell us if the data are valid.
+ private static final int CPI = 0x01;//certificate Profile Identifier
+ private static final int CAR = 0x02;//certification Authority Reference
+ private static final int PK = 0x04;//public Key
+ private static final int CHR = 0x08;//certificate Holder Reference
+ private static final int CHA = 0x10;//certificate Holder Authorization
+ private static final int CEfD = 0x20;//certificate Effective Date
+ private static final int CExD = 0x40;//certificate Expiration Date
+
+ public static final int profileType = 0x7f;//Profile type Certificate
+ public static final int requestType = 0x0D;// Request type Certificate
+
+ private void setIso7816CertificateBody(DERApplicationSpecific appSpe)
+ throws IOException
+ {
+ byte[] content;
+ if (appSpe.getApplicationTag() == EACTags.CERTIFICATE_CONTENT_TEMPLATE)
+ {
+ content = appSpe.getContents();
+ }
+ else
+ {
+ throw new IOException("Bad tag : not an iso7816 CERTIFICATE_CONTENT_TEMPLATE");
+ }
+ ASN1InputStream aIS = new ASN1InputStream(content);
+ ASN1Primitive obj;
+ while ((obj = aIS.readObject()) != null)
+ {
+ DERApplicationSpecific aSpe;
+
+ if (obj instanceof DERApplicationSpecific)
+ {
+ aSpe = (DERApplicationSpecific)obj;
+ }
+ else
+ {
+ throw new IOException("Not a valid iso7816 content : not a DERApplicationSpecific Object :" + EACTags.encodeTag(appSpe) + obj.getClass());
+ }
+ switch (aSpe.getApplicationTag())
+ {
+ case EACTags.INTERCHANGE_PROFILE:
+ setCertificateProfileIdentifier(aSpe);
+ break;
+ case EACTags.ISSUER_IDENTIFICATION_NUMBER:
+ setCertificationAuthorityReference(aSpe);
+ break;
+ case EACTags.CARDHOLDER_PUBLIC_KEY_TEMPLATE:
+ setPublicKey(PublicKeyDataObject.getInstance(aSpe.getObject(BERTags.SEQUENCE)));
+ break;
+ case EACTags.CARDHOLDER_NAME:
+ setCertificateHolderReference(aSpe);
+ break;
+ case EACTags.CERTIFICATE_HOLDER_AUTHORIZATION_TEMPLATE:
+ setCertificateHolderAuthorization(new CertificateHolderAuthorization(aSpe));
+ break;
+ case EACTags.APPLICATION_EFFECTIVE_DATE:
+ setCertificateEffectiveDate(aSpe);
+ break;
+ case EACTags.APPLICATION_EXPIRATION_DATE:
+ setCertificateExpirationDate(aSpe);
+ break;
+ default:
+ certificateType = 0;
+ throw new IOException("Not a valid iso7816 DERApplicationSpecific tag " + aSpe.getApplicationTag());
+ }
+ }
+ }
+
+ /**
+ * builds an Iso7816CertificateBody by settings each parameters.
+ *
+ * @param certificateProfileIdentifier
+ * @param certificationAuthorityReference
+ *
+ * @param publicKey
+ * @param certificateHolderReference
+ * @param certificateHolderAuthorization
+ * @param certificateEffectiveDate
+ * @param certificateExpirationDate
+ * @throws IOException
+ */
+ public CertificateBody(
+ DERApplicationSpecific certificateProfileIdentifier,
+ CertificationAuthorityReference certificationAuthorityReference,
+ PublicKeyDataObject publicKey,
+ CertificateHolderReference certificateHolderReference,
+ CertificateHolderAuthorization certificateHolderAuthorization,
+ PackedDate certificateEffectiveDate,
+ PackedDate certificateExpirationDate
+ )
+ {
+ setCertificateProfileIdentifier(certificateProfileIdentifier);
+ setCertificationAuthorityReference(new DERApplicationSpecific(
+ EACTags.ISSUER_IDENTIFICATION_NUMBER, certificationAuthorityReference.getEncoded()));
+ setPublicKey(publicKey);
+ setCertificateHolderReference(new DERApplicationSpecific(
+ EACTags.CARDHOLDER_NAME, certificateHolderReference.getEncoded()));
+ setCertificateHolderAuthorization(certificateHolderAuthorization);
+ try
+ {
+ setCertificateEffectiveDate(new DERApplicationSpecific(
+ false, EACTags.APPLICATION_EFFECTIVE_DATE, new DEROctetString(certificateEffectiveDate.getEncoding())));
+ setCertificateExpirationDate(new DERApplicationSpecific(
+ false, EACTags.APPLICATION_EXPIRATION_DATE, new DEROctetString(certificateExpirationDate.getEncoding())));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("unable to encode dates: " + e.getMessage());
+ }
+ }
+
+ /**
+ * builds an Iso7816CertificateBody with an ASN1InputStream.
+ *
+ * @param obj DERApplicationSpecific containing the whole body.
+ * @throws IOException if the body is not valid.
+ */
+ private CertificateBody(DERApplicationSpecific obj)
+ throws IOException
+ {
+ setIso7816CertificateBody(obj);
+ }
+
+ /**
+ * create a profile type Iso7816CertificateBody.
+ *
+ * @return return the "profile" type certificate body.
+ * @throws IOException if the DERApplicationSpecific cannot be created.
+ */
+ private ASN1Primitive profileToASN1Object()
+ throws IOException
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(certificateProfileIdentifier);
+ v.add(certificationAuthorityReference);
+ v.add(new DERApplicationSpecific(false, EACTags.CARDHOLDER_PUBLIC_KEY_TEMPLATE, publicKey));
+ v.add(certificateHolderReference);
+ v.add(certificateHolderAuthorization);
+ v.add(certificateEffectiveDate);
+ v.add(certificateExpirationDate);
+ return new DERApplicationSpecific(EACTags.CERTIFICATE_CONTENT_TEMPLATE, v);
+ }
+
+ private void setCertificateProfileIdentifier(DERApplicationSpecific certificateProfileIdentifier)
+ throws IllegalArgumentException
+ {
+ if (certificateProfileIdentifier.getApplicationTag() == EACTags.INTERCHANGE_PROFILE)
+ {
+ this.certificateProfileIdentifier = certificateProfileIdentifier;
+ certificateType |= CPI;
+ }
+ else
+ {
+ throw new IllegalArgumentException("Not an Iso7816Tags.INTERCHANGE_PROFILE tag :" + EACTags.encodeTag(certificateProfileIdentifier));
+ }
+ }
+
+ private void setCertificateHolderReference(DERApplicationSpecific certificateHolderReference)
+ throws IllegalArgumentException
+ {
+ if (certificateHolderReference.getApplicationTag() == EACTags.CARDHOLDER_NAME)
+ {
+ this.certificateHolderReference = certificateHolderReference;
+ certificateType |= CHR;
+ }
+ else
+ {
+ throw new IllegalArgumentException("Not an Iso7816Tags.CARDHOLDER_NAME tag");
+ }
+ }
+
+ /**
+ * set the CertificationAuthorityReference.
+ *
+ * @param certificationAuthorityReference
+ * the DERApplicationSpecific containing the CertificationAuthorityReference.
+ * @throws IllegalArgumentException if the DERApplicationSpecific is not valid.
+ */
+ private void setCertificationAuthorityReference(
+ DERApplicationSpecific certificationAuthorityReference)
+ throws IllegalArgumentException
+ {
+ if (certificationAuthorityReference.getApplicationTag() == EACTags.ISSUER_IDENTIFICATION_NUMBER)
+ {
+ this.certificationAuthorityReference = certificationAuthorityReference;
+ certificateType |= CAR;
+ }
+ else
+ {
+ throw new IllegalArgumentException("Not an Iso7816Tags.ISSUER_IDENTIFICATION_NUMBER tag");
+ }
+ }
+
+ /**
+ * set the public Key
+ *
+ * @param publicKey : the DERApplicationSpecific containing the public key
+ * @throws java.io.IOException
+ */
+ private void setPublicKey(PublicKeyDataObject publicKey)
+ {
+ this.publicKey = PublicKeyDataObject.getInstance(publicKey);
+ this.certificateType |= PK;
+ }
+
+ /**
+ * create a request type Iso7816CertificateBody.
+ *
+ * @return return the "request" type certificate body.
+ * @throws IOException if the DERApplicationSpecific cannot be created.
+ */
+ private ASN1Primitive requestToASN1Object()
+ throws IOException
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(certificateProfileIdentifier);
+ v.add(new DERApplicationSpecific(false, EACTags.CARDHOLDER_PUBLIC_KEY_TEMPLATE, publicKey));
+ v.add(certificateHolderReference);
+ return new DERApplicationSpecific(EACTags.CERTIFICATE_CONTENT_TEMPLATE, v);
+ }
+
+ /**
+ * create a "request" or "profile" type Iso7816CertificateBody according to the variables sets.
+ *
+ * @return return the ASN1Primitive representing the "request" or "profile" type certificate body.
+ * @throws IOException if the DERApplicationSpecific cannot be created or if data are missings to create a valid certificate.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ try
+ {
+ if (certificateType == profileType)
+ {
+ return profileToASN1Object();
+ }
+ if (certificateType == requestType)
+ {
+ return requestToASN1Object();
+ }
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
+ return null;
+ }
+
+ /**
+ * gives the type of the certificate (value should be profileType or requestType if all data are set).
+ *
+ * @return the int representing the data already set.
+ */
+ public int getCertificateType()
+ {
+ return certificateType;
+ }
+
+ /**
+ * Gives an instance of Iso7816CertificateBody taken from Object obj
+ *
+ * @param obj is the Object to extract the certificate body from.
+ * @return the Iso7816CertificateBody taken from Object obj.
+ * @throws IOException if object is not valid.
+ */
+ public static CertificateBody getInstance(Object obj)
+ throws IOException
+ {
+ if (obj instanceof CertificateBody)
+ {
+ return (CertificateBody)obj;
+ }
+ else if (obj != null)
+ {
+ return new CertificateBody(DERApplicationSpecific.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * @return the date of the certificate generation
+ */
+ public PackedDate getCertificateEffectiveDate()
+ {
+ if ((this.certificateType & CertificateBody.CEfD) ==
+ CertificateBody.CEfD)
+ {
+ return new PackedDate(certificateEffectiveDate.getContents());
+ }
+ return null;
+ }
+
+ /**
+ * set the date of the certificate generation
+ *
+ * @param ced DERApplicationSpecific containing the date of the certificate generation
+ * @throws IllegalArgumentException if the tag is not Iso7816Tags.APPLICATION_EFFECTIVE_DATE
+ */
+ private void setCertificateEffectiveDate(DERApplicationSpecific ced)
+ throws IllegalArgumentException
+ {
+ if (ced.getApplicationTag() == EACTags.APPLICATION_EFFECTIVE_DATE)
+ {
+ this.certificateEffectiveDate = ced;
+ certificateType |= CEfD;
+ }
+ else
+ {
+ throw new IllegalArgumentException("Not an Iso7816Tags.APPLICATION_EFFECTIVE_DATE tag :" + EACTags.encodeTag(ced));
+ }
+ }
+
+ /**
+ * @return the date after wich the certificate expires
+ */
+ public PackedDate getCertificateExpirationDate()
+ throws IOException
+ {
+ if ((this.certificateType & CertificateBody.CExD) ==
+ CertificateBody.CExD)
+ {
+ return new PackedDate(certificateExpirationDate.getContents());
+ }
+ throw new IOException("certificate Expiration Date not set");
+ }
+
+ /**
+ * set the date after wich the certificate expires
+ *
+ * @param ced DERApplicationSpecific containing the date after wich the certificate expires
+ * @throws IllegalArgumentException if the tag is not Iso7816Tags.APPLICATION_EXPIRATION_DATE
+ */
+ private void setCertificateExpirationDate(DERApplicationSpecific ced)
+ throws IllegalArgumentException
+ {
+ if (ced.getApplicationTag() == EACTags.APPLICATION_EXPIRATION_DATE)
+ {
+ this.certificateExpirationDate = ced;
+ certificateType |= CExD;
+ }
+ else
+ {
+ throw new IllegalArgumentException("Not an Iso7816Tags.APPLICATION_EXPIRATION_DATE tag");
+ }
+ }
+
+ /**
+ * the Iso7816CertificateHolderAuthorization encodes the role of the holder
+ * (i.e. CVCA, DV, IS) and assigns read/write access rights to data groups
+ * storing sensitive data. This functions returns the Certificate Holder
+ * Authorization
+ *
+ * @return the Iso7816CertificateHolderAuthorization
+ */
+ public CertificateHolderAuthorization getCertificateHolderAuthorization()
+ throws IOException
+ {
+ if ((this.certificateType & CertificateBody.CHA) ==
+ CertificateBody.CHA)
+ {
+ return certificateHolderAuthorization;
+ }
+ throw new IOException("Certificate Holder Authorisation not set");
+ }
+
+ /**
+ * set the CertificateHolderAuthorization
+ *
+ * @param cha the Certificate Holder Authorization
+ */
+ private void setCertificateHolderAuthorization(
+ CertificateHolderAuthorization cha)
+ {
+ this.certificateHolderAuthorization = cha;
+ certificateType |= CHA;
+ }
+
+ /**
+ * certificateHolderReference : associates the public key contained in the certificate with a unique name
+ *
+ * @return the certificateHolderReference.
+ */
+ public CertificateHolderReference getCertificateHolderReference()
+ {
+ return new CertificateHolderReference(certificateHolderReference.getContents());
+ }
+
+ /**
+ * CertificateProfileIdentifier : version of the certificate format. Must be 0 (version 1)
+ *
+ * @return the CertificateProfileIdentifier
+ */
+ public DERApplicationSpecific getCertificateProfileIdentifier()
+ {
+ return certificateProfileIdentifier;
+ }
+
+ /**
+ * get the certificationAuthorityReference
+ * certificationAuthorityReference : uniquely identifies the issuinng CA's signature key pair
+ *
+ * @return the certificationAuthorityReference
+ */
+ public CertificationAuthorityReference getCertificationAuthorityReference()
+ throws IOException
+ {
+ if ((this.certificateType & CertificateBody.CAR) ==
+ CertificateBody.CAR)
+ {
+ return new CertificationAuthorityReference(certificationAuthorityReference.getContents());
+ }
+ throw new IOException("Certification authority reference not set");
+ }
+
+ /**
+ * @return the PublicKey
+ */
+ public PublicKeyDataObject getPublicKey()
+ {
+ return publicKey;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/eac/CertificateHolderAuthorization.java b/core/src/main/java/org/bouncycastle/asn1/eac/CertificateHolderAuthorization.java
new file mode 100644
index 00000000..93ae57f2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/eac/CertificateHolderAuthorization.java
@@ -0,0 +1,185 @@
+package org.bouncycastle.asn1.eac;
+
+import java.io.IOException;
+import java.util.Hashtable;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DERApplicationSpecific;
+import org.bouncycastle.util.Integers;
+
+/**
+ * an Iso7816CertificateHolderAuthorization structure.
+ * <p/>
+ * <pre>
+ * Certificate Holder Authorization ::= SEQUENCE {
+ * // specifies the format and the rules for the evaluation of the authorization
+ * // level
+ * ASN1ObjectIdentifier oid,
+ * // access rights
+ * DERApplicationSpecific accessRights,
+ * }
+ * </pre>
+ */
+public class CertificateHolderAuthorization
+ extends ASN1Object
+{
+ ASN1ObjectIdentifier oid;
+ DERApplicationSpecific accessRights;
+ public static final ASN1ObjectIdentifier id_role_EAC = EACObjectIdentifiers.bsi_de.branch("3.1.2.1");
+ public static final int CVCA = 0xC0;
+ public static final int DV_DOMESTIC = 0x80;
+ public static final int DV_FOREIGN = 0x40;
+ public static final int IS = 0;
+ public static final int RADG4 = 0x02;//Read Access to DG4 (Iris)
+ public static final int RADG3 = 0x01;//Read Access to DG3 (fingerprint)
+
+ static Hashtable RightsDecodeMap = new Hashtable();
+ static BidirectionalMap AuthorizationRole = new BidirectionalMap();
+ static Hashtable ReverseMap = new Hashtable();
+
+ static
+ {
+ RightsDecodeMap.put(Integers.valueOf(RADG4), "RADG4");
+ RightsDecodeMap.put(Integers.valueOf(RADG3), "RADG3");
+
+ AuthorizationRole.put(Integers.valueOf(CVCA), "CVCA");
+ AuthorizationRole.put(Integers.valueOf(DV_DOMESTIC), "DV_DOMESTIC");
+ AuthorizationRole.put(Integers.valueOf(DV_FOREIGN), "DV_FOREIGN");
+ AuthorizationRole.put(Integers.valueOf(IS), "IS");
+
+ /*
+ for (int i : RightsDecodeMap.keySet())
+ ReverseMap.put(RightsDecodeMap.get(i), i);
+
+ for (int i : AuthorizationRole.keySet())
+ ReverseMap.put(AuthorizationRole.get(i), i);
+ */
+ }
+
+ public static String GetRoleDescription(int i)
+ {
+ return (String)AuthorizationRole.get(Integers.valueOf(i));
+ }
+
+ public static int GetFlag(String description)
+ {
+ Integer i = (Integer)AuthorizationRole.getReverse(description);
+ if (i == null)
+ {
+ throw new IllegalArgumentException("Unknown value " + description);
+ }
+
+ return i.intValue();
+ }
+
+ private void setPrivateData(ASN1InputStream cha)
+ throws IOException
+ {
+ ASN1Primitive obj;
+ obj = cha.readObject();
+ if (obj instanceof ASN1ObjectIdentifier)
+ {
+ this.oid = (ASN1ObjectIdentifier)obj;
+ }
+ else
+ {
+ throw new IllegalArgumentException("no Oid in CerticateHolderAuthorization");
+ }
+ obj = cha.readObject();
+ if (obj instanceof DERApplicationSpecific)
+ {
+ this.accessRights = (DERApplicationSpecific)obj;
+ }
+ else
+ {
+ throw new IllegalArgumentException("No access rights in CerticateHolderAuthorization");
+ }
+ }
+
+
+ /**
+ * create an Iso7816CertificateHolderAuthorization according to the parameters
+ *
+ * @param oid Object Identifier : specifies the format and the rules for the
+ * evaluatioin of the authorization level.
+ * @param rights specifies the access rights
+ * @throws IOException
+ */
+ public CertificateHolderAuthorization(ASN1ObjectIdentifier oid, int rights)
+ throws IOException
+ {
+ setOid(oid);
+ setAccessRights((byte)rights);
+ }
+
+ /**
+ * create an Iso7816CertificateHolderAuthorization according to the {@link DERApplicationSpecific}
+ *
+ * @param aSpe the DERApplicationSpecific containing the data
+ * @throws IOException
+ */
+ public CertificateHolderAuthorization(DERApplicationSpecific aSpe)
+ throws IOException
+ {
+ if (aSpe.getApplicationTag() == EACTags.CERTIFICATE_HOLDER_AUTHORIZATION_TEMPLATE)
+ {
+ setPrivateData(new ASN1InputStream(aSpe.getContents()));
+ }
+ }
+
+ /**
+ * @return containing the access rights
+ */
+ public int getAccessRights()
+ {
+ return accessRights.getContents()[0] & 0xff;
+ }
+
+ /**
+ * create a DERApplicationSpecific and set the access rights to "rights"
+ *
+ * @param rights byte containing the rights.
+ */
+ private void setAccessRights(byte rights)
+ {
+ byte[] accessRights = new byte[1];
+ accessRights[0] = rights;
+ this.accessRights = new DERApplicationSpecific(
+ EACTags.getTag(EACTags.DISCRETIONARY_DATA), accessRights);
+ }
+
+ /**
+ * @return the Object identifier
+ */
+ public ASN1ObjectIdentifier getOid()
+ {
+ return oid;
+ }
+
+ /**
+ * set the Object Identifier
+ *
+ * @param oid {@link ASN1ObjectIdentifier} containing the Object Identifier
+ */
+ private void setOid(ASN1ObjectIdentifier oid)
+ {
+ this.oid = oid;
+ }
+
+ /**
+ * return the Certificate Holder Authorization as a DERApplicationSpecific Object
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(oid);
+ v.add(accessRights);
+
+ return new DERApplicationSpecific(EACTags.CERTIFICATE_HOLDER_AUTHORIZATION_TEMPLATE, v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/eac/CertificateHolderReference.java b/core/src/main/java/org/bouncycastle/asn1/eac/CertificateHolderReference.java
new file mode 100644
index 00000000..ec8dec08
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/eac/CertificateHolderReference.java
@@ -0,0 +1,66 @@
+package org.bouncycastle.asn1.eac;
+
+import java.io.UnsupportedEncodingException;
+
+public class CertificateHolderReference
+{
+ private static final String ReferenceEncoding = "ISO-8859-1";
+
+ private String countryCode;
+ private String holderMnemonic;
+ private String sequenceNumber;
+
+ public CertificateHolderReference(String countryCode, String holderMnemonic, String sequenceNumber)
+ {
+ this.countryCode = countryCode;
+ this.holderMnemonic = holderMnemonic;
+ this.sequenceNumber = sequenceNumber;
+ }
+
+ CertificateHolderReference(byte[] contents)
+ {
+ try
+ {
+ String concat = new String(contents, ReferenceEncoding);
+
+ this.countryCode = concat.substring(0, 2);
+ this.holderMnemonic = concat.substring(2, concat.length() - 5);
+
+ this.sequenceNumber = concat.substring(concat.length() - 5);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new IllegalStateException(e.toString());
+ }
+ }
+
+ public String getCountryCode()
+ {
+ return countryCode;
+ }
+
+ public String getHolderMnemonic()
+ {
+ return holderMnemonic;
+ }
+
+ public String getSequenceNumber()
+ {
+ return sequenceNumber;
+ }
+
+
+ public byte[] getEncoded()
+ {
+ String ref = countryCode + holderMnemonic + sequenceNumber;
+
+ try
+ {
+ return ref.getBytes(ReferenceEncoding);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new IllegalStateException(e.toString());
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/eac/CertificationAuthorityReference.java b/core/src/main/java/org/bouncycastle/asn1/eac/CertificationAuthorityReference.java
new file mode 100644
index 00000000..7a5dc8ab
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/eac/CertificationAuthorityReference.java
@@ -0,0 +1,15 @@
+package org.bouncycastle.asn1.eac;
+
+public class CertificationAuthorityReference
+ extends CertificateHolderReference
+{
+ public CertificationAuthorityReference(String countryCode, String holderMnemonic, String sequenceNumber)
+ {
+ super(countryCode, holderMnemonic, sequenceNumber);
+ }
+
+ CertificationAuthorityReference(byte[] contents)
+ {
+ super(contents);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java
new file mode 100644
index 00000000..bef8620c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java
@@ -0,0 +1,55 @@
+package org.bouncycastle.asn1.eac;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface EACObjectIdentifiers
+{
+ // bsi-de OBJECT IDENTIFIER ::= {
+ // itu-t(0) identified-organization(4) etsi(0)
+ // reserved(127) etsi-identified-organization(0) 7
+ // }
+ static final ASN1ObjectIdentifier bsi_de = new ASN1ObjectIdentifier("0.4.0.127.0.7");
+
+ // id-PK OBJECT IDENTIFIER ::= {
+ // bsi-de protocols(2) smartcard(2) 1
+ // }
+ static final ASN1ObjectIdentifier id_PK = bsi_de.branch("2.2.1");
+
+ static final ASN1ObjectIdentifier id_PK_DH = id_PK.branch("1");
+ static final ASN1ObjectIdentifier id_PK_ECDH = id_PK.branch("2");
+
+ // id-CA OBJECT IDENTIFIER ::= {
+ // bsi-de protocols(2) smartcard(2) 3
+ // }
+ static final ASN1ObjectIdentifier id_CA = bsi_de.branch("2.2.3");
+ static final ASN1ObjectIdentifier id_CA_DH = id_CA.branch("1");
+ static final ASN1ObjectIdentifier id_CA_DH_3DES_CBC_CBC = id_CA_DH.branch("1");
+ static final ASN1ObjectIdentifier id_CA_ECDH = id_CA.branch("2");
+ static final ASN1ObjectIdentifier id_CA_ECDH_3DES_CBC_CBC = id_CA_ECDH.branch("1");
+
+ //
+ // id-TA OBJECT IDENTIFIER ::= {
+ // bsi-de protocols(2) smartcard(2) 2
+ // }
+ static final ASN1ObjectIdentifier id_TA = bsi_de.branch("2.2.2");
+
+ static final ASN1ObjectIdentifier id_TA_RSA = id_TA.branch("1");
+ static final ASN1ObjectIdentifier id_TA_RSA_v1_5_SHA_1 = id_TA_RSA .branch("1");
+ static final ASN1ObjectIdentifier id_TA_RSA_v1_5_SHA_256 = id_TA_RSA.branch("2");
+ static final ASN1ObjectIdentifier id_TA_RSA_PSS_SHA_1 = id_TA_RSA.branch("3");
+ static final ASN1ObjectIdentifier id_TA_RSA_PSS_SHA_256 = id_TA_RSA.branch("4");
+ static final ASN1ObjectIdentifier id_TA_RSA_v1_5_SHA_512 = id_TA_RSA.branch("5");
+ static final ASN1ObjectIdentifier id_TA_RSA_PSS_SHA_512 = id_TA_RSA.branch("6");
+ static final ASN1ObjectIdentifier id_TA_ECDSA = id_TA.branch("2");
+ static final ASN1ObjectIdentifier id_TA_ECDSA_SHA_1 = id_TA_ECDSA.branch("1");
+ static final ASN1ObjectIdentifier id_TA_ECDSA_SHA_224 = id_TA_ECDSA.branch("2");
+ static final ASN1ObjectIdentifier id_TA_ECDSA_SHA_256 = id_TA_ECDSA.branch("3");
+ static final ASN1ObjectIdentifier id_TA_ECDSA_SHA_384 = id_TA_ECDSA.branch("4");
+ static final ASN1ObjectIdentifier id_TA_ECDSA_SHA_512 = id_TA_ECDSA.branch("5");
+
+ /**
+ * id-EAC-ePassport OBJECT IDENTIFIER ::= {
+ * bsi-de applications(3) mrtd(1) roles(2) 1}
+ */
+ static final ASN1ObjectIdentifier id_EAC_ePassport = bsi_de.branch("3.1.2.1");
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/eac/EACTags.java b/core/src/main/java/org/bouncycastle/asn1/eac/EACTags.java
new file mode 100644
index 00000000..b9ffe9d1
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/eac/EACTags.java
@@ -0,0 +1,209 @@
+package org.bouncycastle.asn1.eac;
+
+import org.bouncycastle.asn1.BERTags;
+import org.bouncycastle.asn1.DERApplicationSpecific;
+
+public class EACTags
+{
+ public static final int OBJECT_IDENTIFIER = 0x06;
+ public static final int COUNTRY_CODE_NATIONAL_DATA = 0x41;
+ public static final int ISSUER_IDENTIFICATION_NUMBER = 0x02; //0x42;
+ public static final int CARD_SERVICE_DATA = 0x43;
+ public static final int INITIAL_ACCESS_DATA = 0x44;
+ public static final int CARD_ISSUER_DATA = 0x45;
+ public static final int PRE_ISSUING_DATA = 0x46;
+ public static final int CARD_CAPABILITIES = 0x47;
+ public static final int STATUS_INFORMATION = 0x48;
+ public static final int EXTENDED_HEADER_LIST = 0x4D;
+ public static final int APPLICATION_IDENTIFIER = 0x4F;
+ public static final int APPLICATION_LABEL = 0x50;
+ public static final int FILE_REFERENCE = 0x51;
+ public static final int COMMAND_TO_PERFORM = 0x52;
+ public static final int DISCRETIONARY_DATA = 0x53;
+ public static final int OFFSET_DATA_OBJECT = 0x54;
+ public static final int TRACK1_APPLICATION = 0x56;
+ public static final int TRACK2_APPLICATION = 0x57;
+ public static final int TRACK3_APPLICATION = 0x58;
+ public static final int CARD_EXPIRATION_DATA = 0x59;
+ public static final int PRIMARY_ACCOUNT_NUMBER = 0x5A;// PAN
+ public static final int NAME = 0x5B;
+ public static final int TAG_LIST = 0x5C;
+ public static final int HEADER_LIST = 0x5D;
+ public static final int LOGIN_DATA = 0x5E;
+ public static final int CARDHOLDER_NAME = 0x20; // 0x5F20;
+ public static final int TRACK1_CARD = 0x5F21;
+ public static final int TRACK2_CARD = 0x5F22;
+ public static final int TRACK3_CARD = 0x5F23;
+ public static final int APPLICATION_EXPIRATION_DATE = 0x24; // 0x5F24;
+ public static final int APPLICATION_EFFECTIVE_DATE = 0x25; // 0x5F25;
+ public static final int CARD_EFFECTIVE_DATE = 0x5F26;
+ public static final int INTERCHANGE_CONTROL = 0x5F27;
+ public static final int COUNTRY_CODE = 0x5F28;
+ public static final int INTERCHANGE_PROFILE = 0x29; // 0x5F29;
+ public static final int CURRENCY_CODE = 0x5F2A;
+ public static final int DATE_OF_BIRTH = 0x5F2B;
+ public static final int CARDHOLDER_NATIONALITY = 0x5F2C;
+ public static final int LANGUAGE_PREFERENCES = 0x5F2D;
+ public static final int CARDHOLDER_BIOMETRIC_DATA = 0x5F2E;
+ public static final int PIN_USAGE_POLICY = 0x5F2F;
+ public static final int SERVICE_CODE = 0x5F30;
+ public static final int TRANSACTION_COUNTER = 0x5F32;
+ public static final int TRANSACTION_DATE = 0x5F33;
+ public static final int CARD_SEQUENCE_NUMBER = 0x5F34;
+ public static final int SEX = 0x5F35;
+ public static final int CURRENCY_EXPONENT = 0x5F36;
+ public static final int STATIC_INTERNAL_AUTHENTIFICATION_ONE_STEP = 0x37; // 0x5F37;
+ public static final int SIGNATURE = 0x5F37;
+ public static final int STATIC_INTERNAL_AUTHENTIFICATION_FIRST_DATA = 0x5F38;
+ public static final int STATIC_INTERNAL_AUTHENTIFICATION_SECOND_DATA = 0x5F39;
+ public static final int DYNAMIC_INTERNAL_AUTHENTIFICATION = 0x5F3A;
+ public static final int DYNAMIC_EXTERNAL_AUTHENTIFICATION = 0x5F3B;
+ public static final int DYNAMIC_MUTUAL_AUTHENTIFICATION = 0x5F3C;
+ public static final int CARDHOLDER_PORTRAIT_IMAGE = 0x5F40;
+ public static final int ELEMENT_LIST = 0x5F41;
+ public static final int ADDRESS = 0x5F42;
+ public static final int CARDHOLDER_HANDWRITTEN_SIGNATURE = 0x5F43;
+ public static final int APPLICATION_IMAGE = 0x5F44;
+ public static final int DISPLAY_IMAGE = 0x5F45;
+ public static final int TIMER = 0x5F46;
+ public static final int MESSAGE_REFERENCE = 0x5F47;
+ public static final int CARDHOLDER_PRIVATE_KEY = 0x5F48;
+ public static final int CARDHOLDER_PUBLIC_KEY = 0x5F49;
+ public static final int CERTIFICATION_AUTHORITY_PUBLIC_KEY = 0x5F4A;
+ public static final int DEPRECATED = 0x5F4B;
+ public static final int CERTIFICATE_HOLDER_AUTHORIZATION = 0x5F4C;// Not yet defined in iso7816. The allocation is requested
+ public static final int INTEGRATED_CIRCUIT_MANUFACTURER_ID = 0x5F4D;
+ public static final int CERTIFICATE_CONTENT = 0x5F4E;
+ public static final int UNIFORM_RESOURCE_LOCATOR = 0x5F50;
+ public static final int ANSWER_TO_RESET = 0x5F51;
+ public static final int HISTORICAL_BYTES = 0x5F52;
+ public static final int DIGITAL_SIGNATURE = 0x5F3D;
+ public static final int APPLICATION_TEMPLATE = 0x61;
+ public static final int FCP_TEMPLATE = 0x62;
+ public static final int WRAPPER = 0x63;
+ public static final int FMD_TEMPLATE = 0x64;
+ public static final int CARDHOLDER_RELATIVE_DATA = 0x65;
+ public static final int CARD_DATA = 0x66;
+ public static final int AUTHENTIFICATION_DATA = 0x67;
+ public static final int SPECIAL_USER_REQUIREMENTS = 0x68;
+ public static final int LOGIN_TEMPLATE = 0x6A;
+ public static final int QUALIFIED_NAME = 0x6B;
+ public static final int CARDHOLDER_IMAGE_TEMPLATE = 0x6C;
+ public static final int APPLICATION_IMAGE_TEMPLATE = 0x6D;
+ public static final int APPLICATION_RELATED_DATA = 0x6E;
+ public static final int FCI_TEMPLATE = 0x6F;
+ public static final int DISCRETIONARY_DATA_OBJECTS = 0x73;
+ public static final int COMPATIBLE_TAG_ALLOCATION_AUTHORITY = 0x78;
+ public static final int COEXISTANT_TAG_ALLOCATION_AUTHORITY = 0x79;
+ public static final int SECURITY_SUPPORT_TEMPLATE = 0x7A;
+ public static final int SECURITY_ENVIRONMENT_TEMPLATE = 0x7B;
+ public static final int DYNAMIC_AUTHENTIFICATION_TEMPLATE = 0x7C;
+ public static final int SECURE_MESSAGING_TEMPLATE = 0x7D;
+ public static final int NON_INTERINDUSTRY_DATA_OBJECT_NESTING_TEMPLATE = 0x7E;
+ public static final int DISPLAY_CONTROL = 0x7F20;
+ public static final int CARDHOLDER_CERTIFICATE = 0x21; // 0x7F21;
+ public static final int CV_CERTIFICATE = 0x7F21;
+ public static final int CARDHOLER_REQUIREMENTS_INCLUDED_FEATURES = 0x7F22;
+ public static final int CARDHOLER_REQUIREMENTS_EXCLUDED_FEATURES = 0x7F23;
+ public static final int BIOMETRIC_DATA_TEMPLATE = 0x7F2E;
+ public static final int DIGITAL_SIGNATURE_BLOCK = 0x7F3D;
+ public static final int CARDHOLDER_PRIVATE_KEY_TEMPLATE = 0x7F48;
+ public static final int CARDHOLDER_PUBLIC_KEY_TEMPLATE = 0x49; // 0x7F49;
+ public static final int CERTIFICATE_HOLDER_AUTHORIZATION_TEMPLATE = 0x4C; // 0x7F4C;
+ public static final int CERTIFICATE_CONTENT_TEMPLATE = 0x4E; // 0x7F4E;
+ public static final int CERTIFICATE_BODY = 0x4E; // 0x7F4E;
+ public static final int BIOMETRIC_INFORMATION_TEMPLATE = 0x7F60;
+ public static final int BIOMETRIC_INFORMATION_GROUP_TEMPLATE = 0x7F61;
+
+ public static int getTag(int encodedTag)
+ {
+ /*
+ int i;
+ for (i = 24; i>=0; i-=8) {
+ if (((0xFF<<i) & tag) != 0)
+ return (((0xFF<<i) & tag) >> i);
+ }
+ return 0;
+ */
+ return decodeTag(encodedTag);
+ }
+
+ public static int getTagNo(int tag)
+ {
+ int i;
+ for (i = 24; i >= 0; i -= 8)
+ {
+ if (((0xFF << i) & tag) != 0)
+ {
+ return ((~(0xFF << i)) & tag);
+ }
+ }
+ return 0;
+ }
+
+ public static int encodeTag(DERApplicationSpecific spec)
+ {
+ int retValue = BERTags.APPLICATION;
+ boolean constructed = spec.isConstructed();
+ if (constructed)
+ {
+ retValue |= BERTags.CONSTRUCTED;
+ }
+
+ int tag = spec.getApplicationTag();
+
+ if (tag > 31)
+ {
+ retValue |= 0x1F;
+ retValue <<= 8;
+
+ int currentByte = tag & 0x7F;
+ retValue |= currentByte;
+ tag >>= 7;
+
+ while (tag > 0)
+ {
+ retValue |= 0x80;
+ retValue <<= 8;
+
+ currentByte = tag & 0x7F;
+ tag >>= 7;
+ }
+ }
+ else
+ {
+ retValue |= tag;
+ }
+
+ return retValue;
+ }
+
+ public static int decodeTag(int tag)
+ {
+ int retValue = 0;
+ boolean multiBytes = false;
+ for (int i = 24; i >= 0; i -= 8)
+ {
+ int currentByte = tag >> i & 0xFF;
+ if (currentByte == 0)
+ {
+ continue;
+ }
+
+ if (multiBytes)
+ {
+ retValue <<= 7;
+ retValue |= currentByte & 0x7F;
+ }
+ else if ((currentByte & 0x1F) == 0x1F)
+ {
+ multiBytes = true;
+ }
+ else
+ {
+ return currentByte & 0x1F; // higher order bit are for DER.Constructed and type
+ }
+ }
+ return retValue;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/eac/ECDSAPublicKey.java b/core/src/main/java/org/bouncycastle/asn1/eac/ECDSAPublicKey.java
new file mode 100644
index 00000000..3dd22fc3
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/eac/ECDSAPublicKey.java
@@ -0,0 +1,341 @@
+package org.bouncycastle.asn1.eac;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * an Iso7816ECDSAPublicKeyStructure structure.
+ * <p/>
+ * <pre>
+ * Certificate Holder Authorization ::= SEQUENCE {
+ * ASN1TaggedObject primeModulusP; // OPTIONAL
+ * ASN1TaggedObject firstCoefA; // OPTIONAL
+ * ASN1TaggedObject secondCoefB; // OPTIONAL
+ * ASN1TaggedObject basePointG; // OPTIONAL
+ * ASN1TaggedObject orderOfBasePointR; // OPTIONAL
+ * ASN1TaggedObject publicPointY; //REQUIRED
+ * ASN1TaggedObject cofactorF; // OPTIONAL
+ * }
+ * </pre>
+ */
+public class ECDSAPublicKey
+ extends PublicKeyDataObject
+{
+ private ASN1ObjectIdentifier usage;
+ private BigInteger primeModulusP; // OPTIONAL
+ private BigInteger firstCoefA; // OPTIONAL
+ private BigInteger secondCoefB; // OPTIONAL
+ private byte[] basePointG; // OPTIONAL
+ private BigInteger orderOfBasePointR; // OPTIONAL
+ private byte[] publicPointY; //REQUIRED
+ private BigInteger cofactorF; // OPTIONAL
+ private int options;
+ private static final int P = 0x01;
+ private static final int A = 0x02;
+ private static final int B = 0x04;
+ private static final int G = 0x08;
+ private static final int R = 0x10;
+ private static final int Y = 0x20;
+ private static final int F = 0x40;
+
+ ECDSAPublicKey(ASN1Sequence seq)
+ throws IllegalArgumentException
+ {
+ Enumeration en = seq.getObjects();
+
+ this.usage = ASN1ObjectIdentifier.getInstance(en.nextElement());
+
+ options = 0;
+ while (en.hasMoreElements())
+ {
+ Object obj = en.nextElement();
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject to = (ASN1TaggedObject)obj;
+ switch (to.getTagNo())
+ {
+ case 0x1:
+ setPrimeModulusP(UnsignedInteger.getInstance(to).getValue());
+ break;
+ case 0x2:
+ setFirstCoefA(UnsignedInteger.getInstance(to).getValue());
+ break;
+ case 0x3:
+ setSecondCoefB(UnsignedInteger.getInstance(to).getValue());
+ break;
+ case 0x4:
+ setBasePointG(ASN1OctetString.getInstance(to, false));
+ break;
+ case 0x5:
+ setOrderOfBasePointR(UnsignedInteger.getInstance(to).getValue());
+ break;
+ case 0x6:
+ setPublicPointY(ASN1OctetString.getInstance(to, false));
+ break;
+ case 0x7:
+ setCofactorF(UnsignedInteger.getInstance(to).getValue());
+ break;
+ default:
+ options = 0;
+ throw new IllegalArgumentException("Unknown Object Identifier!");
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unknown Object Identifier!");
+ }
+ }
+ if (options != 0x20 && options != 0x7F)
+ {
+ throw new IllegalArgumentException("All options must be either present or absent!");
+ }
+ }
+
+ public ECDSAPublicKey(ASN1ObjectIdentifier usage, byte[] ppY)
+ throws IllegalArgumentException
+ {
+ this.usage = usage;
+ setPublicPointY(new DEROctetString(ppY));
+ }
+
+ public ECDSAPublicKey(ASN1ObjectIdentifier usage, BigInteger p, BigInteger a, BigInteger b, byte[] basePoint, BigInteger order, byte[] publicPoint, int cofactor)
+ {
+ this.usage = usage;
+ setPrimeModulusP(p);
+ setFirstCoefA(a);
+ setSecondCoefB(b);
+ setBasePointG(new DEROctetString(basePoint));
+ setOrderOfBasePointR(order);
+ setPublicPointY(new DEROctetString(publicPoint));
+ setCofactorF(BigInteger.valueOf(cofactor));
+ }
+
+ public ASN1ObjectIdentifier getUsage()
+ {
+ return usage;
+ }
+
+ public byte[] getBasePointG()
+ {
+ if ((options & G) != 0)
+ {
+ return basePointG;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ private void setBasePointG(ASN1OctetString basePointG)
+ throws IllegalArgumentException
+ {
+ if ((options & G) == 0)
+ {
+ options |= G;
+ this.basePointG = basePointG.getOctets();
+ }
+ else
+ {
+ throw new IllegalArgumentException("Base Point G already set");
+ }
+ }
+
+ public BigInteger getCofactorF()
+ {
+ if ((options & F) != 0)
+ {
+ return cofactorF;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ private void setCofactorF(BigInteger cofactorF)
+ throws IllegalArgumentException
+ {
+ if ((options & F) == 0)
+ {
+ options |= F;
+ this.cofactorF = cofactorF;
+ }
+ else
+ {
+ throw new IllegalArgumentException("Cofactor F already set");
+ }
+ }
+
+ public BigInteger getFirstCoefA()
+ {
+ if ((options & A) != 0)
+ {
+ return firstCoefA;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ private void setFirstCoefA(BigInteger firstCoefA)
+ throws IllegalArgumentException
+ {
+ if ((options & A) == 0)
+ {
+ options |= A;
+ this.firstCoefA = firstCoefA;
+ }
+ else
+ {
+ throw new IllegalArgumentException("First Coef A already set");
+ }
+ }
+
+ public BigInteger getOrderOfBasePointR()
+ {
+ if ((options & R) != 0)
+ {
+ return orderOfBasePointR;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ private void setOrderOfBasePointR(BigInteger orderOfBasePointR)
+ throws IllegalArgumentException
+ {
+ if ((options & R) == 0)
+ {
+ options |= R;
+ this.orderOfBasePointR = orderOfBasePointR;
+ }
+ else
+ {
+ throw new IllegalArgumentException("Order of base point R already set");
+ }
+ }
+
+ public BigInteger getPrimeModulusP()
+ {
+ if ((options & P) != 0)
+ {
+ return primeModulusP;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ private void setPrimeModulusP(BigInteger primeModulusP)
+ {
+ if ((options & P) == 0)
+ {
+ options |= P;
+ this.primeModulusP = primeModulusP;
+ }
+ else
+ {
+ throw new IllegalArgumentException("Prime Modulus P already set");
+ }
+ }
+
+ public byte[] getPublicPointY()
+ {
+ if ((options & Y) != 0)
+ {
+ return publicPointY;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ private void setPublicPointY(ASN1OctetString publicPointY)
+ throws IllegalArgumentException
+ {
+ if ((options & Y) == 0)
+ {
+ options |= Y;
+ this.publicPointY = publicPointY.getOctets();
+ }
+ else
+ {
+ throw new IllegalArgumentException("Public Point Y already set");
+ }
+ }
+
+ public BigInteger getSecondCoefB()
+ {
+ if ((options & B) != 0)
+ {
+ return secondCoefB;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ private void setSecondCoefB(BigInteger secondCoefB)
+ throws IllegalArgumentException
+ {
+ if ((options & B) == 0)
+ {
+ options |= B;
+ this.secondCoefB = secondCoefB;
+ }
+ else
+ {
+ throw new IllegalArgumentException("Second Coef B already set");
+ }
+ }
+
+ public boolean hasParameters()
+ {
+ return primeModulusP != null;
+ }
+
+ public ASN1EncodableVector getASN1EncodableVector(ASN1ObjectIdentifier oid, boolean publicPointOnly)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(oid);
+
+ if (!publicPointOnly)
+ {
+ v.add(new UnsignedInteger(0x01, getPrimeModulusP()));
+ v.add(new UnsignedInteger(0x02, getFirstCoefA()));
+ v.add(new UnsignedInteger(0x03, getSecondCoefB()));
+ v.add(new DERTaggedObject(false, 0x04, new DEROctetString(getBasePointG())));
+ v.add(new UnsignedInteger(0x05, getOrderOfBasePointR()));
+ }
+ v.add(new DERTaggedObject(false, 0x06, new DEROctetString(getPublicPointY())));
+ if (!publicPointOnly)
+ {
+ v.add(new UnsignedInteger(0x07, getCofactorF()));
+ }
+
+ return v;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return new DERSequence(getASN1EncodableVector(usage, false));
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/eac/Flags.java b/core/src/main/java/org/bouncycastle/asn1/eac/Flags.java
new file mode 100644
index 00000000..89d4e9f9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/eac/Flags.java
@@ -0,0 +1,96 @@
+package org.bouncycastle.asn1.eac;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+
+public class Flags
+{
+
+ int value = 0;
+
+ public Flags()
+ {
+
+ }
+
+ public Flags(int v)
+ {
+ value = v;
+ }
+
+ public void set(int flag)
+ {
+ value |= flag;
+ }
+
+ public boolean isSet(int flag)
+ {
+ return (value & flag) != 0;
+ }
+
+ public int getFlags()
+ {
+ return value;
+ }
+
+ /* Java 1.5
+ String decode(Map<Integer, String> decodeMap)
+ {
+ StringJoiner joiner = new StringJoiner(" ");
+ for (int i : decodeMap.keySet())
+ {
+ if (isSet(i))
+ joiner.add(decodeMap.get(i));
+ }
+ return joiner.toString();
+ }
+ */
+
+ String decode(Hashtable decodeMap)
+ {
+ StringJoiner joiner = new StringJoiner(" ");
+ Enumeration e = decodeMap.keys();
+ while (e.hasMoreElements())
+ {
+ Integer i = (Integer)e.nextElement();
+ if (isSet(i.intValue()))
+ {
+ joiner.add((String)decodeMap.get(i));
+ }
+ }
+ return joiner.toString();
+ }
+
+ private class StringJoiner
+ {
+
+ String mSeparator;
+ boolean First = true;
+ StringBuffer b = new StringBuffer();
+
+ public StringJoiner(String separator)
+ {
+ mSeparator = separator;
+ }
+
+ public void add(String str)
+ {
+ if (First)
+ {
+ First = false;
+ }
+ else
+ {
+ b.append(mSeparator);
+ }
+
+ b.append(str);
+ }
+
+ public String toString()
+ {
+ return b.toString();
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/eac/PackedDate.java b/core/src/main/java/org/bouncycastle/asn1/eac/PackedDate.java
new file mode 100644
index 00000000..29b08812
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/eac/PackedDate.java
@@ -0,0 +1,103 @@
+package org.bouncycastle.asn1.eac;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.SimpleTimeZone;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * EAC encoding date object
+ */
+public class PackedDate
+{
+ private byte[] time;
+
+ public PackedDate(
+ String time)
+ {
+ this.time = convert(time);
+ }
+
+ /**
+ * base constructer from a java.util.date object
+ */
+ public PackedDate(
+ Date time)
+ {
+ SimpleDateFormat dateF = new SimpleDateFormat("yyMMdd'Z'");
+
+ dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
+
+ this.time = convert(dateF.format(time));
+ }
+
+ private byte[] convert(String sTime)
+ {
+ char[] digs = sTime.toCharArray();
+ byte[] date = new byte[6];
+
+ for (int i = 0; i != 6; i++)
+ {
+ date[i] = (byte)(digs[i] - '0');
+ }
+
+ return date;
+ }
+
+ PackedDate(
+ byte[] bytes)
+ {
+ this.time = bytes;
+ }
+
+ /**
+ * return the time as a date based on whatever a 2 digit year will return. For
+ * standardised processing use getAdjustedDate().
+ *
+ * @return the resulting date
+ * @exception java.text.ParseException if the date string cannot be parsed.
+ */
+ public Date getDate()
+ throws ParseException
+ {
+ SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMdd");
+
+ return dateF.parse("20" + toString());
+ }
+
+ public int hashCode()
+ {
+ return Arrays.hashCode(time);
+ }
+
+ public boolean equals(Object o)
+ {
+ if (!(o instanceof PackedDate))
+ {
+ return false;
+ }
+
+ PackedDate other = (PackedDate)o;
+
+ return Arrays.areEqual(time, other.time);
+ }
+
+ public String toString()
+ {
+ char[] dateC = new char[time.length];
+
+ for (int i = 0; i != dateC.length; i++)
+ {
+ dateC[i] = (char)((time[i] & 0xff) + '0');
+ }
+
+ return new String(dateC);
+ }
+
+ public byte[] getEncoding()
+ {
+ return time;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/eac/PublicKeyDataObject.java b/core/src/main/java/org/bouncycastle/asn1/eac/PublicKeyDataObject.java
new file mode 100644
index 00000000..40ad3bb1
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/eac/PublicKeyDataObject.java
@@ -0,0 +1,35 @@
+package org.bouncycastle.asn1.eac;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Sequence;
+
+public abstract class PublicKeyDataObject
+ extends ASN1Object
+{
+ public static PublicKeyDataObject getInstance(Object obj)
+ {
+ if (obj instanceof PublicKeyDataObject)
+ {
+ return (PublicKeyDataObject)obj;
+ }
+ if (obj != null)
+ {
+ ASN1Sequence seq = ASN1Sequence.getInstance(obj);
+ ASN1ObjectIdentifier usage = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+
+ if (usage.on(EACObjectIdentifiers.id_TA_ECDSA))
+ {
+ return new ECDSAPublicKey(seq);
+ }
+ else
+ {
+ return new RSAPublicKey(seq);
+ }
+ }
+
+ return null;
+ }
+
+ public abstract ASN1ObjectIdentifier getUsage();
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/eac/RSAPublicKey.java b/core/src/main/java/org/bouncycastle/asn1/eac/RSAPublicKey.java
new file mode 100644
index 00000000..7c851699
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/eac/RSAPublicKey.java
@@ -0,0 +1,121 @@
+package org.bouncycastle.asn1.eac;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+
+/**
+ * an Iso7816RSAPublicKeyStructure structure.
+ * <p/>
+ * <pre>
+ * Certificate Holder Authorization ::= SEQUENCE {
+ * // modulus should be at least 1024bit and a multiple of 512.
+ * DERTaggedObject modulus,
+ * // access rights exponent
+ * DERTaggedObject accessRights,
+ * }
+ * </pre>
+ */
+public class RSAPublicKey
+ extends PublicKeyDataObject
+{
+ private ASN1ObjectIdentifier usage;
+ private BigInteger modulus;
+ private BigInteger exponent;
+ private int valid = 0;
+ private static int modulusValid = 0x01;
+ private static int exponentValid = 0x02;
+
+ RSAPublicKey(ASN1Sequence seq)
+ {
+ Enumeration en = seq.getObjects();
+
+ this.usage = ASN1ObjectIdentifier.getInstance(en.nextElement());
+
+ while (en.hasMoreElements())
+ {
+ UnsignedInteger val = UnsignedInteger.getInstance(en.nextElement());
+
+ switch (val.getTagNo())
+ {
+ case 0x1:
+ setModulus(val);
+ break;
+ case 0x2:
+ setExponent(val);
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown DERTaggedObject :" + val.getTagNo() + "-> not an Iso7816RSAPublicKeyStructure");
+ }
+ }
+ if (valid != 0x3)
+ {
+ throw new IllegalArgumentException("missing argument -> not an Iso7816RSAPublicKeyStructure");
+ }
+ }
+
+ public RSAPublicKey(ASN1ObjectIdentifier usage, BigInteger modulus, BigInteger exponent)
+ {
+ this.usage = usage;
+ this.modulus = modulus;
+ this.exponent = exponent;
+ }
+
+ public ASN1ObjectIdentifier getUsage()
+ {
+ return usage;
+ }
+
+ public BigInteger getModulus()
+ {
+ return modulus;
+ }
+
+ public BigInteger getPublicExponent()
+ {
+ return exponent;
+ }
+
+ private void setModulus(UnsignedInteger modulus)
+ {
+ if ((valid & modulusValid) == 0)
+ {
+ valid |= modulusValid;
+ this.modulus = modulus.getValue();
+ }
+ else
+ {
+ throw new IllegalArgumentException("Modulus already set");
+ }
+ }
+
+ private void setExponent(UnsignedInteger exponent)
+ {
+ if ((valid & exponentValid) == 0)
+ {
+ valid |= exponentValid;
+ this.exponent = exponent.getValue();
+ }
+ else
+ {
+ throw new IllegalArgumentException("Exponent already set");
+ }
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(usage);
+ v.add(new UnsignedInteger(0x01, getModulus()));
+ v.add(new UnsignedInteger(0x02, getPublicExponent()));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/eac/UnsignedInteger.java b/core/src/main/java/org/bouncycastle/asn1/eac/UnsignedInteger.java
new file mode 100644
index 00000000..64a91422
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/eac/UnsignedInteger.java
@@ -0,0 +1,74 @@
+package org.bouncycastle.asn1.eac;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class UnsignedInteger
+ extends ASN1Object
+{
+ private int tagNo;
+ private BigInteger value;
+
+ public UnsignedInteger(int tagNo, BigInteger value)
+ {
+ this.tagNo = tagNo;
+ this.value = value;
+ }
+
+ private UnsignedInteger(ASN1TaggedObject obj)
+ {
+ this.tagNo = obj.getTagNo();
+ this.value = new BigInteger(1, ASN1OctetString.getInstance(obj, false).getOctets());
+ }
+
+ public static UnsignedInteger getInstance(Object obj)
+ {
+ if (obj instanceof UnsignedInteger)
+ {
+ return (UnsignedInteger)obj;
+ }
+ if (obj != null)
+ {
+ return new UnsignedInteger(ASN1TaggedObject.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private byte[] convertValue()
+ {
+ byte[] v = value.toByteArray();
+
+ if (v[0] == 0)
+ {
+ byte[] tmp = new byte[v.length - 1];
+
+ System.arraycopy(v, 1, tmp, 0, tmp.length);
+
+ return tmp;
+ }
+
+ return v;
+ }
+
+ public int getTagNo()
+ {
+ return tagNo;
+ }
+
+ public BigInteger getValue()
+ {
+ return value;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return new DERTaggedObject(false, tagNo, new DEROctetString(convertValue()));
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/CommitmentTypeIdentifier.java b/core/src/main/java/org/bouncycastle/asn1/esf/CommitmentTypeIdentifier.java
new file mode 100644
index 00000000..be52e45a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/CommitmentTypeIdentifier.java
@@ -0,0 +1,14 @@
+package org.bouncycastle.asn1.esf;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+
+public interface CommitmentTypeIdentifier
+{
+ public static final ASN1ObjectIdentifier proofOfOrigin = PKCSObjectIdentifiers.id_cti_ets_proofOfOrigin;
+ public static final ASN1ObjectIdentifier proofOfReceipt = PKCSObjectIdentifiers.id_cti_ets_proofOfReceipt;
+ public static final ASN1ObjectIdentifier proofOfDelivery = PKCSObjectIdentifiers.id_cti_ets_proofOfDelivery;
+ public static final ASN1ObjectIdentifier proofOfSender = PKCSObjectIdentifiers.id_cti_ets_proofOfSender;
+ public static final ASN1ObjectIdentifier proofOfApproval = PKCSObjectIdentifiers.id_cti_ets_proofOfApproval;
+ public static final ASN1ObjectIdentifier proofOfCreation = PKCSObjectIdentifiers.id_cti_ets_proofOfCreation;
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/CommitmentTypeIndication.java b/core/src/main/java/org/bouncycastle/asn1/esf/CommitmentTypeIndication.java
new file mode 100644
index 00000000..9e2533d9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/CommitmentTypeIndication.java
@@ -0,0 +1,83 @@
+package org.bouncycastle.asn1.esf;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class CommitmentTypeIndication
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier commitmentTypeId;
+ private ASN1Sequence commitmentTypeQualifier;
+
+ private CommitmentTypeIndication(
+ ASN1Sequence seq)
+ {
+ commitmentTypeId = (ASN1ObjectIdentifier)seq.getObjectAt(0);
+
+ if (seq.size() > 1)
+ {
+ commitmentTypeQualifier = (ASN1Sequence)seq.getObjectAt(1);
+ }
+ }
+
+ public CommitmentTypeIndication(
+ ASN1ObjectIdentifier commitmentTypeId)
+ {
+ this.commitmentTypeId = commitmentTypeId;
+ }
+
+ public CommitmentTypeIndication(
+ ASN1ObjectIdentifier commitmentTypeId,
+ ASN1Sequence commitmentTypeQualifier)
+ {
+ this.commitmentTypeId = commitmentTypeId;
+ this.commitmentTypeQualifier = commitmentTypeQualifier;
+ }
+
+ public static CommitmentTypeIndication getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof CommitmentTypeIndication)
+ {
+ return (CommitmentTypeIndication)obj;
+ }
+
+ return new CommitmentTypeIndication(ASN1Sequence.getInstance(obj));
+ }
+
+ public ASN1ObjectIdentifier getCommitmentTypeId()
+ {
+ return commitmentTypeId;
+ }
+
+ public ASN1Sequence getCommitmentTypeQualifier()
+ {
+ return commitmentTypeQualifier;
+ }
+
+ /**
+ * <pre>
+ * CommitmentTypeIndication ::= SEQUENCE {
+ * commitmentTypeId CommitmentTypeIdentifier,
+ * commitmentTypeQualifier SEQUENCE SIZE (1..MAX) OF
+ * CommitmentTypeQualifier OPTIONAL }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(commitmentTypeId);
+
+ if (commitmentTypeQualifier != null)
+ {
+ v.add(commitmentTypeQualifier);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/CommitmentTypeQualifier.java b/core/src/main/java/org/bouncycastle/asn1/esf/CommitmentTypeQualifier.java
new file mode 100644
index 00000000..2cbba924
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/CommitmentTypeQualifier.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.asn1.esf;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * Commitment type qualifiers, used in the Commitment-Type-Indication attribute (RFC3126).
+ *
+ * <pre>
+ * CommitmentTypeQualifier ::= SEQUENCE {
+ * commitmentTypeIdentifier CommitmentTypeIdentifier,
+ * qualifier ANY DEFINED BY commitmentTypeIdentifier OPTIONAL }
+ * </pre>
+ */
+public class CommitmentTypeQualifier
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier commitmentTypeIdentifier;
+ private ASN1Encodable qualifier;
+
+ /**
+ * Creates a new <code>CommitmentTypeQualifier</code> instance.
+ *
+ * @param commitmentTypeIdentifier a <code>CommitmentTypeIdentifier</code> value
+ */
+ public CommitmentTypeQualifier(
+ ASN1ObjectIdentifier commitmentTypeIdentifier)
+ {
+ this(commitmentTypeIdentifier, null);
+ }
+
+ /**
+ * Creates a new <code>CommitmentTypeQualifier</code> instance.
+ *
+ * @param commitmentTypeIdentifier a <code>CommitmentTypeIdentifier</code> value
+ * @param qualifier the qualifier, defined by the above field.
+ */
+ public CommitmentTypeQualifier(
+ ASN1ObjectIdentifier commitmentTypeIdentifier,
+ ASN1Encodable qualifier)
+ {
+ this.commitmentTypeIdentifier = commitmentTypeIdentifier;
+ this.qualifier = qualifier;
+ }
+
+ /**
+ * Creates a new <code>CommitmentTypeQualifier</code> instance.
+ *
+ * @param as <code>CommitmentTypeQualifier</code> structure
+ * encoded as an ASN1Sequence.
+ */
+ private CommitmentTypeQualifier(
+ ASN1Sequence as)
+ {
+ commitmentTypeIdentifier = (ASN1ObjectIdentifier)as.getObjectAt(0);
+
+ if (as.size() > 1)
+ {
+ qualifier = as.getObjectAt(1);
+ }
+ }
+
+ public static CommitmentTypeQualifier getInstance(Object as)
+ {
+ if (as instanceof CommitmentTypeQualifier)
+ {
+ return (CommitmentTypeQualifier)as;
+ }
+ else if (as != null)
+ {
+ return new CommitmentTypeQualifier(ASN1Sequence.getInstance(as));
+ }
+
+ return null;
+ }
+
+ public ASN1ObjectIdentifier getCommitmentTypeIdentifier()
+ {
+ return commitmentTypeIdentifier;
+ }
+
+ public ASN1Encodable getQualifier()
+ {
+ return qualifier;
+ }
+
+ /**
+ * Returns a DER-encodable representation of this instance.
+ *
+ * @return a <code>ASN1Primitive</code> value
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector dev = new ASN1EncodableVector();
+ dev.add(commitmentTypeIdentifier);
+ if (qualifier != null)
+ {
+ dev.add(qualifier);
+ }
+
+ return new DERSequence(dev);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/CompleteRevocationRefs.java b/core/src/main/java/org/bouncycastle/asn1/esf/CompleteRevocationRefs.java
new file mode 100644
index 00000000..4e81f293
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/CompleteRevocationRefs.java
@@ -0,0 +1,65 @@
+package org.bouncycastle.asn1.esf;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * <pre>
+ * CompleteRevocationRefs ::= SEQUENCE OF CrlOcspRef
+ * </pre>
+ */
+public class CompleteRevocationRefs
+ extends ASN1Object
+{
+
+ private ASN1Sequence crlOcspRefs;
+
+ public static CompleteRevocationRefs getInstance(Object obj)
+ {
+ if (obj instanceof CompleteRevocationRefs)
+ {
+ return (CompleteRevocationRefs)obj;
+ }
+ else if (obj != null)
+ {
+ return new CompleteRevocationRefs(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private CompleteRevocationRefs(ASN1Sequence seq)
+ {
+ Enumeration seqEnum = seq.getObjects();
+ while (seqEnum.hasMoreElements())
+ {
+ CrlOcspRef.getInstance(seqEnum.nextElement());
+ }
+ this.crlOcspRefs = seq;
+ }
+
+ public CompleteRevocationRefs(CrlOcspRef[] crlOcspRefs)
+ {
+ this.crlOcspRefs = new DERSequence(crlOcspRefs);
+ }
+
+ public CrlOcspRef[] getCrlOcspRefs()
+ {
+ CrlOcspRef[] result = new CrlOcspRef[this.crlOcspRefs.size()];
+ for (int idx = 0; idx < result.length; idx++)
+ {
+ result[idx] = CrlOcspRef.getInstance(this.crlOcspRefs
+ .getObjectAt(idx));
+ }
+ return result;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return this.crlOcspRefs;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/CrlIdentifier.java b/core/src/main/java/org/bouncycastle/asn1/esf/CrlIdentifier.java
new file mode 100644
index 00000000..68004185
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/CrlIdentifier.java
@@ -0,0 +1,106 @@
+package org.bouncycastle.asn1.esf;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1UTCTime;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x500.X500Name;
+
+/**
+ * <pre>
+ * CrlIdentifier ::= SEQUENCE
+ * {
+ * crlissuer Name,
+ * crlIssuedTime UTCTime,
+ * crlNumber INTEGER OPTIONAL
+ * }
+ * </pre>
+ */
+public class CrlIdentifier
+ extends ASN1Object
+{
+ private X500Name crlIssuer;
+ private ASN1UTCTime crlIssuedTime;
+ private ASN1Integer crlNumber;
+
+ public static CrlIdentifier getInstance(Object obj)
+ {
+ if (obj instanceof CrlIdentifier)
+ {
+ return (CrlIdentifier)obj;
+ }
+ else if (obj != null)
+ {
+ return new CrlIdentifier(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private CrlIdentifier(ASN1Sequence seq)
+ {
+ if (seq.size() < 2 || seq.size() > 3)
+ {
+ throw new IllegalArgumentException();
+ }
+ this.crlIssuer = X500Name.getInstance(seq.getObjectAt(0));
+ this.crlIssuedTime = ASN1UTCTime.getInstance(seq.getObjectAt(1));
+ if (seq.size() > 2)
+ {
+ this.crlNumber = ASN1Integer.getInstance(seq.getObjectAt(2));
+ }
+ }
+
+ public CrlIdentifier(X500Name crlIssuer, ASN1UTCTime crlIssuedTime)
+ {
+ this(crlIssuer, crlIssuedTime, null);
+ }
+
+ public CrlIdentifier(X500Name crlIssuer, ASN1UTCTime crlIssuedTime,
+ BigInteger crlNumber)
+ {
+ this.crlIssuer = crlIssuer;
+ this.crlIssuedTime = crlIssuedTime;
+ if (null != crlNumber)
+ {
+ this.crlNumber = new ASN1Integer(crlNumber);
+ }
+ }
+
+ public X500Name getCrlIssuer()
+ {
+ return this.crlIssuer;
+ }
+
+ public ASN1UTCTime getCrlIssuedTime()
+ {
+ return this.crlIssuedTime;
+ }
+
+ public BigInteger getCrlNumber()
+ {
+ if (null == this.crlNumber)
+ {
+ return null;
+ }
+ return this.crlNumber.getValue();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(this.crlIssuer.toASN1Primitive());
+ v.add(this.crlIssuedTime);
+ if (null != this.crlNumber)
+ {
+ v.add(this.crlNumber);
+ }
+ return new DERSequence(v);
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/CrlListID.java b/core/src/main/java/org/bouncycastle/asn1/esf/CrlListID.java
new file mode 100644
index 00000000..c0cb333c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/CrlListID.java
@@ -0,0 +1,66 @@
+package org.bouncycastle.asn1.esf;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * <pre>
+ * CRLListID ::= SEQUENCE {
+ * crls SEQUENCE OF CrlValidatedID }
+ * </pre>
+ */
+public class CrlListID
+ extends ASN1Object
+{
+
+ private ASN1Sequence crls;
+
+ public static CrlListID getInstance(Object obj)
+ {
+ if (obj instanceof CrlListID)
+ {
+ return (CrlListID)obj;
+ }
+ else if (obj != null)
+ {
+ return new CrlListID(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private CrlListID(ASN1Sequence seq)
+ {
+ this.crls = (ASN1Sequence)seq.getObjectAt(0);
+ Enumeration e = this.crls.getObjects();
+ while (e.hasMoreElements())
+ {
+ CrlValidatedID.getInstance(e.nextElement());
+ }
+ }
+
+ public CrlListID(CrlValidatedID[] crls)
+ {
+ this.crls = new DERSequence(crls);
+ }
+
+ public CrlValidatedID[] getCrls()
+ {
+ CrlValidatedID[] result = new CrlValidatedID[this.crls.size()];
+ for (int idx = 0; idx < result.length; idx++)
+ {
+ result[idx] = CrlValidatedID
+ .getInstance(this.crls.getObjectAt(idx));
+ }
+ return result;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return new DERSequence(this.crls);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/CrlOcspRef.java b/core/src/main/java/org/bouncycastle/asn1/esf/CrlOcspRef.java
new file mode 100644
index 00000000..39539f34
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/CrlOcspRef.java
@@ -0,0 +1,106 @@
+package org.bouncycastle.asn1.esf;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * <pre>
+ * CrlOcspRef ::= SEQUENCE {
+ * crlids [0] CRLListID OPTIONAL,
+ * ocspids [1] OcspListID OPTIONAL,
+ * otherRev [2] OtherRevRefs OPTIONAL
+ * }
+ * </pre>
+ */
+public class CrlOcspRef
+ extends ASN1Object
+{
+
+ private CrlListID crlids;
+ private OcspListID ocspids;
+ private OtherRevRefs otherRev;
+
+ public static CrlOcspRef getInstance(Object obj)
+ {
+ if (obj instanceof CrlOcspRef)
+ {
+ return (CrlOcspRef)obj;
+ }
+ else if (obj != null)
+ {
+ return new CrlOcspRef(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private CrlOcspRef(ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+ while (e.hasMoreElements())
+ {
+ DERTaggedObject o = (DERTaggedObject)e.nextElement();
+ switch (o.getTagNo())
+ {
+ case 0:
+ this.crlids = CrlListID.getInstance(o.getObject());
+ break;
+ case 1:
+ this.ocspids = OcspListID.getInstance(o.getObject());
+ break;
+ case 2:
+ this.otherRev = OtherRevRefs.getInstance(o.getObject());
+ break;
+ default:
+ throw new IllegalArgumentException("illegal tag");
+ }
+ }
+ }
+
+ public CrlOcspRef(CrlListID crlids, OcspListID ocspids,
+ OtherRevRefs otherRev)
+ {
+ this.crlids = crlids;
+ this.ocspids = ocspids;
+ this.otherRev = otherRev;
+ }
+
+ public CrlListID getCrlids()
+ {
+ return this.crlids;
+ }
+
+ public OcspListID getOcspids()
+ {
+ return this.ocspids;
+ }
+
+ public OtherRevRefs getOtherRev()
+ {
+ return this.otherRev;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ if (null != this.crlids)
+ {
+ v.add(new DERTaggedObject(true, 0, this.crlids.toASN1Primitive()));
+ }
+ if (null != this.ocspids)
+ {
+ v.add(new DERTaggedObject(true, 1, this.ocspids.toASN1Primitive()));
+ }
+ if (null != this.otherRev)
+ {
+ v.add(new DERTaggedObject(true, 2, this.otherRev.toASN1Primitive()));
+ }
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/CrlValidatedID.java b/core/src/main/java/org/bouncycastle/asn1/esf/CrlValidatedID.java
new file mode 100644
index 00000000..b378aeaf
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/CrlValidatedID.java
@@ -0,0 +1,82 @@
+package org.bouncycastle.asn1.esf;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * <pre>
+ * CrlValidatedID ::= SEQUENCE {
+ * crlHash OtherHash,
+ * crlIdentifier CrlIdentifier OPTIONAL }
+ * </pre>
+ */
+public class CrlValidatedID
+ extends ASN1Object
+{
+
+ private OtherHash crlHash;
+ private CrlIdentifier crlIdentifier;
+
+ public static CrlValidatedID getInstance(Object obj)
+ {
+ if (obj instanceof CrlValidatedID)
+ {
+ return (CrlValidatedID)obj;
+ }
+ else if (obj != null)
+ {
+ return new CrlValidatedID(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private CrlValidatedID(ASN1Sequence seq)
+ {
+ if (seq.size() < 1 || seq.size() > 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+ this.crlHash = OtherHash.getInstance(seq.getObjectAt(0));
+ if (seq.size() > 1)
+ {
+ this.crlIdentifier = CrlIdentifier.getInstance(seq.getObjectAt(1));
+ }
+ }
+
+ public CrlValidatedID(OtherHash crlHash)
+ {
+ this(crlHash, null);
+ }
+
+ public CrlValidatedID(OtherHash crlHash, CrlIdentifier crlIdentifier)
+ {
+ this.crlHash = crlHash;
+ this.crlIdentifier = crlIdentifier;
+ }
+
+ public OtherHash getCrlHash()
+ {
+ return this.crlHash;
+ }
+
+ public CrlIdentifier getCrlIdentifier()
+ {
+ return this.crlIdentifier;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(this.crlHash.toASN1Primitive());
+ if (null != this.crlIdentifier)
+ {
+ v.add(this.crlIdentifier.toASN1Primitive());
+ }
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/ESFAttributes.java b/core/src/main/java/org/bouncycastle/asn1/esf/ESFAttributes.java
new file mode 100644
index 00000000..ebdc5ead
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/ESFAttributes.java
@@ -0,0 +1,22 @@
+package org.bouncycastle.asn1.esf;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+
+public interface ESFAttributes
+{
+ public static final ASN1ObjectIdentifier sigPolicyId = PKCSObjectIdentifiers.id_aa_ets_sigPolicyId;
+ public static final ASN1ObjectIdentifier commitmentType = PKCSObjectIdentifiers.id_aa_ets_commitmentType;
+ public static final ASN1ObjectIdentifier signerLocation = PKCSObjectIdentifiers.id_aa_ets_signerLocation;
+ public static final ASN1ObjectIdentifier signerAttr = PKCSObjectIdentifiers.id_aa_ets_signerAttr;
+ public static final ASN1ObjectIdentifier otherSigCert = PKCSObjectIdentifiers.id_aa_ets_otherSigCert;
+ public static final ASN1ObjectIdentifier contentTimestamp = PKCSObjectIdentifiers.id_aa_ets_contentTimestamp;
+ public static final ASN1ObjectIdentifier certificateRefs = PKCSObjectIdentifiers.id_aa_ets_certificateRefs;
+ public static final ASN1ObjectIdentifier revocationRefs = PKCSObjectIdentifiers.id_aa_ets_revocationRefs;
+ public static final ASN1ObjectIdentifier certValues = PKCSObjectIdentifiers.id_aa_ets_certValues;
+ public static final ASN1ObjectIdentifier revocationValues = PKCSObjectIdentifiers.id_aa_ets_revocationValues;
+ public static final ASN1ObjectIdentifier escTimeStamp = PKCSObjectIdentifiers.id_aa_ets_escTimeStamp;
+ public static final ASN1ObjectIdentifier certCRLTimestamp = PKCSObjectIdentifiers.id_aa_ets_certCRLTimestamp;
+ public static final ASN1ObjectIdentifier archiveTimestamp = PKCSObjectIdentifiers.id_aa_ets_archiveTimestamp;
+ public static final ASN1ObjectIdentifier archiveTimestampV2 = PKCSObjectIdentifiers.id_aa.branch("48");
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/OcspIdentifier.java b/core/src/main/java/org/bouncycastle/asn1/esf/OcspIdentifier.java
new file mode 100644
index 00000000..a3c41d47
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/OcspIdentifier.java
@@ -0,0 +1,73 @@
+package org.bouncycastle.asn1.esf;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.ocsp.ResponderID;
+
+/**
+ * <pre>
+ * OcspIdentifier ::= SEQUENCE {
+ * ocspResponderID ResponderID, -- As in OCSP response data
+ * producedAt GeneralizedTime -- As in OCSP response data
+ * }
+ * </pre>
+ */
+public class OcspIdentifier
+ extends ASN1Object
+{
+ private ResponderID ocspResponderID;
+ private ASN1GeneralizedTime producedAt;
+
+ public static OcspIdentifier getInstance(Object obj)
+ {
+ if (obj instanceof OcspIdentifier)
+ {
+ return (OcspIdentifier)obj;
+ }
+ else if (obj != null)
+ {
+ return new OcspIdentifier(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private OcspIdentifier(ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+ this.ocspResponderID = ResponderID.getInstance(seq.getObjectAt(0));
+ this.producedAt = (ASN1GeneralizedTime)seq.getObjectAt(1);
+ }
+
+ public OcspIdentifier(ResponderID ocspResponderID, ASN1GeneralizedTime producedAt)
+ {
+ this.ocspResponderID = ocspResponderID;
+ this.producedAt = producedAt;
+ }
+
+ public ResponderID getOcspResponderID()
+ {
+ return this.ocspResponderID;
+ }
+
+ public ASN1GeneralizedTime getProducedAt()
+ {
+ return this.producedAt;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(this.ocspResponderID);
+ v.add(this.producedAt);
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/OcspListID.java b/core/src/main/java/org/bouncycastle/asn1/esf/OcspListID.java
new file mode 100644
index 00000000..349136f1
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/OcspListID.java
@@ -0,0 +1,72 @@
+package org.bouncycastle.asn1.esf;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * <pre>
+ * OcspListID ::= SEQUENCE {
+ * ocspResponses SEQUENCE OF OcspResponsesID
+ * }
+ * </pre>
+ */
+public class OcspListID
+ extends ASN1Object
+{
+ private ASN1Sequence ocspResponses;
+
+ public static OcspListID getInstance(Object obj)
+ {
+ if (obj instanceof OcspListID)
+ {
+ return (OcspListID)obj;
+ }
+ else if (obj != null)
+ {
+ return new OcspListID(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private OcspListID(ASN1Sequence seq)
+ {
+ if (seq.size() != 1)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+ this.ocspResponses = (ASN1Sequence)seq.getObjectAt(0);
+ Enumeration e = this.ocspResponses.getObjects();
+ while (e.hasMoreElements())
+ {
+ OcspResponsesID.getInstance(e.nextElement());
+ }
+ }
+
+ public OcspListID(OcspResponsesID[] ocspResponses)
+ {
+ this.ocspResponses = new DERSequence(ocspResponses);
+ }
+
+ public OcspResponsesID[] getOcspResponses()
+ {
+ OcspResponsesID[] result = new OcspResponsesID[this.ocspResponses
+ .size()];
+ for (int idx = 0; idx < result.length; idx++)
+ {
+ result[idx] = OcspResponsesID.getInstance(this.ocspResponses
+ .getObjectAt(idx));
+ }
+ return result;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return new DERSequence(this.ocspResponses);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/OcspResponsesID.java b/core/src/main/java/org/bouncycastle/asn1/esf/OcspResponsesID.java
new file mode 100644
index 00000000..2aac80e2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/OcspResponsesID.java
@@ -0,0 +1,83 @@
+package org.bouncycastle.asn1.esf;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * <pre>
+ * OcspResponsesID ::= SEQUENCE {
+ * ocspIdentifier OcspIdentifier,
+ * ocspRepHash OtherHash OPTIONAL
+ * }
+ * </pre>
+ */
+public class OcspResponsesID
+ extends ASN1Object
+{
+
+ private OcspIdentifier ocspIdentifier;
+ private OtherHash ocspRepHash;
+
+ public static OcspResponsesID getInstance(Object obj)
+ {
+ if (obj instanceof OcspResponsesID)
+ {
+ return (OcspResponsesID)obj;
+ }
+ else if (obj != null)
+ {
+ return new OcspResponsesID(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private OcspResponsesID(ASN1Sequence seq)
+ {
+ if (seq.size() < 1 || seq.size() > 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+ this.ocspIdentifier = OcspIdentifier.getInstance(seq.getObjectAt(0));
+ if (seq.size() > 1)
+ {
+ this.ocspRepHash = OtherHash.getInstance(seq.getObjectAt(1));
+ }
+ }
+
+ public OcspResponsesID(OcspIdentifier ocspIdentifier)
+ {
+ this(ocspIdentifier, null);
+ }
+
+ public OcspResponsesID(OcspIdentifier ocspIdentifier, OtherHash ocspRepHash)
+ {
+ this.ocspIdentifier = ocspIdentifier;
+ this.ocspRepHash = ocspRepHash;
+ }
+
+ public OcspIdentifier getOcspIdentifier()
+ {
+ return this.ocspIdentifier;
+ }
+
+ public OtherHash getOcspRepHash()
+ {
+ return this.ocspRepHash;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(this.ocspIdentifier);
+ if (null != this.ocspRepHash)
+ {
+ v.add(this.ocspRepHash);
+ }
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/OtherHash.java b/core/src/main/java/org/bouncycastle/asn1/esf/OtherHash.java
new file mode 100644
index 00000000..0ec257d8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/OtherHash.java
@@ -0,0 +1,81 @@
+package org.bouncycastle.asn1.esf;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * <pre>
+ * OtherHash ::= CHOICE {
+ * sha1Hash OtherHashValue, -- This contains a SHA-1 hash
+ * otherHash OtherHashAlgAndValue
+ * }
+ * </pre>
+ */
+public class OtherHash
+ extends ASN1Object
+ implements ASN1Choice
+{
+
+ private ASN1OctetString sha1Hash;
+ private OtherHashAlgAndValue otherHash;
+
+ public static OtherHash getInstance(Object obj)
+ {
+ if (obj instanceof OtherHash)
+ {
+ return (OtherHash)obj;
+ }
+ if (obj instanceof ASN1OctetString)
+ {
+ return new OtherHash((ASN1OctetString)obj);
+ }
+ return new OtherHash(OtherHashAlgAndValue.getInstance(obj));
+ }
+
+ private OtherHash(ASN1OctetString sha1Hash)
+ {
+ this.sha1Hash = sha1Hash;
+ }
+
+ public OtherHash(OtherHashAlgAndValue otherHash)
+ {
+ this.otherHash = otherHash;
+ }
+
+ public OtherHash(byte[] sha1Hash)
+ {
+ this.sha1Hash = new DEROctetString(sha1Hash);
+ }
+
+ public AlgorithmIdentifier getHashAlgorithm()
+ {
+ if (null == this.otherHash)
+ {
+ return new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1);
+ }
+ return this.otherHash.getHashAlgorithm();
+ }
+
+ public byte[] getHashValue()
+ {
+ if (null == this.otherHash)
+ {
+ return this.sha1Hash.getOctets();
+ }
+ return this.otherHash.getHashValue().getOctets();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ if (null == this.otherHash)
+ {
+ return this.sha1Hash;
+ }
+ return this.otherHash.toASN1Primitive();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/OtherHashAlgAndValue.java b/core/src/main/java/org/bouncycastle/asn1/esf/OtherHashAlgAndValue.java
new file mode 100644
index 00000000..34229d4e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/OtherHashAlgAndValue.java
@@ -0,0 +1,81 @@
+package org.bouncycastle.asn1.esf;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class OtherHashAlgAndValue
+ extends ASN1Object
+{
+ private AlgorithmIdentifier hashAlgorithm;
+ private ASN1OctetString hashValue;
+
+
+ public static OtherHashAlgAndValue getInstance(
+ Object obj)
+ {
+ if (obj instanceof OtherHashAlgAndValue)
+ {
+ return (OtherHashAlgAndValue) obj;
+ }
+ else if (obj != null)
+ {
+ return new OtherHashAlgAndValue(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private OtherHashAlgAndValue(
+ ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ hashAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(0));
+ hashValue = ASN1OctetString.getInstance(seq.getObjectAt(1));
+ }
+
+ public OtherHashAlgAndValue(
+ AlgorithmIdentifier hashAlgorithm,
+ ASN1OctetString hashValue)
+ {
+ this.hashAlgorithm = hashAlgorithm;
+ this.hashValue = hashValue;
+ }
+
+ public AlgorithmIdentifier getHashAlgorithm()
+ {
+ return hashAlgorithm;
+ }
+
+ public ASN1OctetString getHashValue()
+ {
+ return hashValue;
+ }
+
+ /**
+ * <pre>
+ * OtherHashAlgAndValue ::= SEQUENCE {
+ * hashAlgorithm AlgorithmIdentifier,
+ * hashValue OtherHashValue }
+ *
+ * OtherHashValue ::= OCTET STRING
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(hashAlgorithm);
+ v.add(hashValue);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/OtherRevRefs.java b/core/src/main/java/org/bouncycastle/asn1/esf/OtherRevRefs.java
new file mode 100644
index 00000000..ed9a9b30
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/OtherRevRefs.java
@@ -0,0 +1,87 @@
+package org.bouncycastle.asn1.esf;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * <pre>
+ * OtherRevRefs ::= SEQUENCE {
+ * otherRevRefType OtherRevRefType,
+ * otherRevRefs ANY DEFINED BY otherRevRefType
+ * }
+ *
+ * OtherRevRefType ::= OBJECT IDENTIFIER
+ * </pre>
+ */
+public class OtherRevRefs
+ extends ASN1Object
+{
+
+ private ASN1ObjectIdentifier otherRevRefType;
+ private ASN1Encodable otherRevRefs;
+
+ public static OtherRevRefs getInstance(Object obj)
+ {
+ if (obj instanceof OtherRevRefs)
+ {
+ return (OtherRevRefs)obj;
+ }
+ else if (obj != null)
+ {
+ return new OtherRevRefs(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private OtherRevRefs(ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+ this.otherRevRefType = new ASN1ObjectIdentifier(((ASN1ObjectIdentifier)seq.getObjectAt(0)).getId());
+ try
+ {
+ this.otherRevRefs = ASN1Primitive.fromByteArray(seq.getObjectAt(1)
+ .toASN1Primitive().getEncoded(ASN1Encoding.DER));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException();
+ }
+ }
+
+ public OtherRevRefs(ASN1ObjectIdentifier otherRevRefType, ASN1Encodable otherRevRefs)
+ {
+ this.otherRevRefType = otherRevRefType;
+ this.otherRevRefs = otherRevRefs;
+ }
+
+ public ASN1ObjectIdentifier getOtherRevRefType()
+ {
+ return this.otherRevRefType;
+ }
+
+ public ASN1Encodable getOtherRevRefs()
+ {
+ return this.otherRevRefs;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(this.otherRevRefType);
+ v.add(this.otherRevRefs);
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/OtherRevVals.java b/core/src/main/java/org/bouncycastle/asn1/esf/OtherRevVals.java
new file mode 100644
index 00000000..7389bdfc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/OtherRevVals.java
@@ -0,0 +1,89 @@
+package org.bouncycastle.asn1.esf;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * <pre>
+ * OtherRevVals ::= SEQUENCE {
+ * otherRevValType OtherRevValType,
+ * otherRevVals ANY DEFINED BY OtherRevValType
+ * }
+ *
+ * OtherRevValType ::= OBJECT IDENTIFIER
+ * </pre>
+ */
+public class OtherRevVals
+ extends ASN1Object
+{
+
+ private ASN1ObjectIdentifier otherRevValType;
+
+ private ASN1Encodable otherRevVals;
+
+ public static OtherRevVals getInstance(Object obj)
+ {
+ if (obj instanceof OtherRevVals)
+ {
+ return (OtherRevVals)obj;
+ }
+ if (obj != null)
+ {
+ return new OtherRevVals(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private OtherRevVals(ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+ this.otherRevValType = (ASN1ObjectIdentifier)seq.getObjectAt(0);
+ try
+ {
+ this.otherRevVals = ASN1Primitive.fromByteArray(seq.getObjectAt(1)
+ .toASN1Primitive().getEncoded(ASN1Encoding.DER));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException();
+ }
+ }
+
+ public OtherRevVals(ASN1ObjectIdentifier otherRevValType,
+ ASN1Encodable otherRevVals)
+ {
+ this.otherRevValType = otherRevValType;
+ this.otherRevVals = otherRevVals;
+ }
+
+ public ASN1ObjectIdentifier getOtherRevValType()
+ {
+ return this.otherRevValType;
+ }
+
+ public ASN1Encodable getOtherRevVals()
+ {
+ return this.otherRevVals;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(this.otherRevValType);
+ v.add(this.otherRevVals);
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/RevocationValues.java b/core/src/main/java/org/bouncycastle/asn1/esf/RevocationValues.java
new file mode 100644
index 00000000..9ff41131
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/RevocationValues.java
@@ -0,0 +1,151 @@
+package org.bouncycastle.asn1.esf;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.ocsp.BasicOCSPResponse;
+import org.bouncycastle.asn1.x509.CertificateList;
+
+/**
+ * <pre>
+ * RevocationValues ::= SEQUENCE {
+ * crlVals [0] SEQUENCE OF CertificateList OPTIONAL,
+ * ocspVals [1] SEQUENCE OF BasicOCSPResponse OPTIONAL,
+ * otherRevVals [2] OtherRevVals OPTIONAL}
+ * </pre>
+ */
+public class RevocationValues
+ extends ASN1Object
+{
+
+ private ASN1Sequence crlVals;
+ private ASN1Sequence ocspVals;
+ private OtherRevVals otherRevVals;
+
+ public static RevocationValues getInstance(Object obj)
+ {
+ if (obj instanceof RevocationValues)
+ {
+ return (RevocationValues)obj;
+ }
+ else if (obj != null)
+ {
+ return new RevocationValues(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private RevocationValues(ASN1Sequence seq)
+ {
+ if (seq.size() > 3)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+ Enumeration e = seq.getObjects();
+ while (e.hasMoreElements())
+ {
+ DERTaggedObject o = (DERTaggedObject)e.nextElement();
+ switch (o.getTagNo())
+ {
+ case 0:
+ ASN1Sequence crlValsSeq = (ASN1Sequence)o.getObject();
+ Enumeration crlValsEnum = crlValsSeq.getObjects();
+ while (crlValsEnum.hasMoreElements())
+ {
+ CertificateList.getInstance(crlValsEnum.nextElement());
+ }
+ this.crlVals = crlValsSeq;
+ break;
+ case 1:
+ ASN1Sequence ocspValsSeq = (ASN1Sequence)o.getObject();
+ Enumeration ocspValsEnum = ocspValsSeq.getObjects();
+ while (ocspValsEnum.hasMoreElements())
+ {
+ BasicOCSPResponse.getInstance(ocspValsEnum.nextElement());
+ }
+ this.ocspVals = ocspValsSeq;
+ break;
+ case 2:
+ this.otherRevVals = OtherRevVals.getInstance(o.getObject());
+ break;
+ default:
+ throw new IllegalArgumentException("invalid tag: "
+ + o.getTagNo());
+ }
+ }
+ }
+
+ public RevocationValues(CertificateList[] crlVals,
+ BasicOCSPResponse[] ocspVals, OtherRevVals otherRevVals)
+ {
+ if (null != crlVals)
+ {
+ this.crlVals = new DERSequence(crlVals);
+ }
+ if (null != ocspVals)
+ {
+ this.ocspVals = new DERSequence(ocspVals);
+ }
+ this.otherRevVals = otherRevVals;
+ }
+
+ public CertificateList[] getCrlVals()
+ {
+ if (null == this.crlVals)
+ {
+ return new CertificateList[0];
+ }
+ CertificateList[] result = new CertificateList[this.crlVals.size()];
+ for (int idx = 0; idx < result.length; idx++)
+ {
+ result[idx] = CertificateList.getInstance(this.crlVals
+ .getObjectAt(idx));
+ }
+ return result;
+ }
+
+ public BasicOCSPResponse[] getOcspVals()
+ {
+ if (null == this.ocspVals)
+ {
+ return new BasicOCSPResponse[0];
+ }
+ BasicOCSPResponse[] result = new BasicOCSPResponse[this.ocspVals.size()];
+ for (int idx = 0; idx < result.length; idx++)
+ {
+ result[idx] = BasicOCSPResponse.getInstance(this.ocspVals
+ .getObjectAt(idx));
+ }
+ return result;
+ }
+
+ public OtherRevVals getOtherRevVals()
+ {
+ return this.otherRevVals;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ if (null != this.crlVals)
+ {
+ v.add(new DERTaggedObject(true, 0, this.crlVals));
+ }
+ if (null != this.ocspVals)
+ {
+ v.add(new DERTaggedObject(true, 1, this.ocspVals));
+ }
+ if (null != this.otherRevVals)
+ {
+ v.add(new DERTaggedObject(true, 2, this.otherRevVals.toASN1Primitive()));
+ }
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/SPUserNotice.java b/core/src/main/java/org/bouncycastle/asn1/esf/SPUserNotice.java
new file mode 100644
index 00000000..c026cde9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/SPUserNotice.java
@@ -0,0 +1,99 @@
+package org.bouncycastle.asn1.esf;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1String;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.DisplayText;
+import org.bouncycastle.asn1.x509.NoticeReference;
+
+public class SPUserNotice
+ extends ASN1Object
+{
+ private NoticeReference noticeRef;
+ private DisplayText explicitText;
+
+ public static SPUserNotice getInstance(
+ Object obj)
+ {
+ if (obj instanceof SPUserNotice)
+ {
+ return (SPUserNotice)obj;
+ }
+ else if (obj != null)
+ {
+ return new SPUserNotice(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private SPUserNotice(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+ while (e.hasMoreElements())
+ {
+ ASN1Encodable object = (ASN1Encodable)e.nextElement();
+ if (object instanceof DisplayText || object instanceof ASN1String)
+ {
+ explicitText = DisplayText.getInstance(object);
+ }
+ else if (object instanceof NoticeReference || object instanceof ASN1Sequence)
+ {
+ noticeRef = NoticeReference.getInstance(object);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Invalid element in 'SPUserNotice': " + object.getClass().getName());
+ }
+ }
+ }
+
+ public SPUserNotice(
+ NoticeReference noticeRef,
+ DisplayText explicitText)
+ {
+ this.noticeRef = noticeRef;
+ this.explicitText = explicitText;
+ }
+
+ public NoticeReference getNoticeRef()
+ {
+ return noticeRef;
+ }
+
+ public DisplayText getExplicitText()
+ {
+ return explicitText;
+ }
+
+ /**
+ * <pre>
+ * SPUserNotice ::= SEQUENCE {
+ * noticeRef NoticeReference OPTIONAL,
+ * explicitText DisplayText OPTIONAL }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (noticeRef != null)
+ {
+ v.add(noticeRef);
+ }
+
+ if (explicitText != null)
+ {
+ v.add(explicitText);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/SPuri.java b/core/src/main/java/org/bouncycastle/asn1/esf/SPuri.java
new file mode 100644
index 00000000..2e2483db
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/SPuri.java
@@ -0,0 +1,45 @@
+package org.bouncycastle.asn1.esf;
+
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DERIA5String;
+
+public class SPuri
+{
+ private DERIA5String uri;
+
+ public static SPuri getInstance(
+ Object obj)
+ {
+ if (obj instanceof SPuri)
+ {
+ return (SPuri) obj;
+ }
+ else if (obj instanceof DERIA5String)
+ {
+ return new SPuri(DERIA5String.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public SPuri(
+ DERIA5String uri)
+ {
+ this.uri = uri;
+ }
+
+ public DERIA5String getUri()
+ {
+ return uri;
+ }
+
+ /**
+ * <pre>
+ * SPuri ::= IA5String
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return uri.toASN1Primitive();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/SigPolicyQualifierInfo.java b/core/src/main/java/org/bouncycastle/asn1/esf/SigPolicyQualifierInfo.java
new file mode 100644
index 00000000..3ce4836a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/SigPolicyQualifierInfo.java
@@ -0,0 +1,75 @@
+package org.bouncycastle.asn1.esf;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class SigPolicyQualifierInfo
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier sigPolicyQualifierId;
+ private ASN1Encodable sigQualifier;
+
+ public SigPolicyQualifierInfo(
+ ASN1ObjectIdentifier sigPolicyQualifierId,
+ ASN1Encodable sigQualifier)
+ {
+ this.sigPolicyQualifierId = sigPolicyQualifierId;
+ this.sigQualifier = sigQualifier;
+ }
+
+ private SigPolicyQualifierInfo(
+ ASN1Sequence seq)
+ {
+ sigPolicyQualifierId = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+ sigQualifier = seq.getObjectAt(1);
+ }
+
+ public static SigPolicyQualifierInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof SigPolicyQualifierInfo)
+ {
+ return (SigPolicyQualifierInfo) obj;
+ }
+ else if (obj != null)
+ {
+ return new SigPolicyQualifierInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public ASN1ObjectIdentifier getSigPolicyQualifierId()
+ {
+ return new ASN1ObjectIdentifier(sigPolicyQualifierId.getId());
+ }
+
+ public ASN1Encodable getSigQualifier()
+ {
+ return sigQualifier;
+ }
+
+ /**
+ * <pre>
+ * SigPolicyQualifierInfo ::= SEQUENCE {
+ * sigPolicyQualifierId SigPolicyQualifierId,
+ * sigQualifier ANY DEFINED BY sigPolicyQualifierId }
+ *
+ * SigPolicyQualifierId ::= OBJECT IDENTIFIER
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(sigPolicyQualifierId);
+ v.add(sigQualifier);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/SigPolicyQualifiers.java b/core/src/main/java/org/bouncycastle/asn1/esf/SigPolicyQualifiers.java
new file mode 100644
index 00000000..453c6d03
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/SigPolicyQualifiers.java
@@ -0,0 +1,77 @@
+package org.bouncycastle.asn1.esf;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class SigPolicyQualifiers
+ extends ASN1Object
+{
+ ASN1Sequence qualifiers;
+
+ public static SigPolicyQualifiers getInstance(
+ Object obj)
+ {
+ if (obj instanceof SigPolicyQualifiers)
+ {
+ return (SigPolicyQualifiers) obj;
+ }
+ else if (obj instanceof ASN1Sequence)
+ {
+ return new SigPolicyQualifiers(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private SigPolicyQualifiers(
+ ASN1Sequence seq)
+ {
+ qualifiers = seq;
+ }
+
+ public SigPolicyQualifiers(
+ SigPolicyQualifierInfo[] qualifierInfos)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ for (int i=0; i < qualifierInfos.length; i++)
+ {
+ v.add(qualifierInfos[i]);
+ }
+ qualifiers = new DERSequence(v);
+ }
+
+ /**
+ * Return the number of qualifier info elements present.
+ *
+ * @return number of elements present.
+ */
+ public int size()
+ {
+ return qualifiers.size();
+ }
+
+ /**
+ * Return the SigPolicyQualifierInfo at index i.
+ *
+ * @param i index of the info of interest
+ * @return the info at index i.
+ */
+ public SigPolicyQualifierInfo getInfoAt(
+ int i)
+ {
+ return SigPolicyQualifierInfo.getInstance(qualifiers.getObjectAt(i));
+ }
+
+ /**
+ * <pre>
+ * SigPolicyQualifiers ::= SEQUENCE SIZE (1..MAX) OF SigPolicyQualifierInfo
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return qualifiers;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/SignaturePolicyId.java b/core/src/main/java/org/bouncycastle/asn1/esf/SignaturePolicyId.java
new file mode 100644
index 00000000..10b88f85
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/SignaturePolicyId.java
@@ -0,0 +1,103 @@
+package org.bouncycastle.asn1.esf;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class SignaturePolicyId
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier sigPolicyId;
+ private OtherHashAlgAndValue sigPolicyHash;
+ private SigPolicyQualifiers sigPolicyQualifiers;
+
+
+ public static SignaturePolicyId getInstance(
+ Object obj)
+ {
+ if (obj instanceof SignaturePolicyId)
+ {
+ return (SignaturePolicyId)obj;
+ }
+ else if (obj != null)
+ {
+ return new SignaturePolicyId(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private SignaturePolicyId(
+ ASN1Sequence seq)
+ {
+ if (seq.size() != 2 && seq.size() != 3)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ sigPolicyId = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+ sigPolicyHash = OtherHashAlgAndValue.getInstance(seq.getObjectAt(1));
+
+ if (seq.size() == 3)
+ {
+ sigPolicyQualifiers = SigPolicyQualifiers.getInstance(seq.getObjectAt(2));
+ }
+ }
+
+ public SignaturePolicyId(
+ ASN1ObjectIdentifier sigPolicyIdentifier,
+ OtherHashAlgAndValue sigPolicyHash)
+ {
+ this(sigPolicyIdentifier, sigPolicyHash, null);
+ }
+
+ public SignaturePolicyId(
+ ASN1ObjectIdentifier sigPolicyId,
+ OtherHashAlgAndValue sigPolicyHash,
+ SigPolicyQualifiers sigPolicyQualifiers)
+ {
+ this.sigPolicyId = sigPolicyId;
+ this.sigPolicyHash = sigPolicyHash;
+ this.sigPolicyQualifiers = sigPolicyQualifiers;
+ }
+
+ public ASN1ObjectIdentifier getSigPolicyId()
+ {
+ return new ASN1ObjectIdentifier(sigPolicyId.getId());
+ }
+
+ public OtherHashAlgAndValue getSigPolicyHash()
+ {
+ return sigPolicyHash;
+ }
+
+ public SigPolicyQualifiers getSigPolicyQualifiers()
+ {
+ return sigPolicyQualifiers;
+ }
+
+ /**
+ * <pre>
+ * SignaturePolicyId ::= SEQUENCE {
+ * sigPolicyId SigPolicyId,
+ * sigPolicyHash SigPolicyHash,
+ * sigPolicyQualifiers SEQUENCE SIZE (1..MAX) OF SigPolicyQualifierInfo OPTIONAL}
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(sigPolicyId);
+ v.add(sigPolicyHash);
+ if (sigPolicyQualifiers != null)
+ {
+ v.add(sigPolicyQualifiers);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/SignaturePolicyIdentifier.java b/core/src/main/java/org/bouncycastle/asn1/esf/SignaturePolicyIdentifier.java
new file mode 100644
index 00000000..acd8ac4b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/SignaturePolicyIdentifier.java
@@ -0,0 +1,76 @@
+package org.bouncycastle.asn1.esf;
+
+import org.bouncycastle.asn1.ASN1Null;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.BERTags;
+import org.bouncycastle.asn1.DERNull;
+
+public class SignaturePolicyIdentifier
+ extends ASN1Object
+{
+ private SignaturePolicyId signaturePolicyId;
+ private boolean isSignaturePolicyImplied;
+
+ public static SignaturePolicyIdentifier getInstance(
+ Object obj)
+ {
+ if (obj instanceof SignaturePolicyIdentifier)
+ {
+ return (SignaturePolicyIdentifier)obj;
+ }
+ else if (obj instanceof ASN1Null || hasEncodedTagValue(obj, BERTags.NULL))
+ {
+ return new SignaturePolicyIdentifier();
+ }
+ else if (obj != null)
+ {
+ return new SignaturePolicyIdentifier(SignaturePolicyId.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public SignaturePolicyIdentifier()
+ {
+ this.isSignaturePolicyImplied = true;
+ }
+
+ public SignaturePolicyIdentifier(
+ SignaturePolicyId signaturePolicyId)
+ {
+ this.signaturePolicyId = signaturePolicyId;
+ this.isSignaturePolicyImplied = false;
+ }
+
+ public SignaturePolicyId getSignaturePolicyId()
+ {
+ return signaturePolicyId;
+ }
+
+ public boolean isSignaturePolicyImplied()
+ {
+ return isSignaturePolicyImplied;
+ }
+
+ /**
+ * <pre>
+ * SignaturePolicyIdentifier ::= CHOICE{
+ * SignaturePolicyId SignaturePolicyId,
+ * SignaturePolicyImplied SignaturePolicyImplied }
+ *
+ * SignaturePolicyImplied ::= NULL
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ if (isSignaturePolicyImplied)
+ {
+ return DERNull.INSTANCE;
+ }
+ else
+ {
+ return signaturePolicyId.toASN1Primitive();
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/SignerAttribute.java b/core/src/main/java/org/bouncycastle/asn1/esf/SignerAttribute.java
new file mode 100644
index 00000000..ecc4db3c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/SignerAttribute.java
@@ -0,0 +1,123 @@
+package org.bouncycastle.asn1.esf;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.Attribute;
+import org.bouncycastle.asn1.x509.AttributeCertificate;
+
+
+public class SignerAttribute
+ extends ASN1Object
+{
+ private Object[] values;
+
+ public static SignerAttribute getInstance(
+ Object o)
+ {
+ if (o instanceof SignerAttribute)
+ {
+ return (SignerAttribute) o;
+ }
+ else if (o != null)
+ {
+ return new SignerAttribute(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ private SignerAttribute(
+ ASN1Sequence seq)
+ {
+ int index = 0;
+ values = new Object[seq.size()];
+
+ for (Enumeration e = seq.getObjects(); e.hasMoreElements();)
+ {
+ ASN1TaggedObject taggedObject = ASN1TaggedObject.getInstance(e.nextElement());
+
+ if (taggedObject.getTagNo() == 0)
+ {
+ ASN1Sequence attrs = ASN1Sequence.getInstance(taggedObject, true);
+ Attribute[] attributes = new Attribute[attrs.size()];
+
+ for (int i = 0; i != attributes.length; i++)
+ {
+ attributes[i] = Attribute.getInstance(attrs.getObjectAt(i));
+ }
+ values[index] = attributes;
+ }
+ else if (taggedObject.getTagNo() == 1)
+ {
+ values[index] = AttributeCertificate.getInstance(ASN1Sequence.getInstance(taggedObject, true));
+ }
+ else
+ {
+ throw new IllegalArgumentException("illegal tag: " + taggedObject.getTagNo());
+ }
+ index++;
+ }
+ }
+
+ public SignerAttribute(
+ Attribute[] claimedAttributes)
+ {
+ this.values = new Object[1];
+ this.values[0] = claimedAttributes;
+ }
+
+ public SignerAttribute(
+ AttributeCertificate certifiedAttributes)
+ {
+ this.values = new Object[1];
+ this.values[0] = certifiedAttributes;
+ }
+
+ /**
+ * Return the sequence of choices - the array elements will either be of
+ * type Attribute[] or AttributeCertificate depending on what tag was used.
+ *
+ * @return array of choices.
+ */
+ public Object[] getValues()
+ {
+ return values;
+ }
+
+ /**
+ *
+ * <pre>
+ * SignerAttribute ::= SEQUENCE OF CHOICE {
+ * claimedAttributes [0] ClaimedAttributes,
+ * certifiedAttributes [1] CertifiedAttributes }
+ *
+ * ClaimedAttributes ::= SEQUENCE OF Attribute
+ * CertifiedAttributes ::= AttributeCertificate -- as defined in RFC 3281: see clause 4.1.
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ for (int i = 0; i != values.length; i++)
+ {
+ if (values[i] instanceof Attribute[])
+ {
+ v.add(new DERTaggedObject(0, new DERSequence((Attribute[])values[i])));
+ }
+ else
+ {
+ v.add(new DERTaggedObject(1, (AttributeCertificate)values[i]));
+ }
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/esf/SignerLocation.java b/core/src/main/java/org/bouncycastle/asn1/esf/SignerLocation.java
new file mode 100644
index 00000000..fcdb3206
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/esf/SignerLocation.java
@@ -0,0 +1,162 @@
+package org.bouncycastle.asn1.esf;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.DERUTF8String;
+import org.bouncycastle.asn1.x500.DirectoryString;
+
+/**
+ * Signer-Location attribute (RFC3126).
+ *
+ * <pre>
+ * SignerLocation ::= SEQUENCE {
+ * countryName [0] DirectoryString OPTIONAL,
+ * localityName [1] DirectoryString OPTIONAL,
+ * postalAddress [2] PostalAddress OPTIONAL }
+ *
+ * PostalAddress ::= SEQUENCE SIZE(1..6) OF DirectoryString
+ * </pre>
+ */
+public class SignerLocation
+ extends ASN1Object
+{
+ private DERUTF8String countryName;
+ private DERUTF8String localityName;
+ private ASN1Sequence postalAddress;
+
+ private SignerLocation(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ DERTaggedObject o = (DERTaggedObject)e.nextElement();
+
+ switch (o.getTagNo())
+ {
+ case 0:
+ DirectoryString countryNameDirectoryString = DirectoryString.getInstance(o, true);
+ this.countryName = new DERUTF8String(countryNameDirectoryString.getString());
+ break;
+ case 1:
+ DirectoryString localityNameDirectoryString = DirectoryString.getInstance(o, true);
+ this.localityName = new DERUTF8String(localityNameDirectoryString.getString());
+ break;
+ case 2:
+ if (o.isExplicit())
+ {
+ this.postalAddress = ASN1Sequence.getInstance(o, true);
+ }
+ else // handle erroneous implicitly tagged sequences
+ {
+ this.postalAddress = ASN1Sequence.getInstance(o, false);
+ }
+ if (postalAddress != null && postalAddress.size() > 6)
+ {
+ throw new IllegalArgumentException("postal address must contain less than 6 strings");
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("illegal tag");
+ }
+ }
+ }
+
+ public SignerLocation(
+ DERUTF8String countryName,
+ DERUTF8String localityName,
+ ASN1Sequence postalAddress)
+ {
+ if (postalAddress != null && postalAddress.size() > 6)
+ {
+ throw new IllegalArgumentException("postal address must contain less than 6 strings");
+ }
+
+ if (countryName != null)
+ {
+ this.countryName = DERUTF8String.getInstance(countryName.toASN1Primitive());
+ }
+
+ if (localityName != null)
+ {
+ this.localityName = DERUTF8String.getInstance(localityName.toASN1Primitive());
+ }
+
+ if (postalAddress != null)
+ {
+ this.postalAddress = ASN1Sequence.getInstance(postalAddress.toASN1Primitive());
+ }
+ }
+
+ public static SignerLocation getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof SignerLocation)
+ {
+ return (SignerLocation)obj;
+ }
+
+ return new SignerLocation(ASN1Sequence.getInstance(obj));
+ }
+
+ public DERUTF8String getCountryName()
+ {
+ return countryName;
+ }
+
+ public DERUTF8String getLocalityName()
+ {
+ return localityName;
+ }
+
+ public ASN1Sequence getPostalAddress()
+ {
+ return postalAddress;
+ }
+
+ /**
+ * <pre>
+ * SignerLocation ::= SEQUENCE {
+ * countryName [0] DirectoryString OPTIONAL,
+ * localityName [1] DirectoryString OPTIONAL,
+ * postalAddress [2] PostalAddress OPTIONAL }
+ *
+ * PostalAddress ::= SEQUENCE SIZE(1..6) OF DirectoryString
+ *
+ * DirectoryString ::= CHOICE {
+ * teletexString TeletexString (SIZE (1..MAX)),
+ * printableString PrintableString (SIZE (1..MAX)),
+ * universalString UniversalString (SIZE (1..MAX)),
+ * utf8String UTF8String (SIZE (1.. MAX)),
+ * bmpString BMPString (SIZE (1..MAX)) }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (countryName != null)
+ {
+ v.add(new DERTaggedObject(true, 0, countryName));
+ }
+
+ if (localityName != null)
+ {
+ v.add(new DERTaggedObject(true, 1, localityName));
+ }
+
+ if (postalAddress != null)
+ {
+ v.add(new DERTaggedObject(true, 2, postalAddress));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ess/ContentHints.java b/core/src/main/java/org/bouncycastle/asn1/ess/ContentHints.java
new file mode 100644
index 00000000..93d9d0c8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ess/ContentHints.java
@@ -0,0 +1,114 @@
+package org.bouncycastle.asn1.ess;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERUTF8String;
+
+public class ContentHints
+ extends ASN1Object
+{
+ private DERUTF8String contentDescription;
+ private ASN1ObjectIdentifier contentType;
+
+ public static ContentHints getInstance(Object o)
+ {
+ if (o instanceof ContentHints)
+ {
+ return (ContentHints)o;
+ }
+ else if (o != null)
+ {
+ return new ContentHints(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ /**
+ * constructor
+ */
+ private ContentHints(ASN1Sequence seq)
+ {
+ ASN1Encodable field = seq.getObjectAt(0);
+ if (field.toASN1Primitive() instanceof DERUTF8String)
+ {
+ contentDescription = DERUTF8String.getInstance(field);
+ contentType = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(1));
+ }
+ else
+ {
+ contentType = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+ }
+ }
+
+ /**
+ * @deprecated use ASN1ObjectIdentifier
+ */
+ public ContentHints(
+ DERObjectIdentifier contentType)
+ {
+ this(new ASN1ObjectIdentifier(contentType.getId()));
+ }
+
+ /**
+ * @deprecated use ASN1ObjectIdentifier
+ */
+ public ContentHints(
+ DERObjectIdentifier contentType,
+ DERUTF8String contentDescription)
+ {
+ this(new ASN1ObjectIdentifier(contentType.getId()), contentDescription);
+ }
+
+ public ContentHints(
+ ASN1ObjectIdentifier contentType)
+ {
+ this.contentType = contentType;
+ this.contentDescription = null;
+ }
+
+ public ContentHints(
+ ASN1ObjectIdentifier contentType,
+ DERUTF8String contentDescription)
+ {
+ this.contentType = contentType;
+ this.contentDescription = contentDescription;
+ }
+
+ public ASN1ObjectIdentifier getContentType()
+ {
+ return contentType;
+ }
+
+ public DERUTF8String getContentDescription()
+ {
+ return contentDescription;
+ }
+
+ /**
+ * <pre>
+ * ContentHints ::= SEQUENCE {
+ * contentDescription UTF8String (SIZE (1..MAX)) OPTIONAL,
+ * contentType ContentType }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (contentDescription != null)
+ {
+ v.add(contentDescription);
+ }
+
+ v.add(contentType);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ess/ContentIdentifier.java b/core/src/main/java/org/bouncycastle/asn1/ess/ContentIdentifier.java
new file mode 100644
index 00000000..37064c4e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ess/ContentIdentifier.java
@@ -0,0 +1,63 @@
+package org.bouncycastle.asn1.ess;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DEROctetString;
+
+public class ContentIdentifier
+ extends ASN1Object
+{
+ ASN1OctetString value;
+
+ public static ContentIdentifier getInstance(Object o)
+ {
+ if (o instanceof ContentIdentifier)
+ {
+ return (ContentIdentifier) o;
+ }
+ else if (o != null)
+ {
+ return new ContentIdentifier(ASN1OctetString.getInstance(o));
+ }
+
+ return null;
+ }
+
+ /**
+ * Create from OCTET STRING whose octets represent the identifier.
+ */
+ private ContentIdentifier(
+ ASN1OctetString value)
+ {
+ this.value = value;
+ }
+
+ /**
+ * Create from byte array representing the identifier.
+ */
+ public ContentIdentifier(
+ byte[] value)
+ {
+ this(new DEROctetString(value));
+ }
+
+ public ASN1OctetString getValue()
+ {
+ return value;
+ }
+
+ /**
+ * The definition of ContentIdentifier is
+ * <pre>
+ * ContentIdentifier ::= OCTET STRING
+ * </pre>
+ * id-aa-contentIdentifier OBJECT IDENTIFIER ::= { iso(1)
+ * member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
+ * smime(16) id-aa(2) 7 }
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return value;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ess/ESSCertID.java b/core/src/main/java/org/bouncycastle/asn1/ess/ESSCertID.java
new file mode 100644
index 00000000..a6cc3156
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ess/ESSCertID.java
@@ -0,0 +1,95 @@
+package org.bouncycastle.asn1.ess;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.IssuerSerial;
+
+public class ESSCertID
+ extends ASN1Object
+{
+ private ASN1OctetString certHash;
+
+ private IssuerSerial issuerSerial;
+
+ public static ESSCertID getInstance(Object o)
+ {
+ if (o instanceof ESSCertID)
+ {
+ return (ESSCertID)o;
+ }
+ else if (o != null)
+ {
+ return new ESSCertID(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ /**
+ * constructor
+ */
+ private ESSCertID(ASN1Sequence seq)
+ {
+ if (seq.size() < 1 || seq.size() > 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ certHash = ASN1OctetString.getInstance(seq.getObjectAt(0));
+
+ if (seq.size() > 1)
+ {
+ issuerSerial = IssuerSerial.getInstance(seq.getObjectAt(1));
+ }
+ }
+
+ public ESSCertID(
+ byte[] hash)
+ {
+ certHash = new DEROctetString(hash);
+ }
+
+ public ESSCertID(
+ byte[] hash,
+ IssuerSerial issuerSerial)
+ {
+ this.certHash = new DEROctetString(hash);
+ this.issuerSerial = issuerSerial;
+ }
+
+ public byte[] getCertHash()
+ {
+ return certHash.getOctets();
+ }
+
+ public IssuerSerial getIssuerSerial()
+ {
+ return issuerSerial;
+ }
+
+ /**
+ * <pre>
+ * ESSCertID ::= SEQUENCE {
+ * certHash Hash,
+ * issuerSerial IssuerSerial OPTIONAL }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(certHash);
+
+ if (issuerSerial != null)
+ {
+ v.add(issuerSerial);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ess/ESSCertIDv2.java b/core/src/main/java/org/bouncycastle/asn1/ess/ESSCertIDv2.java
new file mode 100644
index 00000000..b511f2c2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ess/ESSCertIDv2.java
@@ -0,0 +1,155 @@
+package org.bouncycastle.asn1.ess;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.IssuerSerial;
+
+public class ESSCertIDv2
+ extends ASN1Object
+{
+ private AlgorithmIdentifier hashAlgorithm;
+ private byte[] certHash;
+ private IssuerSerial issuerSerial;
+ private static final AlgorithmIdentifier DEFAULT_ALG_ID = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256);
+
+ public static ESSCertIDv2 getInstance(
+ Object o)
+ {
+ if (o instanceof ESSCertIDv2)
+ {
+ return (ESSCertIDv2) o;
+ }
+ else if (o != null)
+ {
+ return new ESSCertIDv2(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ private ESSCertIDv2(
+ ASN1Sequence seq)
+ {
+ if (seq.size() > 3)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ int count = 0;
+
+ if (seq.getObjectAt(0) instanceof ASN1OctetString)
+ {
+ // Default value
+ this.hashAlgorithm = DEFAULT_ALG_ID;
+ }
+ else
+ {
+ this.hashAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(count++).toASN1Primitive());
+ }
+
+ this.certHash = ASN1OctetString.getInstance(seq.getObjectAt(count++).toASN1Primitive()).getOctets();
+
+ if (seq.size() > count)
+ {
+ this.issuerSerial = IssuerSerial.getInstance(seq.getObjectAt(count));
+ }
+ }
+
+ public ESSCertIDv2(
+ byte[] certHash)
+ {
+ this(null, certHash, null);
+ }
+
+ public ESSCertIDv2(
+ AlgorithmIdentifier algId,
+ byte[] certHash)
+ {
+ this(algId, certHash, null);
+ }
+
+ public ESSCertIDv2(
+ byte[] certHash,
+ IssuerSerial issuerSerial)
+ {
+ this(null, certHash, issuerSerial);
+ }
+
+ public ESSCertIDv2(
+ AlgorithmIdentifier algId,
+ byte[] certHash,
+ IssuerSerial issuerSerial)
+ {
+ if (algId == null)
+ {
+ // Default value
+ this.hashAlgorithm = DEFAULT_ALG_ID;
+ }
+ else
+ {
+ this.hashAlgorithm = algId;
+ }
+
+ this.certHash = certHash;
+ this.issuerSerial = issuerSerial;
+ }
+
+ public AlgorithmIdentifier getHashAlgorithm()
+ {
+ return this.hashAlgorithm;
+ }
+
+ public byte[] getCertHash()
+ {
+ return certHash;
+ }
+
+ public IssuerSerial getIssuerSerial()
+ {
+ return issuerSerial;
+ }
+
+ /**
+ * <pre>
+ * ESSCertIDv2 ::= SEQUENCE {
+ * hashAlgorithm AlgorithmIdentifier
+ * DEFAULT {algorithm id-sha256},
+ * certHash Hash,
+ * issuerSerial IssuerSerial OPTIONAL
+ * }
+ *
+ * Hash ::= OCTET STRING
+ *
+ * IssuerSerial ::= SEQUENCE {
+ * issuer GeneralNames,
+ * serialNumber CertificateSerialNumber
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (!hashAlgorithm.equals(DEFAULT_ALG_ID))
+ {
+ v.add(hashAlgorithm);
+ }
+
+ v.add(new DEROctetString(certHash).toASN1Primitive());
+
+ if (issuerSerial != null)
+ {
+ v.add(issuerSerial);
+ }
+
+ return new DERSequence(v);
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ess/OtherCertID.java b/core/src/main/java/org/bouncycastle/asn1/ess/OtherCertID.java
new file mode 100644
index 00000000..2cc88ecc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ess/OtherCertID.java
@@ -0,0 +1,137 @@
+package org.bouncycastle.asn1.ess;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.DigestInfo;
+import org.bouncycastle.asn1.x509.IssuerSerial;
+
+public class OtherCertID
+ extends ASN1Object
+{
+ private ASN1Encodable otherCertHash;
+ private IssuerSerial issuerSerial;
+
+ public static OtherCertID getInstance(Object o)
+ {
+ if (o instanceof OtherCertID)
+ {
+ return (OtherCertID) o;
+ }
+ else if (o != null)
+ {
+ return new OtherCertID(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ /**
+ * constructor
+ */
+ private OtherCertID(ASN1Sequence seq)
+ {
+ if (seq.size() < 1 || seq.size() > 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ if (seq.getObjectAt(0).toASN1Primitive() instanceof ASN1OctetString)
+ {
+ otherCertHash = ASN1OctetString.getInstance(seq.getObjectAt(0));
+ }
+ else
+ {
+ otherCertHash = DigestInfo.getInstance(seq.getObjectAt(0));
+
+ }
+
+ if (seq.size() > 1)
+ {
+ issuerSerial = IssuerSerial.getInstance(seq.getObjectAt(1));
+ }
+ }
+
+ public OtherCertID(
+ AlgorithmIdentifier algId,
+ byte[] digest)
+ {
+ this.otherCertHash = new DigestInfo(algId, digest);
+ }
+
+ public OtherCertID(
+ AlgorithmIdentifier algId,
+ byte[] digest,
+ IssuerSerial issuerSerial)
+ {
+ this.otherCertHash = new DigestInfo(algId, digest);
+ this.issuerSerial = issuerSerial;
+ }
+
+ public AlgorithmIdentifier getAlgorithmHash()
+ {
+ if (otherCertHash.toASN1Primitive() instanceof ASN1OctetString)
+ {
+ // SHA-1
+ return new AlgorithmIdentifier("1.3.14.3.2.26");
+ }
+ else
+ {
+ return DigestInfo.getInstance(otherCertHash).getAlgorithmId();
+ }
+ }
+
+ public byte[] getCertHash()
+ {
+ if (otherCertHash.toASN1Primitive() instanceof ASN1OctetString)
+ {
+ // SHA-1
+ return ((ASN1OctetString)otherCertHash.toASN1Primitive()).getOctets();
+ }
+ else
+ {
+ return DigestInfo.getInstance(otherCertHash).getDigest();
+ }
+ }
+
+ public IssuerSerial getIssuerSerial()
+ {
+ return issuerSerial;
+ }
+
+ /**
+ * <pre>
+ * OtherCertID ::= SEQUENCE {
+ * otherCertHash OtherHash,
+ * issuerSerial IssuerSerial OPTIONAL }
+ *
+ * OtherHash ::= CHOICE {
+ * sha1Hash OCTET STRING,
+ * otherHash OtherHashAlgAndValue }
+ *
+ * OtherHashAlgAndValue ::= SEQUENCE {
+ * hashAlgorithm AlgorithmIdentifier,
+ * hashValue OCTET STRING }
+ *
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(otherCertHash);
+
+ if (issuerSerial != null)
+ {
+ v.add(issuerSerial);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ess/OtherSigningCertificate.java b/core/src/main/java/org/bouncycastle/asn1/ess/OtherSigningCertificate.java
new file mode 100644
index 00000000..41f9e930
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ess/OtherSigningCertificate.java
@@ -0,0 +1,109 @@
+package org.bouncycastle.asn1.ess;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.PolicyInformation;
+
+public class OtherSigningCertificate
+ extends ASN1Object
+{
+ ASN1Sequence certs;
+ ASN1Sequence policies;
+
+ public static OtherSigningCertificate getInstance(Object o)
+ {
+ if (o instanceof OtherSigningCertificate)
+ {
+ return (OtherSigningCertificate) o;
+ }
+ else if (o != null)
+ {
+ return new OtherSigningCertificate(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ /**
+ * constructeurs
+ */
+ private OtherSigningCertificate(ASN1Sequence seq)
+ {
+ if (seq.size() < 1 || seq.size() > 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ this.certs = ASN1Sequence.getInstance(seq.getObjectAt(0));
+
+ if (seq.size() > 1)
+ {
+ this.policies = ASN1Sequence.getInstance(seq.getObjectAt(1));
+ }
+ }
+
+ public OtherSigningCertificate(
+ OtherCertID otherCertID)
+ {
+ certs = new DERSequence(otherCertID);
+ }
+
+ public OtherCertID[] getCerts()
+ {
+ OtherCertID[] cs = new OtherCertID[certs.size()];
+
+ for (int i = 0; i != certs.size(); i++)
+ {
+ cs[i] = OtherCertID.getInstance(certs.getObjectAt(i));
+ }
+
+ return cs;
+ }
+
+ public PolicyInformation[] getPolicies()
+ {
+ if (policies == null)
+ {
+ return null;
+ }
+
+ PolicyInformation[] ps = new PolicyInformation[policies.size()];
+
+ for (int i = 0; i != policies.size(); i++)
+ {
+ ps[i] = PolicyInformation.getInstance(policies.getObjectAt(i));
+ }
+
+ return ps;
+ }
+
+ /**
+ * The definition of OtherSigningCertificate is
+ * <pre>
+ * OtherSigningCertificate ::= SEQUENCE {
+ * certs SEQUENCE OF OtherCertID,
+ * policies SEQUENCE OF PolicyInformation OPTIONAL
+ * }
+ * </pre>
+ * id-aa-ets-otherSigCert OBJECT IDENTIFIER ::= { iso(1)
+ * member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
+ * smime(16) id-aa(2) 19 }
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(certs);
+
+ if (policies != null)
+ {
+ v.add(policies);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ess/SigningCertificate.java b/core/src/main/java/org/bouncycastle/asn1/ess/SigningCertificate.java
new file mode 100644
index 00000000..eaf22e9f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ess/SigningCertificate.java
@@ -0,0 +1,109 @@
+package org.bouncycastle.asn1.ess;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.PolicyInformation;
+
+
+public class SigningCertificate
+ extends ASN1Object
+{
+ ASN1Sequence certs;
+ ASN1Sequence policies;
+
+ public static SigningCertificate getInstance(Object o)
+ {
+ if (o instanceof SigningCertificate)
+ {
+ return (SigningCertificate) o;
+ }
+ else if (o != null)
+ {
+ return new SigningCertificate(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ /**
+ * constructeurs
+ */
+ private SigningCertificate(ASN1Sequence seq)
+ {
+ if (seq.size() < 1 || seq.size() > 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+ this.certs = ASN1Sequence.getInstance(seq.getObjectAt(0));
+
+ if (seq.size() > 1)
+ {
+ this.policies = ASN1Sequence.getInstance(seq.getObjectAt(1));
+ }
+ }
+
+ public SigningCertificate(
+ ESSCertID essCertID)
+ {
+ certs = new DERSequence(essCertID);
+ }
+
+ public ESSCertID[] getCerts()
+ {
+ ESSCertID[] cs = new ESSCertID[certs.size()];
+
+ for (int i = 0; i != certs.size(); i++)
+ {
+ cs[i] = ESSCertID.getInstance(certs.getObjectAt(i));
+ }
+
+ return cs;
+ }
+
+ public PolicyInformation[] getPolicies()
+ {
+ if (policies == null)
+ {
+ return null;
+ }
+
+ PolicyInformation[] ps = new PolicyInformation[policies.size()];
+
+ for (int i = 0; i != policies.size(); i++)
+ {
+ ps[i] = PolicyInformation.getInstance(policies.getObjectAt(i));
+ }
+
+ return ps;
+ }
+
+ /**
+ * The definition of SigningCertificate is
+ * <pre>
+ * SigningCertificate ::= SEQUENCE {
+ * certs SEQUENCE OF ESSCertID,
+ * policies SEQUENCE OF PolicyInformation OPTIONAL
+ * }
+ * </pre>
+ * id-aa-signingCertificate OBJECT IDENTIFIER ::= { iso(1)
+ * member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
+ * smime(16) id-aa(2) 12 }
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(certs);
+
+ if (policies != null)
+ {
+ v.add(policies);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ess/SigningCertificateV2.java b/core/src/main/java/org/bouncycastle/asn1/ess/SigningCertificateV2.java
new file mode 100644
index 00000000..07219cdd
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ess/SigningCertificateV2.java
@@ -0,0 +1,136 @@
+package org.bouncycastle.asn1.ess;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.PolicyInformation;
+
+public class SigningCertificateV2
+ extends ASN1Object
+{
+ ASN1Sequence certs;
+ ASN1Sequence policies;
+
+ public static SigningCertificateV2 getInstance(
+ Object o)
+ {
+ if (o == null || o instanceof SigningCertificateV2)
+ {
+ return (SigningCertificateV2) o;
+ }
+ else if (o instanceof ASN1Sequence)
+ {
+ return new SigningCertificateV2((ASN1Sequence) o);
+ }
+
+ return null;
+ }
+
+ private SigningCertificateV2(
+ ASN1Sequence seq)
+ {
+ if (seq.size() < 1 || seq.size() > 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ this.certs = ASN1Sequence.getInstance(seq.getObjectAt(0));
+
+ if (seq.size() > 1)
+ {
+ this.policies = ASN1Sequence.getInstance(seq.getObjectAt(1));
+ }
+ }
+
+ public SigningCertificateV2(
+ ESSCertIDv2 cert)
+ {
+ this.certs = new DERSequence(cert);
+ }
+
+ public SigningCertificateV2(
+ ESSCertIDv2[] certs)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ for (int i=0; i < certs.length; i++)
+ {
+ v.add(certs[i]);
+ }
+ this.certs = new DERSequence(v);
+ }
+
+ public SigningCertificateV2(
+ ESSCertIDv2[] certs,
+ PolicyInformation[] policies)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ for (int i=0; i < certs.length; i++)
+ {
+ v.add(certs[i]);
+ }
+ this.certs = new DERSequence(v);
+
+ if (policies != null)
+ {
+ v = new ASN1EncodableVector();
+ for (int i=0; i < policies.length; i++)
+ {
+ v.add(policies[i]);
+ }
+ this.policies = new DERSequence(v);
+ }
+ }
+
+ public ESSCertIDv2[] getCerts()
+ {
+ ESSCertIDv2[] certIds = new ESSCertIDv2[certs.size()];
+ for (int i = 0; i != certs.size(); i++)
+ {
+ certIds[i] = ESSCertIDv2.getInstance(certs.getObjectAt(i));
+ }
+ return certIds;
+ }
+
+ public PolicyInformation[] getPolicies()
+ {
+ if (policies == null)
+ {
+ return null;
+ }
+
+ PolicyInformation[] policyInformations = new PolicyInformation[policies.size()];
+ for (int i = 0; i != policies.size(); i++)
+ {
+ policyInformations[i] = PolicyInformation.getInstance(policies.getObjectAt(i));
+ }
+ return policyInformations;
+ }
+
+ /**
+ * The definition of SigningCertificateV2 is
+ * <pre>
+ * SigningCertificateV2 ::= SEQUENCE {
+ * certs SEQUENCE OF ESSCertIDv2,
+ * policies SEQUENCE OF PolicyInformation OPTIONAL
+ * }
+ * </pre>
+ * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::= { iso(1)
+ * member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
+ * smime(16) id-aa(2) 47 }
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(certs);
+
+ if (policies != null)
+ {
+ v.add(policies);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/gnu/GNUObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/gnu/GNUObjectIdentifiers.java
new file mode 100644
index 00000000..084a020a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/gnu/GNUObjectIdentifiers.java
@@ -0,0 +1,30 @@
+package org.bouncycastle.asn1.gnu;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface GNUObjectIdentifiers
+{
+ public static final ASN1ObjectIdentifier GNU = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.1"); // GNU Radius
+ public static final ASN1ObjectIdentifier GnuPG = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.2"); // GnuPG (Ägypten)
+ public static final ASN1ObjectIdentifier notation = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.2.1"); // notation
+ public static final ASN1ObjectIdentifier pkaAddress = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.2.1.1"); // pkaAddress
+ public static final ASN1ObjectIdentifier GnuRadar = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.3"); // GNU Radar
+ public static final ASN1ObjectIdentifier digestAlgorithm = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.12"); // digestAlgorithm
+ public static final ASN1ObjectIdentifier Tiger_192 = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.12.2"); // TIGER/192
+ public static final ASN1ObjectIdentifier encryptionAlgorithm = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13"); // encryptionAlgorithm
+ public static final ASN1ObjectIdentifier Serpent = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2"); // Serpent
+ public static final ASN1ObjectIdentifier Serpent_128_ECB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.1"); // Serpent-128-ECB
+ public static final ASN1ObjectIdentifier Serpent_128_CBC = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.2"); // Serpent-128-CBC
+ public static final ASN1ObjectIdentifier Serpent_128_OFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.3"); // Serpent-128-OFB
+ public static final ASN1ObjectIdentifier Serpent_128_CFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.4"); // Serpent-128-CFB
+ public static final ASN1ObjectIdentifier Serpent_192_ECB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.21"); // Serpent-192-ECB
+ public static final ASN1ObjectIdentifier Serpent_192_CBC = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.22"); // Serpent-192-CBC
+ public static final ASN1ObjectIdentifier Serpent_192_OFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.23"); // Serpent-192-OFB
+ public static final ASN1ObjectIdentifier Serpent_192_CFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.24"); // Serpent-192-CFB
+ public static final ASN1ObjectIdentifier Serpent_256_ECB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.41"); // Serpent-256-ECB
+ public static final ASN1ObjectIdentifier Serpent_256_CBC = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.42"); // Serpent-256-CBC
+ public static final ASN1ObjectIdentifier Serpent_256_OFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.43"); // Serpent-256-OFB
+ public static final ASN1ObjectIdentifier Serpent_256_CFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.44"); // Serpent-256-CFB
+ public static final ASN1ObjectIdentifier CRC = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.14"); // CRC algorithms
+ public static final ASN1ObjectIdentifier CRC32 = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.14.1"); // CRC 32
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/iana/IANAObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/iana/IANAObjectIdentifiers.java
new file mode 100644
index 00000000..e9ab8d60
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/iana/IANAObjectIdentifiers.java
@@ -0,0 +1,20 @@
+package org.bouncycastle.asn1.iana;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface IANAObjectIdentifiers
+{
+ // id-SHA1 OBJECT IDENTIFIER ::=
+ // {iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanisms(5) ipsec(8) isakmpOakley(1)}
+ //
+
+ static final ASN1ObjectIdentifier isakmpOakley = new ASN1ObjectIdentifier("1.3.6.1.5.5.8.1");
+
+ static final ASN1ObjectIdentifier hmacMD5 = new ASN1ObjectIdentifier(isakmpOakley + ".1");
+ static final ASN1ObjectIdentifier hmacSHA1 = new ASN1ObjectIdentifier(isakmpOakley + ".2");
+
+ static final ASN1ObjectIdentifier hmacTIGER = new ASN1ObjectIdentifier(isakmpOakley + ".3");
+
+ static final ASN1ObjectIdentifier hmacRIPEMD160 = new ASN1ObjectIdentifier(isakmpOakley + ".4");
+
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/icao/CscaMasterList.java b/core/src/main/java/org/bouncycastle/asn1/icao/CscaMasterList.java
new file mode 100644
index 00000000..2cae261f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/icao/CscaMasterList.java
@@ -0,0 +1,114 @@
+package org.bouncycastle.asn1.icao;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.x509.Certificate;
+
+/**
+ * The CscaMasterList object. This object can be wrapped in a
+ * CMSSignedData to be published in LDAP.
+ * <p/>
+ * <pre>
+ * CscaMasterList ::= SEQUENCE {
+ * version CscaMasterListVersion,
+ * certList SET OF Certificate }
+ *
+ * CscaMasterListVersion :: INTEGER {v0(0)}
+ * </pre>
+ */
+
+public class CscaMasterList
+ extends ASN1Object
+{
+ private ASN1Integer version = new ASN1Integer(0);
+ private Certificate[] certList;
+
+ public static CscaMasterList getInstance(
+ Object obj)
+ {
+ if (obj instanceof CscaMasterList)
+ {
+ return (CscaMasterList)obj;
+ }
+ else if (obj != null)
+ {
+ return new CscaMasterList(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private CscaMasterList(
+ ASN1Sequence seq)
+ {
+ if (seq == null || seq.size() == 0)
+ {
+ throw new IllegalArgumentException(
+ "null or empty sequence passed.");
+ }
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException(
+ "Incorrect sequence size: " + seq.size());
+ }
+
+ version = ASN1Integer.getInstance(seq.getObjectAt(0));
+ ASN1Set certSet = ASN1Set.getInstance(seq.getObjectAt(1));
+ certList = new Certificate[certSet.size()];
+ for (int i = 0; i < certList.length; i++)
+ {
+ certList[i]
+ = Certificate.getInstance(certSet.getObjectAt(i));
+ }
+ }
+
+ public CscaMasterList(
+ Certificate[] certStructs)
+ {
+ certList = copyCertList(certStructs);
+ }
+
+ public int getVersion()
+ {
+ return version.getValue().intValue();
+ }
+
+ public Certificate[] getCertStructs()
+ {
+ return copyCertList(certList);
+ }
+
+ private Certificate[] copyCertList(Certificate[] orig)
+ {
+ Certificate[] certs = new Certificate[orig.length];
+
+ for (int i = 0; i != certs.length; i++)
+ {
+ certs[i] = orig[i];
+ }
+
+ return certs;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector seq = new ASN1EncodableVector();
+
+ seq.add(version);
+
+ ASN1EncodableVector certSet = new ASN1EncodableVector();
+ for (int i = 0; i < certList.length; i++)
+ {
+ certSet.add(certList[i]);
+ }
+ seq.add(new DERSet(certSet));
+
+ return new DERSequence(seq);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/icao/DataGroupHash.java b/core/src/main/java/org/bouncycastle/asn1/icao/DataGroupHash.java
new file mode 100644
index 00000000..b4c4c5c8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/icao/DataGroupHash.java
@@ -0,0 +1,97 @@
+package org.bouncycastle.asn1.icao;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * The DataGroupHash object.
+ * <pre>
+ * DataGroupHash ::= SEQUENCE {
+ * dataGroupNumber DataGroupNumber,
+ * dataGroupHashValue OCTET STRING }
+ *
+ * DataGroupNumber ::= INTEGER {
+ * dataGroup1 (1),
+ * dataGroup1 (2),
+ * dataGroup1 (3),
+ * dataGroup1 (4),
+ * dataGroup1 (5),
+ * dataGroup1 (6),
+ * dataGroup1 (7),
+ * dataGroup1 (8),
+ * dataGroup1 (9),
+ * dataGroup1 (10),
+ * dataGroup1 (11),
+ * dataGroup1 (12),
+ * dataGroup1 (13),
+ * dataGroup1 (14),
+ * dataGroup1 (15),
+ * dataGroup1 (16) }
+ *
+ * </pre>
+ */
+public class DataGroupHash
+ extends ASN1Object
+{
+ ASN1Integer dataGroupNumber;
+ ASN1OctetString dataGroupHashValue;
+
+ public static DataGroupHash getInstance(
+ Object obj)
+ {
+ if (obj instanceof DataGroupHash)
+ {
+ return (DataGroupHash)obj;
+ }
+ else if (obj != null)
+ {
+ return new DataGroupHash(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private DataGroupHash(ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ // dataGroupNumber
+ dataGroupNumber = ASN1Integer.getInstance(e.nextElement());
+ // dataGroupHashValue
+ dataGroupHashValue = ASN1OctetString.getInstance(e.nextElement());
+ }
+
+ public DataGroupHash(
+ int dataGroupNumber,
+ ASN1OctetString dataGroupHashValue)
+ {
+ this.dataGroupNumber = new ASN1Integer(dataGroupNumber);
+ this.dataGroupHashValue = dataGroupHashValue;
+ }
+
+ public int getDataGroupNumber()
+ {
+ return dataGroupNumber.getValue().intValue();
+ }
+
+ public ASN1OctetString getDataGroupHashValue()
+ {
+ return dataGroupHashValue;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector seq = new ASN1EncodableVector();
+ seq.add(dataGroupNumber);
+ seq.add(dataGroupHashValue);
+
+ return new DERSequence(seq);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/icao/ICAOObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/icao/ICAOObjectIdentifiers.java
new file mode 100644
index 00000000..0b5da2be
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/icao/ICAOObjectIdentifiers.java
@@ -0,0 +1,33 @@
+package org.bouncycastle.asn1.icao;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface ICAOObjectIdentifiers
+{
+ //
+ // base id
+ //
+ static final ASN1ObjectIdentifier id_icao = new ASN1ObjectIdentifier("2.23.136");
+
+ static final ASN1ObjectIdentifier id_icao_mrtd = id_icao.branch("1");
+ static final ASN1ObjectIdentifier id_icao_mrtd_security = id_icao_mrtd.branch("1");
+
+ // LDS security object, see ICAO Doc 9303-Volume 2-Section IV-A3.2
+ static final ASN1ObjectIdentifier id_icao_ldsSecurityObject = id_icao_mrtd_security.branch("1");
+
+ // CSCA master list, see TR CSCA Countersigning and Master List issuance
+ static final ASN1ObjectIdentifier id_icao_cscaMasterList = id_icao_mrtd_security.branch("2");
+ static final ASN1ObjectIdentifier id_icao_cscaMasterListSigningKey = id_icao_mrtd_security.branch("3");
+
+ // document type list, see draft TR LDS and PKI Maintenance, par. 3.2.1
+ static final ASN1ObjectIdentifier id_icao_documentTypeList = id_icao_mrtd_security.branch("4");
+
+ // Active Authentication protocol, see draft TR LDS and PKI Maintenance,
+ // par. 5.2.2
+ static final ASN1ObjectIdentifier id_icao_aaProtocolObject = id_icao_mrtd_security.branch("5");
+
+ // CSCA name change and key reoll-over, see draft TR LDS and PKI
+ // Maintenance, par. 3.2.1
+ static final ASN1ObjectIdentifier id_icao_extensions = id_icao_mrtd_security.branch("6");
+ static final ASN1ObjectIdentifier id_icao_extensions_namechangekeyrollover = id_icao_extensions.branch("1");
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/icao/LDSSecurityObject.java b/core/src/main/java/org/bouncycastle/asn1/icao/LDSSecurityObject.java
new file mode 100644
index 00000000..fae8762b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/icao/LDSSecurityObject.java
@@ -0,0 +1,159 @@
+package org.bouncycastle.asn1.icao;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * The LDSSecurityObject object (V1.8).
+ * <pre>
+ * LDSSecurityObject ::= SEQUENCE {
+ * version LDSSecurityObjectVersion,
+ * hashAlgorithm DigestAlgorithmIdentifier,
+ * dataGroupHashValues SEQUENCE SIZE (2..ub-DataGroups) OF DataHashGroup,
+ * ldsVersionInfo LDSVersionInfo OPTIONAL
+ * -- if present, version MUST be v1 }
+ *
+ * DigestAlgorithmIdentifier ::= AlgorithmIdentifier,
+ *
+ * LDSSecurityObjectVersion :: INTEGER {V0(0)}
+ * </pre>
+ */
+
+public class LDSSecurityObject
+ extends ASN1Object
+ implements ICAOObjectIdentifiers
+{
+ public static final int ub_DataGroups = 16;
+
+ private ASN1Integer version = new ASN1Integer(0);
+ private AlgorithmIdentifier digestAlgorithmIdentifier;
+ private DataGroupHash[] datagroupHash;
+ private LDSVersionInfo versionInfo;
+
+ public static LDSSecurityObject getInstance(
+ Object obj)
+ {
+ if (obj instanceof LDSSecurityObject)
+ {
+ return (LDSSecurityObject)obj;
+ }
+ else if (obj != null)
+ {
+ return new LDSSecurityObject(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private LDSSecurityObject(
+ ASN1Sequence seq)
+ {
+ if (seq == null || seq.size() == 0)
+ {
+ throw new IllegalArgumentException("null or empty sequence passed.");
+ }
+
+ Enumeration e = seq.getObjects();
+
+ // version
+ version = ASN1Integer.getInstance(e.nextElement());
+ // digestAlgorithmIdentifier
+ digestAlgorithmIdentifier = AlgorithmIdentifier.getInstance(e.nextElement());
+
+ ASN1Sequence datagroupHashSeq = ASN1Sequence.getInstance(e.nextElement());
+
+ if (version.getValue().intValue() == 1)
+ {
+ versionInfo = LDSVersionInfo.getInstance(e.nextElement());
+ }
+
+ checkDatagroupHashSeqSize(datagroupHashSeq.size());
+
+ datagroupHash = new DataGroupHash[datagroupHashSeq.size()];
+ for (int i= 0; i< datagroupHashSeq.size();i++)
+ {
+ datagroupHash[i] = DataGroupHash.getInstance(datagroupHashSeq.getObjectAt(i));
+ }
+ }
+
+ public LDSSecurityObject(
+ AlgorithmIdentifier digestAlgorithmIdentifier,
+ DataGroupHash[] datagroupHash)
+ {
+ this.version = new ASN1Integer(0);
+ this.digestAlgorithmIdentifier = digestAlgorithmIdentifier;
+ this.datagroupHash = datagroupHash;
+
+ checkDatagroupHashSeqSize(datagroupHash.length);
+ }
+
+ public LDSSecurityObject(
+ AlgorithmIdentifier digestAlgorithmIdentifier,
+ DataGroupHash[] datagroupHash,
+ LDSVersionInfo versionInfo)
+ {
+ this.version = new ASN1Integer(1);
+ this.digestAlgorithmIdentifier = digestAlgorithmIdentifier;
+ this.datagroupHash = datagroupHash;
+ this.versionInfo = versionInfo;
+
+ checkDatagroupHashSeqSize(datagroupHash.length);
+ }
+
+ private void checkDatagroupHashSeqSize(int size)
+ {
+ if ((size < 2) || (size > ub_DataGroups))
+ {
+ throw new IllegalArgumentException("wrong size in DataGroupHashValues : not in (2.."+ ub_DataGroups +")");
+ }
+ }
+
+ public int getVersion()
+ {
+ return version.getValue().intValue();
+ }
+
+ public AlgorithmIdentifier getDigestAlgorithmIdentifier()
+ {
+ return digestAlgorithmIdentifier;
+ }
+
+ public DataGroupHash[] getDatagroupHash()
+ {
+ return datagroupHash;
+ }
+
+ public LDSVersionInfo getVersionInfo()
+ {
+ return versionInfo;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector seq = new ASN1EncodableVector();
+
+ seq.add(version);
+ seq.add(digestAlgorithmIdentifier);
+
+ ASN1EncodableVector seqname = new ASN1EncodableVector();
+ for (int i = 0; i < datagroupHash.length; i++)
+ {
+ seqname.add(datagroupHash[i]);
+ }
+ seq.add(new DERSequence(seqname));
+
+ if (versionInfo != null)
+ {
+ seq.add(versionInfo);
+ }
+
+ return new DERSequence(seq);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/icao/LDSVersionInfo.java b/core/src/main/java/org/bouncycastle/asn1/icao/LDSVersionInfo.java
new file mode 100644
index 00000000..9c5ae336
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/icao/LDSVersionInfo.java
@@ -0,0 +1,75 @@
+package org.bouncycastle.asn1.icao;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERPrintableString;
+import org.bouncycastle.asn1.DERSequence;
+
+public class LDSVersionInfo
+ extends ASN1Object
+{
+ private DERPrintableString ldsVersion;
+ private DERPrintableString unicodeVersion;
+
+ public LDSVersionInfo(String ldsVersion, String unicodeVersion)
+ {
+ this.ldsVersion = new DERPrintableString(ldsVersion);
+ this.unicodeVersion = new DERPrintableString(unicodeVersion);
+ }
+
+ private LDSVersionInfo(ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("sequence wrong size for LDSVersionInfo");
+ }
+
+ this.ldsVersion = DERPrintableString.getInstance(seq.getObjectAt(0));
+ this.unicodeVersion = DERPrintableString.getInstance(seq.getObjectAt(1));
+ }
+
+ public static LDSVersionInfo getInstance(Object obj)
+ {
+ if (obj instanceof LDSVersionInfo)
+ {
+ return (LDSVersionInfo)obj;
+ }
+ else if (obj != null)
+ {
+ return new LDSVersionInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public String getLdsVersion()
+ {
+ return ldsVersion.getString();
+ }
+
+ public String getUnicodeVersion()
+ {
+ return unicodeVersion.getString();
+ }
+
+ /**
+ * <pre>
+ * LDSVersionInfo ::= SEQUENCE {
+ * ldsVersion PRINTABLE STRING
+ * unicodeVersion PRINTABLE STRING
+ * }
+ * </pre>
+ * @return
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(ldsVersion);
+ v.add(unicodeVersion);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java
new file mode 100644
index 00000000..bc2ac8d5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java
@@ -0,0 +1,180 @@
+package org.bouncycastle.asn1.isismtt;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface ISISMTTObjectIdentifiers
+{
+
+ static final ASN1ObjectIdentifier id_isismtt = new ASN1ObjectIdentifier("1.3.36.8");
+
+ static final ASN1ObjectIdentifier id_isismtt_cp = id_isismtt.branch("1");
+
+ /**
+ * The id-isismtt-cp-accredited OID indicates that the certificate is a
+ * qualified certificate according to Directive 1999/93/EC of the European
+ * Parliament and of the Council of 13 December 1999 on a Community
+ * Framework for Electronic Signatures, which additionally conforms the
+ * special requirements of the SigG and has been issued by an accredited CA.
+ */
+ static final ASN1ObjectIdentifier id_isismtt_cp_accredited = id_isismtt_cp.branch("1");
+
+ static final ASN1ObjectIdentifier id_isismtt_at = id_isismtt.branch("3");
+
+ /**
+ * Certificate extensionDate of certificate generation
+ *
+ * <pre>
+ * DateOfCertGenSyntax ::= GeneralizedTime
+ * </pre>
+ */
+ static final ASN1ObjectIdentifier id_isismtt_at_dateOfCertGen = id_isismtt_at.branch("1");
+
+ /**
+ * Attribute to indicate that the certificate holder may sign in the name of
+ * a third person. May also be used as extension in a certificate.
+ */
+ static final ASN1ObjectIdentifier id_isismtt_at_procuration = id_isismtt_at.branch("2");
+
+ /**
+ * Attribute to indicate admissions to certain professions. May be used as
+ * attribute in attribute certificate or as extension in a certificate
+ */
+ static final ASN1ObjectIdentifier id_isismtt_at_admission = id_isismtt_at.branch("3");
+
+ /**
+ * Monetary limit for transactions. The QcEuMonetaryLimit QC statement MUST
+ * be used in new certificates in place of the extension/attribute
+ * MonetaryLimit since January 1, 2004. For the sake of backward
+ * compatibility with certificates already in use, SigG conforming
+ * components MUST support MonetaryLimit (as well as QcEuLimitValue).
+ */
+ static final ASN1ObjectIdentifier id_isismtt_at_monetaryLimit = id_isismtt_at.branch("4");
+
+ /**
+ * A declaration of majority. May be used as attribute in attribute
+ * certificate or as extension in a certificate
+ */
+ static final ASN1ObjectIdentifier id_isismtt_at_declarationOfMajority = id_isismtt_at.branch("5");
+
+ /**
+ *
+ * Serial number of the smart card containing the corresponding private key
+ *
+ * <pre>
+ * ICCSNSyntax ::= OCTET STRING (SIZE(8..20))
+ * </pre>
+ */
+ static final ASN1ObjectIdentifier id_isismtt_at_iCCSN = id_isismtt_at.branch("6");
+
+ /**
+ *
+ * Reference for a file of a smartcard that stores the public key of this
+ * certificate and that is used as �security anchor�.
+ *
+ * <pre>
+ * PKReferenceSyntax ::= OCTET STRING (SIZE(20))
+ * </pre>
+ */
+ static final ASN1ObjectIdentifier id_isismtt_at_PKReference = id_isismtt_at.branch("7");
+
+ /**
+ * Some other restriction regarding the usage of this certificate. May be
+ * used as attribute in attribute certificate or as extension in a
+ * certificate.
+ *
+ * <pre>
+ * RestrictionSyntax ::= DirectoryString (SIZE(1..1024))
+ * </pre>
+ *
+ * @see org.bouncycastle.asn1.isismtt.x509.Restriction
+ */
+ static final ASN1ObjectIdentifier id_isismtt_at_restriction = id_isismtt_at.branch("8");
+
+ /**
+ *
+ * (Single)Request extension: Clients may include this extension in a
+ * (single) Request to request the responder to send the certificate in the
+ * response message along with the status information. Besides the LDAP
+ * service, this extension provides another mechanism for the distribution
+ * of certificates, which MAY optionally be provided by certificate
+ * repositories.
+ *
+ * <pre>
+ * RetrieveIfAllowed ::= BOOLEAN
+ *
+ * </pre>
+ */
+ static final ASN1ObjectIdentifier id_isismtt_at_retrieveIfAllowed = id_isismtt_at.branch("9");
+
+ /**
+ * SingleOCSPResponse extension: The certificate requested by the client by
+ * inserting the RetrieveIfAllowed extension in the request, will be
+ * returned in this extension.
+ *
+ * @see org.bouncycastle.asn1.isismtt.ocsp.RequestedCertificate
+ */
+ static final ASN1ObjectIdentifier id_isismtt_at_requestedCertificate = id_isismtt_at.branch("10");
+
+ /**
+ * Base ObjectIdentifier for naming authorities
+ */
+ static final ASN1ObjectIdentifier id_isismtt_at_namingAuthorities = id_isismtt_at.branch("11");
+
+ /**
+ * SingleOCSPResponse extension: Date, when certificate has been published
+ * in the directory and status information has become available. Currently,
+ * accrediting authorities enforce that SigG-conforming OCSP servers include
+ * this extension in the responses.
+ *
+ * <pre>
+ * CertInDirSince ::= GeneralizedTime
+ * </pre>
+ */
+ static final ASN1ObjectIdentifier id_isismtt_at_certInDirSince = id_isismtt_at.branch("12");
+
+ /**
+ * Hash of a certificate in OCSP.
+ *
+ * @see org.bouncycastle.asn1.isismtt.ocsp.CertHash
+ */
+ static final ASN1ObjectIdentifier id_isismtt_at_certHash = id_isismtt_at.branch("13");
+
+ /**
+ * <pre>
+ * NameAtBirth ::= DirectoryString(SIZE(1..64)
+ * </pre>
+ *
+ * Used in
+ * {@link org.bouncycastle.asn1.x509.SubjectDirectoryAttributes SubjectDirectoryAttributes}
+ */
+ static final ASN1ObjectIdentifier id_isismtt_at_nameAtBirth = id_isismtt_at.branch("14");
+
+ /**
+ * Some other information of non-restrictive nature regarding the usage of
+ * this certificate. May be used as attribute in atribute certificate or as
+ * extension in a certificate.
+ *
+ * <pre>
+ * AdditionalInformationSyntax ::= DirectoryString (SIZE(1..2048))
+ * </pre>
+ *
+ * @see org.bouncycastle.asn1.isismtt.x509.AdditionalInformationSyntax
+ */
+ static final ASN1ObjectIdentifier id_isismtt_at_additionalInformation = id_isismtt_at.branch("15");
+
+ /**
+ * Indicates that an attribute certificate exists, which limits the
+ * usability of this public key certificate. Whenever verifying a signature
+ * with the help of this certificate, the content of the corresponding
+ * attribute certificate should be concerned. This extension MUST be
+ * included in a PKC, if a corresponding attribute certificate (having the
+ * PKC as base certificate) contains some attribute that restricts the
+ * usability of the PKC too. Attribute certificates with restricting content
+ * MUST always be included in the signed document.
+ *
+ * <pre>
+ * LiabilityLimitationFlagSyntax ::= BOOLEAN
+ * </pre>
+ */
+ static final ASN1ObjectIdentifier id_isismtt_at_liabilityLimitationFlag = new ASN1ObjectIdentifier("0.2.262.1.10.12.0");
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/isismtt/ocsp/CertHash.java b/core/src/main/java/org/bouncycastle/asn1/isismtt/ocsp/CertHash.java
new file mode 100644
index 00000000..932d3008
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/isismtt/ocsp/CertHash.java
@@ -0,0 +1,124 @@
+package org.bouncycastle.asn1.isismtt.ocsp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * ISIS-MTT PROFILE: The responder may include this extension in a response to
+ * send the hash of the requested certificate to the responder. This hash is
+ * cryptographically bound to the certificate and serves as evidence that the
+ * certificate is known to the responder (i.e. it has been issued and is present
+ * in the directory). Hence, this extension is a means to provide a positive
+ * statement of availability as described in T8.[8]. As explained in T13.[1],
+ * clients may rely on this information to be able to validate signatures after
+ * the expiry of the corresponding certificate. Hence, clients MUST support this
+ * extension. If a positive statement of availability is to be delivered, this
+ * extension syntax and OID MUST be used.
+ * <p/>
+ * <p/>
+ * <pre>
+ * CertHash ::= SEQUENCE {
+ * hashAlgorithm AlgorithmIdentifier,
+ * certificateHash OCTET STRING
+ * }
+ * </pre>
+ */
+public class CertHash
+ extends ASN1Object
+{
+
+ private AlgorithmIdentifier hashAlgorithm;
+ private byte[] certificateHash;
+
+ public static CertHash getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof CertHash)
+ {
+ return (CertHash)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new CertHash((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: "
+ + obj.getClass().getName());
+ }
+
+ /**
+ * Constructor from ASN1Sequence.
+ * <p/>
+ * The sequence is of type CertHash:
+ * <p/>
+ * <pre>
+ * CertHash ::= SEQUENCE {
+ * hashAlgorithm AlgorithmIdentifier,
+ * certificateHash OCTET STRING
+ * }
+ * </pre>
+ *
+ * @param seq The ASN.1 sequence.
+ */
+ private CertHash(ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+ hashAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(0));
+ certificateHash = DEROctetString.getInstance(seq.getObjectAt(1)).getOctets();
+ }
+
+ /**
+ * Constructor from a given details.
+ *
+ * @param hashAlgorithm The hash algorithm identifier.
+ * @param certificateHash The hash of the whole DER encoding of the certificate.
+ */
+ public CertHash(AlgorithmIdentifier hashAlgorithm, byte[] certificateHash)
+ {
+ this.hashAlgorithm = hashAlgorithm;
+ this.certificateHash = new byte[certificateHash.length];
+ System.arraycopy(certificateHash, 0, this.certificateHash, 0,
+ certificateHash.length);
+ }
+
+ public AlgorithmIdentifier getHashAlgorithm()
+ {
+ return hashAlgorithm;
+ }
+
+ public byte[] getCertificateHash()
+ {
+ return certificateHash;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <p/>
+ * Returns:
+ * <p/>
+ * <pre>
+ * CertHash ::= SEQUENCE {
+ * hashAlgorithm AlgorithmIdentifier,
+ * certificateHash OCTET STRING
+ * }
+ * </pre>
+ *
+ * @return a DERObject
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+ vec.add(hashAlgorithm);
+ vec.add(new DEROctetString(certificateHash));
+ return new DERSequence(vec);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/isismtt/ocsp/RequestedCertificate.java b/core/src/main/java/org/bouncycastle/asn1/isismtt/ocsp/RequestedCertificate.java
new file mode 100644
index 00000000..cffcc5ab
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/isismtt/ocsp/RequestedCertificate.java
@@ -0,0 +1,183 @@
+package org.bouncycastle.asn1.isismtt.ocsp;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.Certificate;
+
+/**
+ * ISIS-MTT-Optional: The certificate requested by the client by inserting the
+ * RetrieveIfAllowed extension in the request, will be returned in this
+ * extension.
+ * <p/>
+ * ISIS-MTT-SigG: The signature act allows publishing certificates only then,
+ * when the certificate owner gives his explicit permission. Accordingly, there
+ * may be �nondownloadable� certificates, about which the responder must provide
+ * status information, but MUST NOT include them in the response. Clients may
+ * get therefore the following three kind of answers on a single request
+ * including the RetrieveIfAllowed extension:
+ * <ul>
+ * <li> a) the responder supports the extension and is allowed to publish the
+ * certificate: RequestedCertificate returned including the requested
+ * certificate
+ * <li>b) the responder supports the extension but is NOT allowed to publish
+ * the certificate: RequestedCertificate returned including an empty OCTET
+ * STRING
+ * <li>c) the responder does not support the extension: RequestedCertificate is
+ * not included in the response
+ * </ul>
+ * Clients requesting RetrieveIfAllowed MUST be able to handle these cases. If
+ * any of the OCTET STRING options is used, it MUST contain the DER encoding of
+ * the requested certificate.
+ * <p/>
+ * <pre>
+ * RequestedCertificate ::= CHOICE {
+ * Certificate Certificate,
+ * publicKeyCertificate [0] EXPLICIT OCTET STRING,
+ * attributeCertificate [1] EXPLICIT OCTET STRING
+ * }
+ * </pre>
+ */
+public class RequestedCertificate
+ extends ASN1Object
+ implements ASN1Choice
+{
+ public static final int certificate = -1;
+ public static final int publicKeyCertificate = 0;
+ public static final int attributeCertificate = 1;
+
+ private Certificate cert;
+ private byte[] publicKeyCert;
+ private byte[] attributeCert;
+
+ public static RequestedCertificate getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof RequestedCertificate)
+ {
+ return (RequestedCertificate)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new RequestedCertificate(Certificate.getInstance(obj));
+ }
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return new RequestedCertificate((ASN1TaggedObject)obj);
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: "
+ + obj.getClass().getName());
+ }
+
+ public static RequestedCertificate getInstance(ASN1TaggedObject obj, boolean explicit)
+ {
+ if (!explicit)
+ {
+ throw new IllegalArgumentException("choice item must be explicitly tagged");
+ }
+
+ return getInstance(obj.getObject());
+ }
+
+ private RequestedCertificate(ASN1TaggedObject tagged)
+ {
+ if (tagged.getTagNo() == publicKeyCertificate)
+ {
+ publicKeyCert = ASN1OctetString.getInstance(tagged, true).getOctets();
+ }
+ else if (tagged.getTagNo() == attributeCertificate)
+ {
+ attributeCert = ASN1OctetString.getInstance(tagged, true).getOctets();
+ }
+ else
+ {
+ throw new IllegalArgumentException("unknown tag number: " + tagged.getTagNo());
+ }
+ }
+
+ /**
+ * Constructor from a given details.
+ * <p/>
+ * Only one parameter can be given. All other must be <code>null</code>.
+ *
+ * @param certificate Given as Certificate
+ */
+ public RequestedCertificate(Certificate certificate)
+ {
+ this.cert = certificate;
+ }
+
+ public RequestedCertificate(int type, byte[] certificateOctets)
+ {
+ this(new DERTaggedObject(type, new DEROctetString(certificateOctets)));
+ }
+
+ public int getType()
+ {
+ if (cert != null)
+ {
+ return certificate;
+ }
+ if (publicKeyCert != null)
+ {
+ return publicKeyCertificate;
+ }
+ return attributeCertificate;
+ }
+
+ public byte[] getCertificateBytes()
+ {
+ if (cert != null)
+ {
+ try
+ {
+ return cert.getEncoded();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("can't decode certificate: " + e);
+ }
+ }
+ if (publicKeyCert != null)
+ {
+ return publicKeyCert;
+ }
+ return attributeCert;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <p/>
+ * Returns:
+ * <p/>
+ * <pre>
+ * RequestedCertificate ::= CHOICE {
+ * Certificate Certificate,
+ * publicKeyCertificate [0] EXPLICIT OCTET STRING,
+ * attributeCertificate [1] EXPLICIT OCTET STRING
+ * }
+ * </pre>
+ *
+ * @return a DERObject
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ if (publicKeyCert != null)
+ {
+ return new DERTaggedObject(0, new DEROctetString(publicKeyCert));
+ }
+ if (attributeCert != null)
+ {
+ return new DERTaggedObject(1, new DEROctetString(attributeCert));
+ }
+ return cert.toASN1Primitive();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/AdditionalInformationSyntax.java b/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/AdditionalInformationSyntax.java
new file mode 100644
index 00000000..ff9ed124
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/AdditionalInformationSyntax.java
@@ -0,0 +1,70 @@
+package org.bouncycastle.asn1.isismtt.x509;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.x500.DirectoryString;
+
+/**
+ * Some other information of non-restrictive nature regarding the usage of this
+ * certificate.
+ *
+ * <pre>
+ * AdditionalInformationSyntax ::= DirectoryString (SIZE(1..2048))
+ * </pre>
+ */
+public class AdditionalInformationSyntax
+ extends ASN1Object
+{
+ private DirectoryString information;
+
+ public static AdditionalInformationSyntax getInstance(Object obj)
+ {
+ if (obj instanceof AdditionalInformationSyntax)
+ {
+ return (AdditionalInformationSyntax)obj;
+ }
+
+ if (obj != null)
+ {
+ return new AdditionalInformationSyntax(DirectoryString.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private AdditionalInformationSyntax(DirectoryString information)
+ {
+ this.information = information;
+ }
+
+ /**
+ * Constructor from a given details.
+ *
+ * @param information The describtion of the information.
+ */
+ public AdditionalInformationSyntax(String information)
+ {
+ this(new DirectoryString(information));
+ }
+
+ public DirectoryString getInformation()
+ {
+ return information;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <p/>
+ * Returns:
+ * <p/>
+ * <pre>
+ * AdditionalInformationSyntax ::= DirectoryString (SIZE(1..2048))
+ * </pre>
+ *
+ * @return a DERObject
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return information.toASN1Primitive();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/AdmissionSyntax.java b/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/AdmissionSyntax.java
new file mode 100644
index 00000000..202373ec
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/AdmissionSyntax.java
@@ -0,0 +1,280 @@
+package org.bouncycastle.asn1.isismtt.x509;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.GeneralName;
+
+/**
+ * Attribute to indicate admissions to certain professions.
+ * <p/>
+ * <pre>
+ * AdmissionSyntax ::= SEQUENCE
+ * {
+ * admissionAuthority GeneralName OPTIONAL,
+ * contentsOfAdmissions SEQUENCE OF Admissions
+ * }
+ * <p/>
+ * Admissions ::= SEQUENCE
+ * {
+ * admissionAuthority [0] EXPLICIT GeneralName OPTIONAL
+ * namingAuthority [1] EXPLICIT NamingAuthority OPTIONAL
+ * professionInfos SEQUENCE OF ProfessionInfo
+ * }
+ * <p/>
+ * NamingAuthority ::= SEQUENCE
+ * {
+ * namingAuthorityId OBJECT IDENTIFIER OPTIONAL,
+ * namingAuthorityUrl IA5String OPTIONAL,
+ * namingAuthorityText DirectoryString(SIZE(1..128)) OPTIONAL
+ * }
+ * <p/>
+ * ProfessionInfo ::= SEQUENCE
+ * {
+ * namingAuthority [0] EXPLICIT NamingAuthority OPTIONAL,
+ * professionItems SEQUENCE OF DirectoryString (SIZE(1..128)),
+ * professionOIDs SEQUENCE OF OBJECT IDENTIFIER OPTIONAL,
+ * registrationNumber PrintableString(SIZE(1..128)) OPTIONAL,
+ * addProfessionInfo OCTET STRING OPTIONAL
+ * }
+ * </pre>
+ * <p/>
+ * <p/>
+ * ISIS-MTT PROFILE: The relatively complex structure of AdmissionSyntax
+ * supports the following concepts and requirements:
+ * <ul>
+ * <li> External institutions (e.g. professional associations, chambers, unions,
+ * administrative bodies, companies, etc.), which are responsible for granting
+ * and verifying professional admissions, are indicated by means of the data
+ * field admissionAuthority. An admission authority is indicated by a
+ * GeneralName object. Here an X.501 directory name (distinguished name) can be
+ * indicated in the field directoryName, a URL address can be indicated in the
+ * field uniformResourceIdentifier, and an object identifier can be indicated in
+ * the field registeredId.
+ * <li> The names of authorities which are responsible for the administration of
+ * title registers are indicated in the data field namingAuthority. The name of
+ * the authority can be identified by an object identifier in the field
+ * namingAuthorityId, by means of a text string in the field
+ * namingAuthorityText, by means of a URL address in the field
+ * namingAuthorityUrl, or by a combination of them. For example, the text string
+ * can contain the name of the authority, the country and the name of the title
+ * register. The URL-option refers to a web page which contains lists with
+ * �officially� registered professions (text and possibly OID) as well as
+ * further information on these professions. Object identifiers for the
+ * component namingAuthorityId are grouped under the OID-branch
+ * id-isis-at-namingAuthorities and must be applied for.
+ * <li>See
+ * http://www.teletrust.de/anwend.asp?Id=30200&Sprache=E_&HomePG=0 for
+ * an application form and http://www.teletrust.de/links.asp?id=30220,11
+ * for an overview of registered naming authorities.
+ * <li> By means of the data type ProfessionInfo certain professions,
+ * specializations, disciplines, fields of activity, etc. are identified. A
+ * profession is represented by one or more text strings, resp. profession OIDs
+ * in the fields professionItems and professionOIDs and by a registration number
+ * in the field registrationNumber. An indication in text form must always be
+ * present, whereas the other indications are optional. The component
+ * addProfessionInfo may contain additional applicationspecific information in
+ * DER-encoded form.
+ * </ul>
+ * <p/>
+ * By means of different namingAuthority-OIDs or profession OIDs hierarchies of
+ * professions, specializations, disciplines, fields of activity, etc. can be
+ * expressed. The issuing admission authority should always be indicated (field
+ * admissionAuthority), whenever a registration number is presented. Still,
+ * information on admissions can be given without indicating an admission or a
+ * naming authority by the exclusive use of the component professionItems. In
+ * this case the certification authority is responsible for the verification of
+ * the admission information.
+ * <p/>
+ * <p/>
+ * <p/>
+ * This attribute is single-valued. Still, several admissions can be captured in
+ * the sequence structure of the component contentsOfAdmissions of
+ * AdmissionSyntax or in the component professionInfos of Admissions. The
+ * component admissionAuthority of AdmissionSyntax serves as default value for
+ * the component admissionAuthority of Admissions. Within the latter component
+ * the default value can be overwritten, in case that another authority is
+ * responsible. The component namingAuthority of Admissions serves as a default
+ * value for the component namingAuthority of ProfessionInfo. Within the latter
+ * component the default value can be overwritten, in case that another naming
+ * authority needs to be recorded.
+ * <p/>
+ * The length of the string objects is limited to 128 characters. It is
+ * recommended to indicate a namingAuthorityURL in all issued attribute
+ * certificates. If a namingAuthorityURL is indicated, the field professionItems
+ * of ProfessionInfo should contain only registered titles. If the field
+ * professionOIDs exists, it has to contain the OIDs of the professions listed
+ * in professionItems in the same order. In general, the field professionInfos
+ * should contain only one entry, unless the admissions that are to be listed
+ * are logically connected (e.g. they have been issued under the same admission
+ * number).
+ *
+ * @see org.bouncycastle.asn1.isismtt.x509.Admissions
+ * @see org.bouncycastle.asn1.isismtt.x509.ProfessionInfo
+ * @see org.bouncycastle.asn1.isismtt.x509.NamingAuthority
+ */
+public class AdmissionSyntax
+ extends ASN1Object
+{
+
+ private GeneralName admissionAuthority;
+
+ private ASN1Sequence contentsOfAdmissions;
+
+ public static AdmissionSyntax getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof AdmissionSyntax)
+ {
+ return (AdmissionSyntax)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new AdmissionSyntax((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: "
+ + obj.getClass().getName());
+ }
+
+ /**
+ * Constructor from ASN1Sequence.
+ * <p/>
+ * The sequence is of type ProcurationSyntax:
+ * <p/>
+ * <pre>
+ * AdmissionSyntax ::= SEQUENCE
+ * {
+ * admissionAuthority GeneralName OPTIONAL,
+ * contentsOfAdmissions SEQUENCE OF Admissions
+ * }
+ * <p/>
+ * Admissions ::= SEQUENCE
+ * {
+ * admissionAuthority [0] EXPLICIT GeneralName OPTIONAL
+ * namingAuthority [1] EXPLICIT NamingAuthority OPTIONAL
+ * professionInfos SEQUENCE OF ProfessionInfo
+ * }
+ * <p/>
+ * NamingAuthority ::= SEQUENCE
+ * {
+ * namingAuthorityId OBJECT IDENTIFIER OPTIONAL,
+ * namingAuthorityUrl IA5String OPTIONAL,
+ * namingAuthorityText DirectoryString(SIZE(1..128)) OPTIONAL
+ * }
+ * <p/>
+ * ProfessionInfo ::= SEQUENCE
+ * {
+ * namingAuthority [0] EXPLICIT NamingAuthority OPTIONAL,
+ * professionItems SEQUENCE OF DirectoryString (SIZE(1..128)),
+ * professionOIDs SEQUENCE OF OBJECT IDENTIFIER OPTIONAL,
+ * registrationNumber PrintableString(SIZE(1..128)) OPTIONAL,
+ * addProfessionInfo OCTET STRING OPTIONAL
+ * }
+ * </pre>
+ *
+ * @param seq The ASN.1 sequence.
+ */
+ private AdmissionSyntax(ASN1Sequence seq)
+ {
+ switch (seq.size())
+ {
+ case 1:
+ contentsOfAdmissions = DERSequence.getInstance(seq.getObjectAt(0));
+ break;
+ case 2:
+ admissionAuthority = GeneralName.getInstance(seq.getObjectAt(0));
+ contentsOfAdmissions = DERSequence.getInstance(seq.getObjectAt(1));
+ break;
+ default:
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+ }
+
+ /**
+ * Constructor from given details.
+ *
+ * @param admissionAuthority The admission authority.
+ * @param contentsOfAdmissions The admissions.
+ */
+ public AdmissionSyntax(GeneralName admissionAuthority, ASN1Sequence contentsOfAdmissions)
+ {
+ this.admissionAuthority = admissionAuthority;
+ this.contentsOfAdmissions = contentsOfAdmissions;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <p/>
+ * Returns:
+ * <p/>
+ * <pre>
+ * AdmissionSyntax ::= SEQUENCE
+ * {
+ * admissionAuthority GeneralName OPTIONAL,
+ * contentsOfAdmissions SEQUENCE OF Admissions
+ * }
+ * <p/>
+ * Admissions ::= SEQUENCE
+ * {
+ * admissionAuthority [0] EXPLICIT GeneralName OPTIONAL
+ * namingAuthority [1] EXPLICIT NamingAuthority OPTIONAL
+ * professionInfos SEQUENCE OF ProfessionInfo
+ * }
+ * <p/>
+ * NamingAuthority ::= SEQUENCE
+ * {
+ * namingAuthorityId OBJECT IDENTIFIER OPTIONAL,
+ * namingAuthorityUrl IA5String OPTIONAL,
+ * namingAuthorityText DirectoryString(SIZE(1..128)) OPTIONAL
+ * }
+ * <p/>
+ * ProfessionInfo ::= SEQUENCE
+ * {
+ * namingAuthority [0] EXPLICIT NamingAuthority OPTIONAL,
+ * professionItems SEQUENCE OF DirectoryString (SIZE(1..128)),
+ * professionOIDs SEQUENCE OF OBJECT IDENTIFIER OPTIONAL,
+ * registrationNumber PrintableString(SIZE(1..128)) OPTIONAL,
+ * addProfessionInfo OCTET STRING OPTIONAL
+ * }
+ * </pre>
+ *
+ * @return a DERObject
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+ if (admissionAuthority != null)
+ {
+ vec.add(admissionAuthority);
+ }
+ vec.add(contentsOfAdmissions);
+ return new DERSequence(vec);
+ }
+
+ /**
+ * @return Returns the admissionAuthority if present, null otherwise.
+ */
+ public GeneralName getAdmissionAuthority()
+ {
+ return admissionAuthority;
+ }
+
+ /**
+ * @return Returns the contentsOfAdmissions.
+ */
+ public Admissions[] getContentsOfAdmissions()
+ {
+ Admissions[] admissions = new Admissions[contentsOfAdmissions.size()];
+ int count = 0;
+ for (Enumeration e = contentsOfAdmissions.getObjects(); e.hasMoreElements();)
+ {
+ admissions[count++] = Admissions.getInstance(e.nextElement());
+ }
+ return admissions;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/Admissions.java b/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/Admissions.java
new file mode 100644
index 00000000..3a5ef242
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/Admissions.java
@@ -0,0 +1,189 @@
+package org.bouncycastle.asn1.isismtt.x509;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.GeneralName;
+
+/**
+ * An Admissions structure.
+ * <p/>
+ * <pre>
+ * Admissions ::= SEQUENCE
+ * {
+ * admissionAuthority [0] EXPLICIT GeneralName OPTIONAL
+ * namingAuthority [1] EXPLICIT NamingAuthority OPTIONAL
+ * professionInfos SEQUENCE OF ProfessionInfo
+ * }
+ * <p/>
+ * </pre>
+ *
+ * @see org.bouncycastle.asn1.isismtt.x509.AdmissionSyntax
+ * @see org.bouncycastle.asn1.isismtt.x509.ProfessionInfo
+ * @see org.bouncycastle.asn1.isismtt.x509.NamingAuthority
+ */
+public class Admissions
+ extends ASN1Object
+{
+
+ private GeneralName admissionAuthority;
+
+ private NamingAuthority namingAuthority;
+
+ private ASN1Sequence professionInfos;
+
+ public static Admissions getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof Admissions)
+ {
+ return (Admissions)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new Admissions((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * Constructor from ASN1Sequence.
+ * <p/>
+ * The sequence is of type ProcurationSyntax:
+ * <p/>
+ * <pre>
+ * Admissions ::= SEQUENCE
+ * {
+ * admissionAuthority [0] EXPLICIT GeneralName OPTIONAL
+ * namingAuthority [1] EXPLICIT NamingAuthority OPTIONAL
+ * professionInfos SEQUENCE OF ProfessionInfo
+ * }
+ * </pre>
+ *
+ * @param seq The ASN.1 sequence.
+ */
+ private Admissions(ASN1Sequence seq)
+ {
+ if (seq.size() > 3)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+ Enumeration e = seq.getObjects();
+
+ ASN1Encodable o = (ASN1Encodable)e.nextElement();
+ if (o instanceof ASN1TaggedObject)
+ {
+ switch (((ASN1TaggedObject)o).getTagNo())
+ {
+ case 0:
+ admissionAuthority = GeneralName.getInstance((ASN1TaggedObject)o, true);
+ break;
+ case 1:
+ namingAuthority = NamingAuthority.getInstance((ASN1TaggedObject)o, true);
+ break;
+ default:
+ throw new IllegalArgumentException("Bad tag number: " + ((ASN1TaggedObject)o).getTagNo());
+ }
+ o = (ASN1Encodable)e.nextElement();
+ }
+ if (o instanceof ASN1TaggedObject)
+ {
+ switch (((ASN1TaggedObject)o).getTagNo())
+ {
+ case 1:
+ namingAuthority = NamingAuthority.getInstance((ASN1TaggedObject)o, true);
+ break;
+ default:
+ throw new IllegalArgumentException("Bad tag number: " + ((ASN1TaggedObject)o).getTagNo());
+ }
+ o = (ASN1Encodable)e.nextElement();
+ }
+ professionInfos = ASN1Sequence.getInstance(o);
+ if (e.hasMoreElements())
+ {
+ throw new IllegalArgumentException("Bad object encountered: "
+ + e.nextElement().getClass());
+ }
+ }
+
+ /**
+ * Constructor from a given details.
+ * <p/>
+ * Parameter <code>professionInfos</code> is mandatory.
+ *
+ * @param admissionAuthority The admission authority.
+ * @param namingAuthority The naming authority.
+ * @param professionInfos The profession infos.
+ */
+ public Admissions(GeneralName admissionAuthority,
+ NamingAuthority namingAuthority, ProfessionInfo[] professionInfos)
+ {
+ this.admissionAuthority = admissionAuthority;
+ this.namingAuthority = namingAuthority;
+ this.professionInfos = new DERSequence(professionInfos);
+ }
+
+ public GeneralName getAdmissionAuthority()
+ {
+ return admissionAuthority;
+ }
+
+ public NamingAuthority getNamingAuthority()
+ {
+ return namingAuthority;
+ }
+
+ public ProfessionInfo[] getProfessionInfos()
+ {
+ ProfessionInfo[] infos = new ProfessionInfo[professionInfos.size()];
+ int count = 0;
+ for (Enumeration e = professionInfos.getObjects(); e.hasMoreElements();)
+ {
+ infos[count++] = ProfessionInfo.getInstance(e.nextElement());
+ }
+ return infos;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <p/>
+ * Returns:
+ * <p/>
+ * <pre>
+ * Admissions ::= SEQUENCE
+ * {
+ * admissionAuthority [0] EXPLICIT GeneralName OPTIONAL
+ * namingAuthority [1] EXPLICIT NamingAuthority OPTIONAL
+ * professionInfos SEQUENCE OF ProfessionInfo
+ * }
+ * <p/>
+ * </pre>
+ *
+ * @return an ASN1Primitive
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+
+ if (admissionAuthority != null)
+ {
+ vec.add(new DERTaggedObject(true, 0, admissionAuthority));
+ }
+ if (namingAuthority != null)
+ {
+ vec.add(new DERTaggedObject(true, 1, namingAuthority));
+ }
+ vec.add(professionInfos);
+
+ return new DERSequence(vec);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/DeclarationOfMajority.java b/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/DeclarationOfMajority.java
new file mode 100644
index 00000000..20887cee
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/DeclarationOfMajority.java
@@ -0,0 +1,164 @@
+package org.bouncycastle.asn1.isismtt.x509;
+
+import org.bouncycastle.asn1.ASN1Boolean;
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERPrintableString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * A declaration of majority.
+ * <p/>
+ * <pre>
+ * DeclarationOfMajoritySyntax ::= CHOICE
+ * {
+ * notYoungerThan [0] IMPLICIT INTEGER,
+ * fullAgeAtCountry [1] IMPLICIT SEQUENCE
+ * {
+ * fullAge BOOLEAN DEFAULT TRUE,
+ * country PrintableString (SIZE(2))
+ * }
+ * dateOfBirth [2] IMPLICIT GeneralizedTime
+ * }
+ * </pre>
+ * <p/>
+ * fullAgeAtCountry indicates the majority of the owner with respect to the laws
+ * of a specific country.
+ */
+public class DeclarationOfMajority
+ extends ASN1Object
+ implements ASN1Choice
+{
+ public static final int notYoungerThan = 0;
+ public static final int fullAgeAtCountry = 1;
+ public static final int dateOfBirth = 2;
+
+ private ASN1TaggedObject declaration;
+
+ public DeclarationOfMajority(int notYoungerThan)
+ {
+ declaration = new DERTaggedObject(false, 0, new ASN1Integer(notYoungerThan));
+ }
+
+ public DeclarationOfMajority(boolean fullAge, String country)
+ {
+ if (country.length() > 2)
+ {
+ throw new IllegalArgumentException("country can only be 2 characters");
+ }
+
+ if (fullAge)
+ {
+ declaration = new DERTaggedObject(false, 1, new DERSequence(new DERPrintableString(country, true)));
+ }
+ else
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(ASN1Boolean.FALSE);
+ v.add(new DERPrintableString(country, true));
+
+ declaration = new DERTaggedObject(false, 1, new DERSequence(v));
+ }
+ }
+
+ public DeclarationOfMajority(ASN1GeneralizedTime dateOfBirth)
+ {
+ declaration = new DERTaggedObject(false, 2, dateOfBirth);
+ }
+
+ public static DeclarationOfMajority getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof DeclarationOfMajority)
+ {
+ return (DeclarationOfMajority)obj;
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return new DeclarationOfMajority((ASN1TaggedObject)obj);
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: "
+ + obj.getClass().getName());
+ }
+
+ private DeclarationOfMajority(ASN1TaggedObject o)
+ {
+ if (o.getTagNo() > 2)
+ {
+ throw new IllegalArgumentException("Bad tag number: " + o.getTagNo());
+ }
+ declaration = o;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <p/>
+ * Returns:
+ * <p/>
+ * <pre>
+ * DeclarationOfMajoritySyntax ::= CHOICE
+ * {
+ * notYoungerThan [0] IMPLICIT INTEGER,
+ * fullAgeAtCountry [1] IMPLICIT SEQUENCE
+ * {
+ * fullAge BOOLEAN DEFAULT TRUE,
+ * country PrintableString (SIZE(2))
+ * }
+ * dateOfBirth [2] IMPLICIT GeneralizedTime
+ * }
+ * </pre>
+ *
+ * @return a DERObject
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return declaration;
+ }
+
+ public int getType()
+ {
+ return declaration.getTagNo();
+ }
+
+ /**
+ * @return notYoungerThan if that's what we are, -1 otherwise
+ */
+ public int notYoungerThan()
+ {
+ if (declaration.getTagNo() != 0)
+ {
+ return -1;
+ }
+
+ return ASN1Integer.getInstance(declaration, false).getValue().intValue();
+ }
+
+ public ASN1Sequence fullAgeAtCountry()
+ {
+ if (declaration.getTagNo() != 1)
+ {
+ return null;
+ }
+
+ return ASN1Sequence.getInstance(declaration, false);
+ }
+
+ public ASN1GeneralizedTime getDateOfBirth()
+ {
+ if (declaration.getTagNo() != 2)
+ {
+ return null;
+ }
+
+ return ASN1GeneralizedTime.getInstance(declaration, false);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/MonetaryLimit.java b/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/MonetaryLimit.java
new file mode 100644
index 00000000..1b101998
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/MonetaryLimit.java
@@ -0,0 +1,131 @@
+package org.bouncycastle.asn1.isismtt.x509;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERPrintableString;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * Monetary limit for transactions. The QcEuMonetaryLimit QC statement MUST be
+ * used in new certificates in place of the extension/attribute MonetaryLimit
+ * since January 1, 2004. For the sake of backward compatibility with
+ * certificates already in use, components SHOULD support MonetaryLimit (as well
+ * as QcEuLimitValue).
+ * <p/>
+ * Indicates a monetary limit within which the certificate holder is authorized
+ * to act. (This value DOES NOT express a limit on the liability of the
+ * certification authority).
+ * <p/>
+ * <pre>
+ * MonetaryLimitSyntax ::= SEQUENCE
+ * {
+ * currency PrintableString (SIZE(3)),
+ * amount INTEGER,
+ * exponent INTEGER
+ * }
+ * </pre>
+ * <p/>
+ * currency must be the ISO code.
+ * <p/>
+ * value = amount�10*exponent
+ */
+public class MonetaryLimit
+ extends ASN1Object
+{
+ DERPrintableString currency;
+ ASN1Integer amount;
+ ASN1Integer exponent;
+
+ public static MonetaryLimit getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof MonetaryLimit)
+ {
+ return (MonetaryLimit)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new MonetaryLimit(ASN1Sequence.getInstance(obj));
+ }
+
+ throw new IllegalArgumentException("unknown object in getInstance");
+ }
+
+ private MonetaryLimit(ASN1Sequence seq)
+ {
+ if (seq.size() != 3)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+ Enumeration e = seq.getObjects();
+ currency = DERPrintableString.getInstance(e.nextElement());
+ amount = ASN1Integer.getInstance(e.nextElement());
+ exponent = ASN1Integer.getInstance(e.nextElement());
+ }
+
+ /**
+ * Constructor from a given details.
+ * <p/>
+ * <p/>
+ * value = amount�10^exponent
+ *
+ * @param currency The currency. Must be the ISO code.
+ * @param amount The amount
+ * @param exponent The exponent
+ */
+ public MonetaryLimit(String currency, int amount, int exponent)
+ {
+ this.currency = new DERPrintableString(currency, true);
+ this.amount = new ASN1Integer(amount);
+ this.exponent = new ASN1Integer(exponent);
+ }
+
+ public String getCurrency()
+ {
+ return currency.getString();
+ }
+
+ public BigInteger getAmount()
+ {
+ return amount.getValue();
+ }
+
+ public BigInteger getExponent()
+ {
+ return exponent.getValue();
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <p/>
+ * Returns:
+ * <p/>
+ * <pre>
+ * MonetaryLimitSyntax ::= SEQUENCE
+ * {
+ * currency PrintableString (SIZE(3)),
+ * amount INTEGER,
+ * exponent INTEGER
+ * }
+ * </pre>
+ *
+ * @return a DERObject
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector seq = new ASN1EncodableVector();
+ seq.add(currency);
+ seq.add(amount);
+ seq.add(exponent);
+
+ return new DERSequence(seq);
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/NamingAuthority.java b/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/NamingAuthority.java
new file mode 100644
index 00000000..237f5e55
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/NamingAuthority.java
@@ -0,0 +1,244 @@
+package org.bouncycastle.asn1.isismtt.x509;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1String;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.isismtt.ISISMTTObjectIdentifiers;
+import org.bouncycastle.asn1.x500.DirectoryString;
+
+/**
+ * Names of authorities which are responsible for the administration of title
+ * registers.
+ *
+ * <pre>
+ * NamingAuthority ::= SEQUENCE
+ * {
+ * namingAuthorityId OBJECT IDENTIFIER OPTIONAL,
+ * namingAuthorityUrl IA5String OPTIONAL,
+ * namingAuthorityText DirectoryString(SIZE(1..128)) OPTIONAL
+ * }
+ * </pre>
+ * @see org.bouncycastle.asn1.isismtt.x509.AdmissionSyntax
+ *
+ */
+public class NamingAuthority
+ extends ASN1Object
+{
+
+ /**
+ * Profession OIDs should always be defined under the OID branch of the
+ * responsible naming authority. At the time of this writing, the work group
+ * �Recht, Wirtschaft, Steuern� (�Law, Economy, Taxes�) is registered as the
+ * first naming authority under the OID id-isismtt-at-namingAuthorities.
+ */
+ public static final ASN1ObjectIdentifier id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern =
+ new ASN1ObjectIdentifier(ISISMTTObjectIdentifiers.id_isismtt_at_namingAuthorities + ".1");
+
+ private ASN1ObjectIdentifier namingAuthorityId;
+ private String namingAuthorityUrl;
+ private DirectoryString namingAuthorityText;
+
+ public static NamingAuthority getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof NamingAuthority)
+ {
+ return (NamingAuthority)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new NamingAuthority((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: "
+ + obj.getClass().getName());
+ }
+
+ public static NamingAuthority getInstance(ASN1TaggedObject obj, boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * Constructor from ASN1Sequence.
+ * <p/>
+ * <p/>
+ * <pre>
+ * NamingAuthority ::= SEQUENCE
+ * {
+ * namingAuthorityId OBJECT IDENTIFIER OPTIONAL,
+ * namingAuthorityUrl IA5String OPTIONAL,
+ * namingAuthorityText DirectoryString(SIZE(1..128)) OPTIONAL
+ * }
+ * </pre>
+ *
+ * @param seq The ASN.1 sequence.
+ */
+ private NamingAuthority(ASN1Sequence seq)
+ {
+
+ if (seq.size() > 3)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ Enumeration e = seq.getObjects();
+
+ if (e.hasMoreElements())
+ {
+ ASN1Encodable o = (ASN1Encodable)e.nextElement();
+ if (o instanceof ASN1ObjectIdentifier)
+ {
+ namingAuthorityId = (ASN1ObjectIdentifier)o;
+ }
+ else if (o instanceof DERIA5String)
+ {
+ namingAuthorityUrl = DERIA5String.getInstance(o).getString();
+ }
+ else if (o instanceof ASN1String)
+ {
+ namingAuthorityText = DirectoryString.getInstance(o);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Bad object encountered: "
+ + o.getClass());
+ }
+ }
+ if (e.hasMoreElements())
+ {
+ ASN1Encodable o = (ASN1Encodable)e.nextElement();
+ if (o instanceof DERIA5String)
+ {
+ namingAuthorityUrl = DERIA5String.getInstance(o).getString();
+ }
+ else if (o instanceof ASN1String)
+ {
+ namingAuthorityText = DirectoryString.getInstance(o);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Bad object encountered: "
+ + o.getClass());
+ }
+ }
+ if (e.hasMoreElements())
+ {
+ ASN1Encodable o = (ASN1Encodable)e.nextElement();
+ if (o instanceof ASN1String)
+ {
+ namingAuthorityText = DirectoryString.getInstance(o);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Bad object encountered: "
+ + o.getClass());
+ }
+
+ }
+ }
+
+ /**
+ * @return Returns the namingAuthorityId.
+ */
+ public ASN1ObjectIdentifier getNamingAuthorityId()
+ {
+ return namingAuthorityId;
+ }
+
+ /**
+ * @return Returns the namingAuthorityText.
+ */
+ public DirectoryString getNamingAuthorityText()
+ {
+ return namingAuthorityText;
+ }
+
+ /**
+ * @return Returns the namingAuthorityUrl.
+ */
+ public String getNamingAuthorityUrl()
+ {
+ return namingAuthorityUrl;
+ }
+
+ /**
+ * Constructor from given details.
+ * <p/>
+ * All parameters can be combined.
+ *
+ * @param namingAuthorityId ObjectIdentifier for naming authority.
+ * @param namingAuthorityUrl URL for naming authority.
+ * @param namingAuthorityText Textual representation of naming authority.
+ * @deprecated use ASN1ObjectIdentifier method
+ */
+ public NamingAuthority(DERObjectIdentifier namingAuthorityId,
+ String namingAuthorityUrl, DirectoryString namingAuthorityText)
+ {
+ this.namingAuthorityId = new ASN1ObjectIdentifier(namingAuthorityId.getId());
+ this.namingAuthorityUrl = namingAuthorityUrl;
+ this.namingAuthorityText = namingAuthorityText;
+ }
+
+ /**
+ * Constructor from given details.
+ * <p/>
+ * All parameters can be combined.
+ *
+ * @param namingAuthorityId ObjectIdentifier for naming authority.
+ * @param namingAuthorityUrl URL for naming authority.
+ * @param namingAuthorityText Textual representation of naming authority.
+ */
+ public NamingAuthority(ASN1ObjectIdentifier namingAuthorityId,
+ String namingAuthorityUrl, DirectoryString namingAuthorityText)
+ {
+ this.namingAuthorityId = namingAuthorityId;
+ this.namingAuthorityUrl = namingAuthorityUrl;
+ this.namingAuthorityText = namingAuthorityText;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <p/>
+ * Returns:
+ * <p/>
+ * <pre>
+ * NamingAuthority ::= SEQUENCE
+ * {
+ * namingAuthorityId OBJECT IDENTIFIER OPTIONAL,
+ * namingAuthorityUrl IA5String OPTIONAL,
+ * namingAuthorityText DirectoryString(SIZE(1..128)) OPTIONAL
+ * }
+ * </pre>
+ *
+ * @return a DERObject
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+ if (namingAuthorityId != null)
+ {
+ vec.add(namingAuthorityId);
+ }
+ if (namingAuthorityUrl != null)
+ {
+ vec.add(new DERIA5String(namingAuthorityUrl, true));
+ }
+ if (namingAuthorityText != null)
+ {
+ vec.add(namingAuthorityText);
+ }
+ return new DERSequence(vec);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/ProcurationSyntax.java b/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/ProcurationSyntax.java
new file mode 100644
index 00000000..0a64f8e9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/ProcurationSyntax.java
@@ -0,0 +1,240 @@
+package org.bouncycastle.asn1.isismtt.x509;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERPrintableString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x500.DirectoryString;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.IssuerSerial;
+
+/**
+ * Attribute to indicate that the certificate holder may sign in the name of a
+ * third person.
+ * <p>
+ * ISIS-MTT PROFILE: The corresponding ProcurationSyntax contains either the
+ * name of the person who is represented (subcomponent thirdPerson) or a
+ * reference to his/her base certificate (in the component signingFor,
+ * subcomponent certRef), furthermore the optional components country and
+ * typeSubstitution to indicate the country whose laws apply, and respectively
+ * the type of procuration (e.g. manager, procuration, custody).
+ * <p>
+ * ISIS-MTT PROFILE: The GeneralName MUST be of type directoryName and MAY only
+ * contain: - RFC3039 attributes, except pseudonym (countryName, commonName,
+ * surname, givenName, serialNumber, organizationName, organizationalUnitName,
+ * stateOrProvincename, localityName, postalAddress) and - SubjectDirectoryName
+ * attributes (title, dateOfBirth, placeOfBirth, gender, countryOfCitizenship,
+ * countryOfResidence and NameAtBirth).
+ *
+ * <pre>
+ * ProcurationSyntax ::= SEQUENCE {
+ * country [1] EXPLICIT PrintableString(SIZE(2)) OPTIONAL,
+ * typeOfSubstitution [2] EXPLICIT DirectoryString (SIZE(1..128)) OPTIONAL,
+ * signingFor [3] EXPLICIT SigningFor
+ * }
+ *
+ * SigningFor ::= CHOICE
+ * {
+ * thirdPerson GeneralName,
+ * certRef IssuerSerial
+ * }
+ * </pre>
+ *
+ */
+public class ProcurationSyntax
+ extends ASN1Object
+{
+ private String country;
+ private DirectoryString typeOfSubstitution;
+
+ private GeneralName thirdPerson;
+ private IssuerSerial certRef;
+
+ public static ProcurationSyntax getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof ProcurationSyntax)
+ {
+ return (ProcurationSyntax)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new ProcurationSyntax((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: "
+ + obj.getClass().getName());
+ }
+
+ /**
+ * Constructor from ASN1Sequence.
+ * <p/>
+ * The sequence is of type ProcurationSyntax:
+ * <p/>
+ * <pre>
+ * ProcurationSyntax ::= SEQUENCE {
+ * country [1] EXPLICIT PrintableString(SIZE(2)) OPTIONAL,
+ * typeOfSubstitution [2] EXPLICIT DirectoryString (SIZE(1..128)) OPTIONAL,
+ * signingFor [3] EXPLICIT SigningFor
+ * }
+ * <p/>
+ * SigningFor ::= CHOICE
+ * {
+ * thirdPerson GeneralName,
+ * certRef IssuerSerial
+ * }
+ * </pre>
+ *
+ * @param seq The ASN.1 sequence.
+ */
+ private ProcurationSyntax(ASN1Sequence seq)
+ {
+ if (seq.size() < 1 || seq.size() > 3)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ ASN1TaggedObject o = ASN1TaggedObject.getInstance(e.nextElement());
+ switch (o.getTagNo())
+ {
+ case 1:
+ country = DERPrintableString.getInstance(o, true).getString();
+ break;
+ case 2:
+ typeOfSubstitution = DirectoryString.getInstance(o, true);
+ break;
+ case 3:
+ ASN1Encodable signingFor = o.getObject();
+ if (signingFor instanceof ASN1TaggedObject)
+ {
+ thirdPerson = GeneralName.getInstance(signingFor);
+ }
+ else
+ {
+ certRef = IssuerSerial.getInstance(signingFor);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Bad tag number: " + o.getTagNo());
+ }
+ }
+ }
+
+ /**
+ * Constructor from a given details.
+ * <p/>
+ * <p/>
+ * Either <code>generalName</code> or <code>certRef</code> MUST be
+ * <code>null</code>.
+ *
+ * @param country The country code whose laws apply.
+ * @param typeOfSubstitution The type of procuration.
+ * @param certRef Reference to certificate of the person who is represented.
+ */
+ public ProcurationSyntax(
+ String country,
+ DirectoryString typeOfSubstitution,
+ IssuerSerial certRef)
+ {
+ this.country = country;
+ this.typeOfSubstitution = typeOfSubstitution;
+ this.thirdPerson = null;
+ this.certRef = certRef;
+ }
+
+ /**
+ * Constructor from a given details.
+ * <p/>
+ * <p/>
+ * Either <code>generalName</code> or <code>certRef</code> MUST be
+ * <code>null</code>.
+ *
+ * @param country The country code whose laws apply.
+ * @param typeOfSubstitution The type of procuration.
+ * @param thirdPerson The GeneralName of the person who is represented.
+ */
+ public ProcurationSyntax(
+ String country,
+ DirectoryString typeOfSubstitution,
+ GeneralName thirdPerson)
+ {
+ this.country = country;
+ this.typeOfSubstitution = typeOfSubstitution;
+ this.thirdPerson = thirdPerson;
+ this.certRef = null;
+ }
+
+ public String getCountry()
+ {
+ return country;
+ }
+
+ public DirectoryString getTypeOfSubstitution()
+ {
+ return typeOfSubstitution;
+ }
+
+ public GeneralName getThirdPerson()
+ {
+ return thirdPerson;
+ }
+
+ public IssuerSerial getCertRef()
+ {
+ return certRef;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <p/>
+ * Returns:
+ * <p/>
+ * <pre>
+ * ProcurationSyntax ::= SEQUENCE {
+ * country [1] EXPLICIT PrintableString(SIZE(2)) OPTIONAL,
+ * typeOfSubstitution [2] EXPLICIT DirectoryString (SIZE(1..128)) OPTIONAL,
+ * signingFor [3] EXPLICIT SigningFor
+ * }
+ * <p/>
+ * SigningFor ::= CHOICE
+ * {
+ * thirdPerson GeneralName,
+ * certRef IssuerSerial
+ * }
+ * </pre>
+ *
+ * @return a DERObject
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+ if (country != null)
+ {
+ vec.add(new DERTaggedObject(true, 1, new DERPrintableString(country, true)));
+ }
+ if (typeOfSubstitution != null)
+ {
+ vec.add(new DERTaggedObject(true, 2, typeOfSubstitution));
+ }
+ if (thirdPerson != null)
+ {
+ vec.add(new DERTaggedObject(true, 3, thirdPerson));
+ }
+ else
+ {
+ vec.add(new DERTaggedObject(true, 3, certRef));
+ }
+
+ return new DERSequence(vec);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/ProfessionInfo.java b/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/ProfessionInfo.java
new file mode 100644
index 00000000..081d9af9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/ProfessionInfo.java
@@ -0,0 +1,408 @@
+package org.bouncycastle.asn1.isismtt.x509;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERPrintableString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x500.DirectoryString;
+
+/**
+ * Professions, specializations, disciplines, fields of activity, etc.
+ *
+ * <pre>
+ * ProfessionInfo ::= SEQUENCE
+ * {
+ * namingAuthority [0] EXPLICIT NamingAuthority OPTIONAL,
+ * professionItems SEQUENCE OF DirectoryString (SIZE(1..128)),
+ * professionOIDs SEQUENCE OF OBJECT IDENTIFIER OPTIONAL,
+ * registrationNumber PrintableString(SIZE(1..128)) OPTIONAL,
+ * addProfessionInfo OCTET STRING OPTIONAL
+ * }
+ * </pre>
+ *
+ * @see org.bouncycastle.asn1.isismtt.x509.AdmissionSyntax
+ */
+public class ProfessionInfo
+ extends ASN1Object
+{
+
+ /**
+ * Rechtsanw�ltin
+ */
+ public static final ASN1ObjectIdentifier Rechtsanwltin = new ASN1ObjectIdentifier(
+ NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".1");
+
+ /**
+ * Rechtsanwalt
+ */
+ public static final ASN1ObjectIdentifier Rechtsanwalt = new ASN1ObjectIdentifier(
+ NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".2");
+
+ /**
+ * Rechtsbeistand
+ */
+ public static final ASN1ObjectIdentifier Rechtsbeistand = new ASN1ObjectIdentifier(
+ NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".3");
+
+ /**
+ * Steuerberaterin
+ */
+ public static final ASN1ObjectIdentifier Steuerberaterin = new ASN1ObjectIdentifier(
+ NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".4");
+
+ /**
+ * Steuerberater
+ */
+ public static final ASN1ObjectIdentifier Steuerberater = new ASN1ObjectIdentifier(
+ NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".5");
+
+ /**
+ * Steuerbevollm�chtigte
+ */
+ public static final ASN1ObjectIdentifier Steuerbevollmchtigte = new ASN1ObjectIdentifier(
+ NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".6");
+
+ /**
+ * Steuerbevollm�chtigter
+ */
+ public static final ASN1ObjectIdentifier Steuerbevollmchtigter = new ASN1ObjectIdentifier(
+ NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".7");
+
+ /**
+ * Notarin
+ */
+ public static final ASN1ObjectIdentifier Notarin = new ASN1ObjectIdentifier(
+ NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".8");
+
+ /**
+ * Notar
+ */
+ public static final ASN1ObjectIdentifier Notar = new ASN1ObjectIdentifier(
+ NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".9");
+
+ /**
+ * Notarvertreterin
+ */
+ public static final ASN1ObjectIdentifier Notarvertreterin = new ASN1ObjectIdentifier(
+ NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".10");
+
+ /**
+ * Notarvertreter
+ */
+ public static final ASN1ObjectIdentifier Notarvertreter = new ASN1ObjectIdentifier(
+ NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".11");
+
+ /**
+ * Notariatsverwalterin
+ */
+ public static final ASN1ObjectIdentifier Notariatsverwalterin = new ASN1ObjectIdentifier(
+ NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".12");
+
+ /**
+ * Notariatsverwalter
+ */
+ public static final ASN1ObjectIdentifier Notariatsverwalter = new ASN1ObjectIdentifier(
+ NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".13");
+
+ /**
+ * Wirtschaftspr�ferin
+ */
+ public static final ASN1ObjectIdentifier Wirtschaftsprferin = new ASN1ObjectIdentifier(
+ NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".14");
+
+ /**
+ * Wirtschaftspr�fer
+ */
+ public static final ASN1ObjectIdentifier Wirtschaftsprfer = new ASN1ObjectIdentifier(
+ NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".15");
+
+ /**
+ * Vereidigte Buchpr�ferin
+ */
+ public static final ASN1ObjectIdentifier VereidigteBuchprferin = new ASN1ObjectIdentifier(
+ NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".16");
+
+ /**
+ * Vereidigter Buchpr�fer
+ */
+ public static final ASN1ObjectIdentifier VereidigterBuchprfer = new ASN1ObjectIdentifier(
+ NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".17");
+
+ /**
+ * Patentanw�ltin
+ */
+ public static final ASN1ObjectIdentifier Patentanwltin = new ASN1ObjectIdentifier(
+ NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".18");
+
+ /**
+ * Patentanwalt
+ */
+ public static final ASN1ObjectIdentifier Patentanwalt = new ASN1ObjectIdentifier(
+ NamingAuthority.id_isismtt_at_namingAuthorities_RechtWirtschaftSteuern + ".19");
+
+ private NamingAuthority namingAuthority;
+
+ private ASN1Sequence professionItems;
+
+ private ASN1Sequence professionOIDs;
+
+ private String registrationNumber;
+
+ private ASN1OctetString addProfessionInfo;
+
+ public static ProfessionInfo getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof ProfessionInfo)
+ {
+ return (ProfessionInfo)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new ProfessionInfo((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: "
+ + obj.getClass().getName());
+ }
+
+ /**
+ * Constructor from ASN1Sequence.
+ * <p/>
+ * <p/>
+ * <pre>
+ * ProfessionInfo ::= SEQUENCE
+ * {
+ * namingAuthority [0] EXPLICIT NamingAuthority OPTIONAL,
+ * professionItems SEQUENCE OF DirectoryString (SIZE(1..128)),
+ * professionOIDs SEQUENCE OF OBJECT IDENTIFIER OPTIONAL,
+ * registrationNumber PrintableString(SIZE(1..128)) OPTIONAL,
+ * addProfessionInfo OCTET STRING OPTIONAL
+ * }
+ * </pre>
+ *
+ * @param seq The ASN.1 sequence.
+ */
+ private ProfessionInfo(ASN1Sequence seq)
+ {
+ if (seq.size() > 5)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ Enumeration e = seq.getObjects();
+
+ ASN1Encodable o = (ASN1Encodable)e.nextElement();
+
+ if (o instanceof ASN1TaggedObject)
+ {
+ if (((ASN1TaggedObject)o).getTagNo() != 0)
+ {
+ throw new IllegalArgumentException("Bad tag number: "
+ + ((ASN1TaggedObject)o).getTagNo());
+ }
+ namingAuthority = NamingAuthority.getInstance((ASN1TaggedObject)o, true);
+ o = (ASN1Encodable)e.nextElement();
+ }
+
+ professionItems = ASN1Sequence.getInstance(o);
+
+ if (e.hasMoreElements())
+ {
+ o = (ASN1Encodable)e.nextElement();
+ if (o instanceof ASN1Sequence)
+ {
+ professionOIDs = ASN1Sequence.getInstance(o);
+ }
+ else if (o instanceof DERPrintableString)
+ {
+ registrationNumber = DERPrintableString.getInstance(o).getString();
+ }
+ else if (o instanceof ASN1OctetString)
+ {
+ addProfessionInfo = ASN1OctetString.getInstance(o);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Bad object encountered: "
+ + o.getClass());
+ }
+ }
+ if (e.hasMoreElements())
+ {
+ o = (ASN1Encodable)e.nextElement();
+ if (o instanceof DERPrintableString)
+ {
+ registrationNumber = DERPrintableString.getInstance(o).getString();
+ }
+ else if (o instanceof DEROctetString)
+ {
+ addProfessionInfo = (DEROctetString)o;
+ }
+ else
+ {
+ throw new IllegalArgumentException("Bad object encountered: "
+ + o.getClass());
+ }
+ }
+ if (e.hasMoreElements())
+ {
+ o = (ASN1Encodable)e.nextElement();
+ if (o instanceof DEROctetString)
+ {
+ addProfessionInfo = (DEROctetString)o;
+ }
+ else
+ {
+ throw new IllegalArgumentException("Bad object encountered: "
+ + o.getClass());
+ }
+ }
+
+ }
+
+ /**
+ * Constructor from given details.
+ * <p/>
+ * <code>professionItems</code> is mandatory, all other parameters are
+ * optional.
+ *
+ * @param namingAuthority The naming authority.
+ * @param professionItems Directory strings of the profession.
+ * @param professionOIDs DERObjectIdentfier objects for the
+ * profession.
+ * @param registrationNumber Registration number.
+ * @param addProfessionInfo Additional infos in encoded form.
+ */
+ public ProfessionInfo(NamingAuthority namingAuthority,
+ DirectoryString[] professionItems, ASN1ObjectIdentifier[] professionOIDs,
+ String registrationNumber, ASN1OctetString addProfessionInfo)
+ {
+ this.namingAuthority = namingAuthority;
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ for (int i = 0; i != professionItems.length; i++)
+ {
+ v.add(professionItems[i]);
+ }
+ this.professionItems = new DERSequence(v);
+ if (professionOIDs != null)
+ {
+ v = new ASN1EncodableVector();
+ for (int i = 0; i != professionOIDs.length; i++)
+ {
+ v.add(professionOIDs[i]);
+ }
+ this.professionOIDs = new DERSequence(v);
+ }
+ this.registrationNumber = registrationNumber;
+ this.addProfessionInfo = addProfessionInfo;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <p/>
+ * Returns:
+ * <p/>
+ * <pre>
+ * ProfessionInfo ::= SEQUENCE
+ * {
+ * namingAuthority [0] EXPLICIT NamingAuthority OPTIONAL,
+ * professionItems SEQUENCE OF DirectoryString (SIZE(1..128)),
+ * professionOIDs SEQUENCE OF OBJECT IDENTIFIER OPTIONAL,
+ * registrationNumber PrintableString(SIZE(1..128)) OPTIONAL,
+ * addProfessionInfo OCTET STRING OPTIONAL
+ * }
+ * </pre>
+ *
+ * @return a DERObject
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+ if (namingAuthority != null)
+ {
+ vec.add(new DERTaggedObject(true, 0, namingAuthority));
+ }
+ vec.add(professionItems);
+ if (professionOIDs != null)
+ {
+ vec.add(professionOIDs);
+ }
+ if (registrationNumber != null)
+ {
+ vec.add(new DERPrintableString(registrationNumber, true));
+ }
+ if (addProfessionInfo != null)
+ {
+ vec.add(addProfessionInfo);
+ }
+ return new DERSequence(vec);
+ }
+
+ /**
+ * @return Returns the addProfessionInfo.
+ */
+ public ASN1OctetString getAddProfessionInfo()
+ {
+ return addProfessionInfo;
+ }
+
+ /**
+ * @return Returns the namingAuthority.
+ */
+ public NamingAuthority getNamingAuthority()
+ {
+ return namingAuthority;
+ }
+
+ /**
+ * @return Returns the professionItems.
+ */
+ public DirectoryString[] getProfessionItems()
+ {
+ DirectoryString[] items = new DirectoryString[professionItems.size()];
+ int count = 0;
+ for (Enumeration e = professionItems.getObjects(); e.hasMoreElements();)
+ {
+ items[count++] = DirectoryString.getInstance(e.nextElement());
+ }
+ return items;
+ }
+
+ /**
+ * @return Returns the professionOIDs.
+ */
+ public ASN1ObjectIdentifier[] getProfessionOIDs()
+ {
+ if (professionOIDs == null)
+ {
+ return new ASN1ObjectIdentifier[0];
+ }
+ ASN1ObjectIdentifier[] oids = new ASN1ObjectIdentifier[professionOIDs.size()];
+ int count = 0;
+ for (Enumeration e = professionOIDs.getObjects(); e.hasMoreElements();)
+ {
+ oids[count++] = ASN1ObjectIdentifier.getInstance(e.nextElement());
+ }
+ return oids;
+ }
+
+ /**
+ * @return Returns the registrationNumber.
+ */
+ public String getRegistrationNumber()
+ {
+ return registrationNumber;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/Restriction.java b/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/Restriction.java
new file mode 100644
index 00000000..c2a2a413
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/isismtt/x509/Restriction.java
@@ -0,0 +1,81 @@
+package org.bouncycastle.asn1.isismtt.x509;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.x500.DirectoryString;
+
+/**
+ * Some other restriction regarding the usage of this certificate.
+ * <p/>
+ * <pre>
+ * RestrictionSyntax ::= DirectoryString (SIZE(1..1024))
+ * </pre>
+ */
+public class Restriction
+ extends ASN1Object
+{
+ private DirectoryString restriction;
+
+ public static Restriction getInstance(Object obj)
+ {
+ if (obj instanceof Restriction)
+ {
+ return (Restriction)obj;
+ }
+
+ if (obj != null)
+ {
+ return new Restriction(DirectoryString.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Constructor from DirectoryString.
+ * <p/>
+ * The DirectoryString is of type RestrictionSyntax:
+ * <p/>
+ * <pre>
+ * RestrictionSyntax ::= DirectoryString (SIZE(1..1024))
+ * </pre>
+ *
+ * @param restriction A DirectoryString.
+ */
+ private Restriction(DirectoryString restriction)
+ {
+ this.restriction = restriction;
+ }
+
+ /**
+ * Constructor from a given details.
+ *
+ * @param restriction The describtion of the restriction.
+ */
+ public Restriction(String restriction)
+ {
+ this.restriction = new DirectoryString(restriction);
+ }
+
+ public DirectoryString getRestriction()
+ {
+ return restriction;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <p/>
+ * Returns:
+ * <p/>
+ * <pre>
+ * RestrictionSyntax ::= DirectoryString (SIZE(1..1024))
+ * <p/>
+ * </pre>
+ *
+ * @return a DERObject
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return restriction.toASN1Primitive();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java
new file mode 100644
index 00000000..73e0c581
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.asn1.kisa;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface KISAObjectIdentifiers
+{
+ public static final ASN1ObjectIdentifier id_seedCBC = new ASN1ObjectIdentifier("1.2.410.200004.1.4");
+ public static final ASN1ObjectIdentifier id_npki_app_cmsSeed_wrap = new ASN1ObjectIdentifier("1.2.410.200004.7.1.1.1");
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/microsoft/MicrosoftObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/microsoft/MicrosoftObjectIdentifiers.java
new file mode 100644
index 00000000..f40a943b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/microsoft/MicrosoftObjectIdentifiers.java
@@ -0,0 +1,17 @@
+package org.bouncycastle.asn1.microsoft;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface MicrosoftObjectIdentifiers
+{
+ //
+ // Microsoft
+ // iso(1) identified-organization(3) dod(6) internet(1) private(4) enterprise(1) microsoft(311)
+ //
+ static final ASN1ObjectIdentifier microsoft = new ASN1ObjectIdentifier("1.3.6.1.4.1.311");
+ static final ASN1ObjectIdentifier microsoftCertTemplateV1 = microsoft.branch("20.2");
+ static final ASN1ObjectIdentifier microsoftCaVersion = microsoft.branch("21.1");
+ static final ASN1ObjectIdentifier microsoftPrevCaCertHash = microsoft.branch("21.2");
+ static final ASN1ObjectIdentifier microsoftCertTemplateV2 = microsoft.branch("21.7");
+ static final ASN1ObjectIdentifier microsoftAppPolicies = microsoft.branch("21.10");
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/misc/CAST5CBCParameters.java b/core/src/main/java/org/bouncycastle/asn1/misc/CAST5CBCParameters.java
new file mode 100644
index 00000000..715e4bb9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/misc/CAST5CBCParameters.java
@@ -0,0 +1,78 @@
+package org.bouncycastle.asn1.misc;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+
+public class CAST5CBCParameters
+ extends ASN1Object
+{
+ ASN1Integer keyLength;
+ ASN1OctetString iv;
+
+ public static CAST5CBCParameters getInstance(
+ Object o)
+ {
+ if (o instanceof CAST5CBCParameters)
+ {
+ return (CAST5CBCParameters)o;
+ }
+ else if (o != null)
+ {
+ return new CAST5CBCParameters(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public CAST5CBCParameters(
+ byte[] iv,
+ int keyLength)
+ {
+ this.iv = new DEROctetString(iv);
+ this.keyLength = new ASN1Integer(keyLength);
+ }
+
+ public CAST5CBCParameters(
+ ASN1Sequence seq)
+ {
+ iv = (ASN1OctetString)seq.getObjectAt(0);
+ keyLength = (ASN1Integer)seq.getObjectAt(1);
+ }
+
+ public byte[] getIV()
+ {
+ return iv.getOctets();
+ }
+
+ public int getKeyLength()
+ {
+ return keyLength.getValue().intValue();
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * cast5CBCParameters ::= SEQUENCE {
+ * iv OCTET STRING DEFAULT 0,
+ * -- Initialization vector
+ * keyLength INTEGER
+ * -- Key length, in bits
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(iv);
+ v.add(keyLength);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/misc/IDEACBCPar.java b/core/src/main/java/org/bouncycastle/asn1/misc/IDEACBCPar.java
new file mode 100644
index 00000000..35b0f244
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/misc/IDEACBCPar.java
@@ -0,0 +1,81 @@
+package org.bouncycastle.asn1.misc;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+
+public class IDEACBCPar
+ extends ASN1Object
+{
+ ASN1OctetString iv;
+
+ public static IDEACBCPar getInstance(
+ Object o)
+ {
+ if (o instanceof IDEACBCPar)
+ {
+ return (IDEACBCPar)o;
+ }
+ else if (o != null)
+ {
+ return new IDEACBCPar(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public IDEACBCPar(
+ byte[] iv)
+ {
+ this.iv = new DEROctetString(iv);
+ }
+
+ public IDEACBCPar(
+ ASN1Sequence seq)
+ {
+ if (seq.size() == 1)
+ {
+ iv = (ASN1OctetString)seq.getObjectAt(0);
+ }
+ else
+ {
+ iv = null;
+ }
+ }
+
+ public byte[] getIV()
+ {
+ if (iv != null)
+ {
+ return iv.getOctets();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * IDEA-CBCPar ::= SEQUENCE {
+ * iv OCTET STRING OPTIONAL -- exactly 8 octets
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (iv != null)
+ {
+ v.add(iv);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
new file mode 100644
index 00000000..debf2688
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
@@ -0,0 +1,47 @@
+package org.bouncycastle.asn1.misc;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface MiscObjectIdentifiers
+{
+ //
+ // Netscape
+ // iso/itu(2) joint-assign(16) us(840) uscompany(1) netscape(113730) cert-extensions(1) }
+ //
+ static final ASN1ObjectIdentifier netscape = new ASN1ObjectIdentifier("2.16.840.1.113730.1");
+ static final ASN1ObjectIdentifier netscapeCertType = netscape.branch("1");
+ static final ASN1ObjectIdentifier netscapeBaseURL = netscape.branch("2");
+ static final ASN1ObjectIdentifier netscapeRevocationURL = netscape.branch("3");
+ static final ASN1ObjectIdentifier netscapeCARevocationURL = netscape.branch("4");
+ static final ASN1ObjectIdentifier netscapeRenewalURL = netscape.branch("7");
+ static final ASN1ObjectIdentifier netscapeCApolicyURL = netscape.branch("8");
+ static final ASN1ObjectIdentifier netscapeSSLServerName = netscape.branch("12");
+ static final ASN1ObjectIdentifier netscapeCertComment = netscape.branch("13");
+
+ //
+ // Verisign
+ // iso/itu(2) joint-assign(16) us(840) uscompany(1) verisign(113733) cert-extensions(1) }
+ //
+ static final ASN1ObjectIdentifier verisign = new ASN1ObjectIdentifier("2.16.840.1.113733.1");
+
+ //
+ // CZAG - country, zip, age, and gender
+ //
+ static final ASN1ObjectIdentifier verisignCzagExtension = verisign.branch("6.3");
+ // D&B D-U-N-S number
+ static final ASN1ObjectIdentifier verisignDnbDunsNumber = verisign.branch("6.15");
+
+ //
+ // Novell
+ // iso/itu(2) country(16) us(840) organization(1) novell(113719)
+ //
+ static final ASN1ObjectIdentifier novell = new ASN1ObjectIdentifier("2.16.840.1.113719");
+ static final ASN1ObjectIdentifier novellSecurityAttribs = novell.branch("1.9.4.1");
+
+ //
+ // Entrust
+ // iso(1) member-body(16) us(840) nortelnetworks(113533) entrust(7)
+ //
+ static final ASN1ObjectIdentifier entrust = new ASN1ObjectIdentifier("1.2.840.113533.7");
+ static final ASN1ObjectIdentifier entrustVersionExtension = entrust.branch("65.0");
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/misc/NetscapeCertType.java b/core/src/main/java/org/bouncycastle/asn1/misc/NetscapeCertType.java
new file mode 100644
index 00000000..846a2057
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/misc/NetscapeCertType.java
@@ -0,0 +1,54 @@
+package org.bouncycastle.asn1.misc;
+
+import org.bouncycastle.asn1.DERBitString;
+
+/**
+ * The NetscapeCertType object.
+ * <pre>
+ * NetscapeCertType ::= BIT STRING {
+ * SSLClient (0),
+ * SSLServer (1),
+ * S/MIME (2),
+ * Object Signing (3),
+ * Reserved (4),
+ * SSL CA (5),
+ * S/MIME CA (6),
+ * Object Signing CA (7) }
+ * </pre>
+ */
+public class NetscapeCertType
+ extends DERBitString
+{
+ public static final int sslClient = (1 << 7);
+ public static final int sslServer = (1 << 6);
+ public static final int smime = (1 << 5);
+ public static final int objectSigning = (1 << 4);
+ public static final int reserved = (1 << 3);
+ public static final int sslCA = (1 << 2);
+ public static final int smimeCA = (1 << 1);
+ public static final int objectSigningCA = (1 << 0);
+
+ /**
+ * Basic constructor.
+ *
+ * @param usage - the bitwise OR of the Key Usage flags giving the
+ * allowed uses for the key.
+ * e.g. (X509NetscapeCertType.sslCA | X509NetscapeCertType.smimeCA)
+ */
+ public NetscapeCertType(
+ int usage)
+ {
+ super(getBytes(usage), getPadBits(usage));
+ }
+
+ public NetscapeCertType(
+ DERBitString usage)
+ {
+ super(usage.getBytes(), usage.getPadBits());
+ }
+
+ public String toString()
+ {
+ return "NetscapeCertType: 0x" + Integer.toHexString(data[0] & 0xff);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/misc/NetscapeRevocationURL.java b/core/src/main/java/org/bouncycastle/asn1/misc/NetscapeRevocationURL.java
new file mode 100644
index 00000000..c0347da0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/misc/NetscapeRevocationURL.java
@@ -0,0 +1,18 @@
+package org.bouncycastle.asn1.misc;
+
+import org.bouncycastle.asn1.DERIA5String;
+
+public class NetscapeRevocationURL
+ extends DERIA5String
+{
+ public NetscapeRevocationURL(
+ DERIA5String str)
+ {
+ super(str.getString());
+ }
+
+ public String toString()
+ {
+ return "NetscapeRevocationURL: " + this.getString();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/misc/VerisignCzagExtension.java b/core/src/main/java/org/bouncycastle/asn1/misc/VerisignCzagExtension.java
new file mode 100644
index 00000000..f09880ac
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/misc/VerisignCzagExtension.java
@@ -0,0 +1,18 @@
+package org.bouncycastle.asn1.misc;
+
+import org.bouncycastle.asn1.DERIA5String;
+
+public class VerisignCzagExtension
+ extends DERIA5String
+{
+ public VerisignCzagExtension(
+ DERIA5String str)
+ {
+ super(str.getString());
+ }
+
+ public String toString()
+ {
+ return "VerisignCzagExtension: " + this.getString();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/mozilla/PublicKeyAndChallenge.java b/core/src/main/java/org/bouncycastle/asn1/mozilla/PublicKeyAndChallenge.java
new file mode 100644
index 00000000..b4e44bf5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/mozilla/PublicKeyAndChallenge.java
@@ -0,0 +1,63 @@
+package org.bouncycastle.asn1.mozilla;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+
+/**
+ * This is designed to parse
+ * the PublicKeyAndChallenge created by the KEYGEN tag included by
+ * Mozilla based browsers.
+ * <pre>
+ * PublicKeyAndChallenge ::= SEQUENCE {
+ * spki SubjectPublicKeyInfo,
+ * challenge IA5STRING
+ * }
+ *
+ * </pre>
+ */
+public class PublicKeyAndChallenge
+ extends ASN1Object
+{
+ private ASN1Sequence pkacSeq;
+ private SubjectPublicKeyInfo spki;
+ private DERIA5String challenge;
+
+ public static PublicKeyAndChallenge getInstance(Object obj)
+ {
+ if (obj instanceof PublicKeyAndChallenge)
+ {
+ return (PublicKeyAndChallenge)obj;
+ }
+ else if (obj != null)
+ {
+ return new PublicKeyAndChallenge(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private PublicKeyAndChallenge(ASN1Sequence seq)
+ {
+ pkacSeq = seq;
+ spki = SubjectPublicKeyInfo.getInstance(seq.getObjectAt(0));
+ challenge = DERIA5String.getInstance(seq.getObjectAt(1));
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return pkacSeq;
+ }
+
+ public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
+ {
+ return spki;
+ }
+
+ public DERIA5String getChallenge()
+ {
+ return challenge;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/nist/NISTNamedCurves.java b/core/src/main/java/org/bouncycastle/asn1/nist/NISTNamedCurves.java
new file mode 100644
index 00000000..ba7e5187
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/nist/NISTNamedCurves.java
@@ -0,0 +1,99 @@
+package org.bouncycastle.asn1.nist;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.sec.SECNamedCurves;
+import org.bouncycastle.asn1.sec.SECObjectIdentifiers;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.util.Strings;
+
+/**
+ * Utility class for fetching curves using their NIST names as published in FIPS-PUB 186-3
+ */
+public class NISTNamedCurves
+{
+ static final Hashtable objIds = new Hashtable();
+ static final Hashtable names = new Hashtable();
+
+ static void defineCurve(String name, ASN1ObjectIdentifier oid)
+ {
+ objIds.put(name, oid);
+ names.put(oid, name);
+ }
+
+ static
+ {
+ defineCurve("B-571", SECObjectIdentifiers.sect571r1);
+ defineCurve("B-409", SECObjectIdentifiers.sect409r1);
+ defineCurve("B-283", SECObjectIdentifiers.sect283r1);
+ defineCurve("B-233", SECObjectIdentifiers.sect233r1);
+ defineCurve("B-163", SECObjectIdentifiers.sect163r2);
+ defineCurve("K-571", SECObjectIdentifiers.sect571k1);
+ defineCurve("K-409", SECObjectIdentifiers.sect409k1);
+ defineCurve("K-283", SECObjectIdentifiers.sect283k1);
+ defineCurve("K-233", SECObjectIdentifiers.sect233k1);
+ defineCurve("K-163", SECObjectIdentifiers.sect163k1);
+ defineCurve("P-521", SECObjectIdentifiers.secp521r1);
+ defineCurve("P-384", SECObjectIdentifiers.secp384r1);
+ defineCurve("P-256", SECObjectIdentifiers.secp256r1);
+ defineCurve("P-224", SECObjectIdentifiers.secp224r1);
+ defineCurve("P-192", SECObjectIdentifiers.secp192r1);
+ }
+
+ public static X9ECParameters getByName(
+ String name)
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)objIds.get(Strings.toUpperCase(name));
+
+ if (oid != null)
+ {
+ return getByOID(oid);
+ }
+
+ return null;
+ }
+
+ /**
+ * return the X9ECParameters object for the named curve represented by
+ * the passed in object identifier. Null if the curve isn't present.
+ *
+ * @param oid an object identifier representing a named curve, if present.
+ */
+ public static X9ECParameters getByOID(
+ ASN1ObjectIdentifier oid)
+ {
+ return SECNamedCurves.getByOID(oid);
+ }
+
+ /**
+ * return the object identifier signified by the passed in name. Null
+ * if there is no object identifier associated with name.
+ *
+ * @return the object identifier associated with name, if present.
+ */
+ public static ASN1ObjectIdentifier getOID(
+ String name)
+ {
+ return (ASN1ObjectIdentifier)objIds.get(Strings.toUpperCase(name));
+ }
+
+ /**
+ * return the named curve name represented by the given object identifier.
+ */
+ public static String getName(
+ ASN1ObjectIdentifier oid)
+ {
+ return (String)names.get(oid);
+ }
+
+ /**
+ * returns an enumeration containing the name strings for curves
+ * contained in this structure.
+ */
+ public static Enumeration getNames()
+ {
+ return objIds.keys();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java
new file mode 100644
index 00000000..afa93c4c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java
@@ -0,0 +1,60 @@
+package org.bouncycastle.asn1.nist;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface NISTObjectIdentifiers
+{
+ //
+ // NIST
+ // iso/itu(2) joint-assign(16) us(840) organization(1) gov(101) csor(3)
+
+ //
+ // nistalgorithms(4)
+ //
+ static final ASN1ObjectIdentifier nistAlgorithm = new ASN1ObjectIdentifier("2.16.840.1.101.3.4");
+
+ static final ASN1ObjectIdentifier hashAlgs = nistAlgorithm.branch("2");
+
+ static final ASN1ObjectIdentifier id_sha256 = hashAlgs.branch("1");
+ static final ASN1ObjectIdentifier id_sha384 = hashAlgs.branch("2");
+ static final ASN1ObjectIdentifier id_sha512 = hashAlgs.branch("3");
+ static final ASN1ObjectIdentifier id_sha224 = hashAlgs.branch("4");
+ static final ASN1ObjectIdentifier id_sha512_224 = hashAlgs.branch("5");
+ static final ASN1ObjectIdentifier id_sha512_256 = hashAlgs.branch("6");
+
+ static final ASN1ObjectIdentifier aes = nistAlgorithm.branch("1");
+
+ static final ASN1ObjectIdentifier id_aes128_ECB = aes.branch("1");
+ static final ASN1ObjectIdentifier id_aes128_CBC = aes.branch("2");
+ static final ASN1ObjectIdentifier id_aes128_OFB = aes.branch("3");
+ static final ASN1ObjectIdentifier id_aes128_CFB = aes.branch("4");
+ static final ASN1ObjectIdentifier id_aes128_wrap = aes.branch("5");
+ static final ASN1ObjectIdentifier id_aes128_GCM = aes.branch("6");
+ static final ASN1ObjectIdentifier id_aes128_CCM = aes.branch("7");
+
+ static final ASN1ObjectIdentifier id_aes192_ECB = aes.branch("21");
+ static final ASN1ObjectIdentifier id_aes192_CBC = aes.branch("22");
+ static final ASN1ObjectIdentifier id_aes192_OFB = aes.branch("23");
+ static final ASN1ObjectIdentifier id_aes192_CFB = aes.branch("24");
+ static final ASN1ObjectIdentifier id_aes192_wrap = aes.branch("25");
+ static final ASN1ObjectIdentifier id_aes192_GCM = aes.branch("26");
+ static final ASN1ObjectIdentifier id_aes192_CCM = aes.branch("27");
+
+ static final ASN1ObjectIdentifier id_aes256_ECB = aes.branch("41");
+ static final ASN1ObjectIdentifier id_aes256_CBC = aes.branch("42");
+ static final ASN1ObjectIdentifier id_aes256_OFB = aes.branch("43");
+ static final ASN1ObjectIdentifier id_aes256_CFB = aes.branch("44");
+ static final ASN1ObjectIdentifier id_aes256_wrap = aes.branch("45");
+ static final ASN1ObjectIdentifier id_aes256_GCM = aes.branch("46");
+ static final ASN1ObjectIdentifier id_aes256_CCM = aes.branch("47");
+
+ //
+ // signatures
+ //
+ static final ASN1ObjectIdentifier id_dsa_with_sha2 = nistAlgorithm.branch("3");
+
+ static final ASN1ObjectIdentifier dsa_with_sha224 = id_dsa_with_sha2.branch("1");
+ static final ASN1ObjectIdentifier dsa_with_sha256 = id_dsa_with_sha2.branch("2");
+ static final ASN1ObjectIdentifier dsa_with_sha384 = id_dsa_with_sha2.branch("3");
+ static final ASN1ObjectIdentifier dsa_with_sha512 = id_dsa_with_sha2.branch("4");
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ntt/NTTObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/ntt/NTTObjectIdentifiers.java
new file mode 100644
index 00000000..2e4132ad
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ntt/NTTObjectIdentifiers.java
@@ -0,0 +1,17 @@
+package org.bouncycastle.asn1.ntt;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+/**
+ * From RFC 3657
+ */
+public interface NTTObjectIdentifiers
+{
+ public static final ASN1ObjectIdentifier id_camellia128_cbc = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.1.2");
+ public static final ASN1ObjectIdentifier id_camellia192_cbc = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.1.3");
+ public static final ASN1ObjectIdentifier id_camellia256_cbc = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.1.4");
+
+ public static final ASN1ObjectIdentifier id_camellia128_wrap = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.3.2");
+ public static final ASN1ObjectIdentifier id_camellia192_wrap = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.3.3");
+ public static final ASN1ObjectIdentifier id_camellia256_wrap = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.3.4");
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ocsp/BasicOCSPResponse.java b/core/src/main/java/org/bouncycastle/asn1/ocsp/BasicOCSPResponse.java
new file mode 100644
index 00000000..1b2e7f53
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ocsp/BasicOCSPResponse.java
@@ -0,0 +1,112 @@
+package org.bouncycastle.asn1.ocsp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class BasicOCSPResponse
+ extends ASN1Object
+{
+ private ResponseData tbsResponseData;
+ private AlgorithmIdentifier signatureAlgorithm;
+ private DERBitString signature;
+ private ASN1Sequence certs;
+
+ public BasicOCSPResponse(
+ ResponseData tbsResponseData,
+ AlgorithmIdentifier signatureAlgorithm,
+ DERBitString signature,
+ ASN1Sequence certs)
+ {
+ this.tbsResponseData = tbsResponseData;
+ this.signatureAlgorithm = signatureAlgorithm;
+ this.signature = signature;
+ this.certs = certs;
+ }
+
+ private BasicOCSPResponse(
+ ASN1Sequence seq)
+ {
+ this.tbsResponseData = ResponseData.getInstance(seq.getObjectAt(0));
+ this.signatureAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
+ this.signature = (DERBitString)seq.getObjectAt(2);
+
+ if (seq.size() > 3)
+ {
+ this.certs = ASN1Sequence.getInstance((ASN1TaggedObject)seq.getObjectAt(3), true);
+ }
+ }
+
+ public static BasicOCSPResponse getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static BasicOCSPResponse getInstance(
+ Object obj)
+ {
+ if (obj instanceof BasicOCSPResponse)
+ {
+ return (BasicOCSPResponse)obj;
+ }
+ else if (obj != null)
+ {
+ return new BasicOCSPResponse(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public ResponseData getTbsResponseData()
+ {
+ return tbsResponseData;
+ }
+
+ public AlgorithmIdentifier getSignatureAlgorithm()
+ {
+ return signatureAlgorithm;
+ }
+
+ public DERBitString getSignature()
+ {
+ return signature;
+ }
+
+ public ASN1Sequence getCerts()
+ {
+ return certs;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * BasicOCSPResponse ::= SEQUENCE {
+ * tbsResponseData ResponseData,
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signature BIT STRING,
+ * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(tbsResponseData);
+ v.add(signatureAlgorithm);
+ v.add(signature);
+ if (certs != null)
+ {
+ v.add(new DERTaggedObject(true, 0, certs));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ocsp/CertID.java b/core/src/main/java/org/bouncycastle/asn1/ocsp/CertID.java
new file mode 100644
index 00000000..9d3496ef
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ocsp/CertID.java
@@ -0,0 +1,105 @@
+package org.bouncycastle.asn1.ocsp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class CertID
+ extends ASN1Object
+{
+ AlgorithmIdentifier hashAlgorithm;
+ ASN1OctetString issuerNameHash;
+ ASN1OctetString issuerKeyHash;
+ ASN1Integer serialNumber;
+
+ public CertID(
+ AlgorithmIdentifier hashAlgorithm,
+ ASN1OctetString issuerNameHash,
+ ASN1OctetString issuerKeyHash,
+ ASN1Integer serialNumber)
+ {
+ this.hashAlgorithm = hashAlgorithm;
+ this.issuerNameHash = issuerNameHash;
+ this.issuerKeyHash = issuerKeyHash;
+ this.serialNumber = serialNumber;
+ }
+
+ private CertID(
+ ASN1Sequence seq)
+ {
+ hashAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(0));
+ issuerNameHash = (ASN1OctetString)seq.getObjectAt(1);
+ issuerKeyHash = (ASN1OctetString)seq.getObjectAt(2);
+ serialNumber = (ASN1Integer)seq.getObjectAt(3);
+ }
+
+ public static CertID getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static CertID getInstance(
+ Object obj)
+ {
+ if (obj instanceof CertID)
+ {
+ return (CertID)obj;
+ }
+ else if (obj != null)
+ {
+ return new CertID(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public AlgorithmIdentifier getHashAlgorithm()
+ {
+ return hashAlgorithm;
+ }
+
+ public ASN1OctetString getIssuerNameHash()
+ {
+ return issuerNameHash;
+ }
+
+ public ASN1OctetString getIssuerKeyHash()
+ {
+ return issuerKeyHash;
+ }
+
+ public ASN1Integer getSerialNumber()
+ {
+ return serialNumber;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * CertID ::= SEQUENCE {
+ * hashAlgorithm AlgorithmIdentifier,
+ * issuerNameHash OCTET STRING, -- Hash of Issuer's DN
+ * issuerKeyHash OCTET STRING, -- Hash of Issuers public key
+ * serialNumber CertificateSerialNumber }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(hashAlgorithm);
+ v.add(issuerNameHash);
+ v.add(issuerKeyHash);
+ v.add(serialNumber);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ocsp/CertStatus.java b/core/src/main/java/org/bouncycastle/asn1/ocsp/CertStatus.java
new file mode 100644
index 00000000..af530ae5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ocsp/CertStatus.java
@@ -0,0 +1,105 @@
+package org.bouncycastle.asn1.ocsp;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class CertStatus
+ extends ASN1Object
+ implements ASN1Choice
+{
+ private int tagNo;
+ private ASN1Encodable value;
+
+ /**
+ * create a CertStatus object with a tag of zero.
+ */
+ public CertStatus()
+ {
+ tagNo = 0;
+ value = DERNull.INSTANCE;
+ }
+
+ public CertStatus(
+ RevokedInfo info)
+ {
+ tagNo = 1;
+ value = info;
+ }
+
+ public CertStatus(
+ int tagNo,
+ ASN1Encodable value)
+ {
+ this.tagNo = tagNo;
+ this.value = value;
+ }
+
+ public CertStatus(
+ ASN1TaggedObject choice)
+ {
+ this.tagNo = choice.getTagNo();
+
+ switch (choice.getTagNo())
+ {
+ case 0:
+ value = DERNull.INSTANCE;
+ break;
+ case 1:
+ value = RevokedInfo.getInstance(choice, false);
+ break;
+ case 2:
+ value = DERNull.INSTANCE;
+ }
+ }
+
+ public static CertStatus getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof CertStatus)
+ {
+ return (CertStatus)obj;
+ }
+ else if (obj instanceof ASN1TaggedObject)
+ {
+ return new CertStatus((ASN1TaggedObject)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
+ }
+
+ public static CertStatus getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject()); // must be explicitly tagged
+ }
+
+ public int getTagNo()
+ {
+ return tagNo;
+ }
+
+ public ASN1Encodable getStatus()
+ {
+ return value;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * CertStatus ::= CHOICE {
+ * good [0] IMPLICIT NULL,
+ * revoked [1] IMPLICIT RevokedInfo,
+ * unknown [2] IMPLICIT UnknownInfo }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return new DERTaggedObject(false, tagNo, value);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ocsp/CrlID.java b/core/src/main/java/org/bouncycastle/asn1/ocsp/CrlID.java
new file mode 100644
index 00000000..e14fe294
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ocsp/CrlID.java
@@ -0,0 +1,111 @@
+package org.bouncycastle.asn1.ocsp;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERGeneralizedTime;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class CrlID
+ extends ASN1Object
+{
+ private DERIA5String crlUrl;
+ private ASN1Integer crlNum;
+ private ASN1GeneralizedTime crlTime;
+
+ private CrlID(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ ASN1TaggedObject o = (ASN1TaggedObject)e.nextElement();
+
+ switch (o.getTagNo())
+ {
+ case 0:
+ crlUrl = DERIA5String.getInstance(o, true);
+ break;
+ case 1:
+ crlNum = ASN1Integer.getInstance(o, true);
+ break;
+ case 2:
+ crlTime = DERGeneralizedTime.getInstance(o, true);
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "unknown tag number: " + o.getTagNo());
+ }
+ }
+ }
+
+ public static CrlID getInstance(
+ Object obj)
+ {
+ if (obj instanceof CrlID)
+ {
+ return (CrlID)obj;
+ }
+ else if (obj != null)
+ {
+ return new CrlID(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public DERIA5String getCrlUrl()
+ {
+ return crlUrl;
+ }
+
+ public ASN1Integer getCrlNum()
+ {
+ return crlNum;
+ }
+
+ public ASN1GeneralizedTime getCrlTime()
+ {
+ return crlTime;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * CrlID ::= SEQUENCE {
+ * crlUrl [0] EXPLICIT IA5String OPTIONAL,
+ * crlNum [1] EXPLICIT INTEGER OPTIONAL,
+ * crlTime [2] EXPLICIT GeneralizedTime OPTIONAL }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (crlUrl != null)
+ {
+ v.add(new DERTaggedObject(true, 0, crlUrl));
+ }
+
+ if (crlNum != null)
+ {
+ v.add(new DERTaggedObject(true, 1, crlNum));
+ }
+
+ if (crlTime != null)
+ {
+ v.add(new DERTaggedObject(true, 2, crlTime));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ocsp/OCSPObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/ocsp/OCSPObjectIdentifiers.java
new file mode 100644
index 00000000..40b15e96
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ocsp/OCSPObjectIdentifiers.java
@@ -0,0 +1,22 @@
+package org.bouncycastle.asn1.ocsp;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface OCSPObjectIdentifiers
+{
+ public static final String pkix_ocsp = "1.3.6.1.5.5.7.48.1";
+
+ public static final ASN1ObjectIdentifier id_pkix_ocsp = new ASN1ObjectIdentifier(pkix_ocsp);
+ public static final ASN1ObjectIdentifier id_pkix_ocsp_basic = new ASN1ObjectIdentifier(pkix_ocsp + ".1");
+
+ //
+ // extensions
+ //
+ public static final ASN1ObjectIdentifier id_pkix_ocsp_nonce = new ASN1ObjectIdentifier(pkix_ocsp + ".2");
+ public static final ASN1ObjectIdentifier id_pkix_ocsp_crl = new ASN1ObjectIdentifier(pkix_ocsp + ".3");
+
+ public static final ASN1ObjectIdentifier id_pkix_ocsp_response = new ASN1ObjectIdentifier(pkix_ocsp + ".4");
+ public static final ASN1ObjectIdentifier id_pkix_ocsp_nocheck = new ASN1ObjectIdentifier(pkix_ocsp + ".5");
+ public static final ASN1ObjectIdentifier id_pkix_ocsp_archive_cutoff = new ASN1ObjectIdentifier(pkix_ocsp + ".6");
+ public static final ASN1ObjectIdentifier id_pkix_ocsp_service_locator = new ASN1ObjectIdentifier(pkix_ocsp + ".7");
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ocsp/OCSPRequest.java b/core/src/main/java/org/bouncycastle/asn1/ocsp/OCSPRequest.java
new file mode 100644
index 00000000..559cf4ce
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ocsp/OCSPRequest.java
@@ -0,0 +1,90 @@
+package org.bouncycastle.asn1.ocsp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class OCSPRequest
+ extends ASN1Object
+{
+ TBSRequest tbsRequest;
+ Signature optionalSignature;
+
+ public OCSPRequest(
+ TBSRequest tbsRequest,
+ Signature optionalSignature)
+ {
+ this.tbsRequest = tbsRequest;
+ this.optionalSignature = optionalSignature;
+ }
+
+ private OCSPRequest(
+ ASN1Sequence seq)
+ {
+ tbsRequest = TBSRequest.getInstance(seq.getObjectAt(0));
+
+ if (seq.size() == 2)
+ {
+ optionalSignature = Signature.getInstance(
+ (ASN1TaggedObject)seq.getObjectAt(1), true);
+ }
+ }
+
+ public static OCSPRequest getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static OCSPRequest getInstance(
+ Object obj)
+ {
+ if (obj instanceof OCSPRequest)
+ {
+ return (OCSPRequest)obj;
+ }
+ else if (obj != null)
+ {
+ return new OCSPRequest(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public TBSRequest getTbsRequest()
+ {
+ return tbsRequest;
+ }
+
+ public Signature getOptionalSignature()
+ {
+ return optionalSignature;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * OCSPRequest ::= SEQUENCE {
+ * tbsRequest TBSRequest,
+ * optionalSignature [0] EXPLICIT Signature OPTIONAL }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(tbsRequest);
+
+ if (optionalSignature != null)
+ {
+ v.add(new DERTaggedObject(true, 0, optionalSignature));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ocsp/OCSPResponse.java b/core/src/main/java/org/bouncycastle/asn1/ocsp/OCSPResponse.java
new file mode 100644
index 00000000..31602daf
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ocsp/OCSPResponse.java
@@ -0,0 +1,90 @@
+package org.bouncycastle.asn1.ocsp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class OCSPResponse
+ extends ASN1Object
+{
+ OCSPResponseStatus responseStatus;
+ ResponseBytes responseBytes;
+
+ public OCSPResponse(
+ OCSPResponseStatus responseStatus,
+ ResponseBytes responseBytes)
+ {
+ this.responseStatus = responseStatus;
+ this.responseBytes = responseBytes;
+ }
+
+ private OCSPResponse(
+ ASN1Sequence seq)
+ {
+ responseStatus = OCSPResponseStatus.getInstance(seq.getObjectAt(0));
+
+ if (seq.size() == 2)
+ {
+ responseBytes = ResponseBytes.getInstance(
+ (ASN1TaggedObject)seq.getObjectAt(1), true);
+ }
+ }
+
+ public static OCSPResponse getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static OCSPResponse getInstance(
+ Object obj)
+ {
+ if (obj instanceof OCSPResponse)
+ {
+ return (OCSPResponse)obj;
+ }
+ else if (obj != null)
+ {
+ return new OCSPResponse(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public OCSPResponseStatus getResponseStatus()
+ {
+ return responseStatus;
+ }
+
+ public ResponseBytes getResponseBytes()
+ {
+ return responseBytes;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * OCSPResponse ::= SEQUENCE {
+ * responseStatus OCSPResponseStatus,
+ * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(responseStatus);
+
+ if (responseBytes != null)
+ {
+ v.add(new DERTaggedObject(true, 0, responseBytes));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ocsp/OCSPResponseStatus.java b/core/src/main/java/org/bouncycastle/asn1/ocsp/OCSPResponseStatus.java
new file mode 100644
index 00000000..aa225f93
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ocsp/OCSPResponseStatus.java
@@ -0,0 +1,71 @@
+package org.bouncycastle.asn1.ocsp;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1Enumerated;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+
+public class OCSPResponseStatus
+ extends ASN1Object
+{
+ public static final int SUCCESSFUL = 0;
+ public static final int MALFORMED_REQUEST = 1;
+ public static final int INTERNAL_ERROR = 2;
+ public static final int TRY_LATER = 3;
+ public static final int SIG_REQUIRED = 5;
+ public static final int UNAUTHORIZED = 6;
+
+ private ASN1Enumerated value;
+
+ /**
+ * The OCSPResponseStatus enumeration.
+ * <pre>
+ * OCSPResponseStatus ::= ENUMERATED {
+ * successful (0), --Response has valid confirmations
+ * malformedRequest (1), --Illegal confirmation request
+ * internalError (2), --Internal error in issuer
+ * tryLater (3), --Try again later
+ * --(4) is not used
+ * sigRequired (5), --Must sign the request
+ * unauthorized (6) --Request unauthorized
+ * }
+ * </pre>
+ */
+ public OCSPResponseStatus(
+ int value)
+ {
+ this(new ASN1Enumerated(value));
+ }
+
+ private OCSPResponseStatus(
+ ASN1Enumerated value)
+ {
+ this.value = value;
+ }
+
+ public static OCSPResponseStatus getInstance(
+ Object obj)
+ {
+ if (obj instanceof OCSPResponseStatus)
+ {
+ return (OCSPResponseStatus)obj;
+ }
+ else if (obj != null)
+ {
+ return new OCSPResponseStatus(ASN1Enumerated.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public BigInteger getValue()
+ {
+ return value.getValue();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return value;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ocsp/Request.java b/core/src/main/java/org/bouncycastle/asn1/ocsp/Request.java
new file mode 100644
index 00000000..236bc72f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ocsp/Request.java
@@ -0,0 +1,91 @@
+package org.bouncycastle.asn1.ocsp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.Extensions;
+
+public class Request
+ extends ASN1Object
+{
+ CertID reqCert;
+ Extensions singleRequestExtensions;
+
+ public Request(
+ CertID reqCert,
+ Extensions singleRequestExtensions)
+ {
+ this.reqCert = reqCert;
+ this.singleRequestExtensions = singleRequestExtensions;
+ }
+
+ private Request(
+ ASN1Sequence seq)
+ {
+ reqCert = CertID.getInstance(seq.getObjectAt(0));
+
+ if (seq.size() == 2)
+ {
+ singleRequestExtensions = Extensions.getInstance(
+ (ASN1TaggedObject)seq.getObjectAt(1), true);
+ }
+ }
+
+ public static Request getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static Request getInstance(
+ Object obj)
+ {
+ if (obj instanceof Request)
+ {
+ return (Request)obj;
+ }
+ else if (obj != null)
+ {
+ return new Request(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public CertID getReqCert()
+ {
+ return reqCert;
+ }
+
+ public Extensions getSingleRequestExtensions()
+ {
+ return singleRequestExtensions;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * Request ::= SEQUENCE {
+ * reqCert CertID,
+ * singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(reqCert);
+
+ if (singleRequestExtensions != null)
+ {
+ v.add(new DERTaggedObject(true, 0, singleRequestExtensions));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ocsp/ResponderID.java b/core/src/main/java/org/bouncycastle/asn1/ocsp/ResponderID.java
new file mode 100644
index 00000000..97190472
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ocsp/ResponderID.java
@@ -0,0 +1,104 @@
+package org.bouncycastle.asn1.ocsp;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x500.X500Name;
+
+public class ResponderID
+ extends ASN1Object
+ implements ASN1Choice
+{
+ private ASN1Encodable value;
+
+ public ResponderID(
+ ASN1OctetString value)
+ {
+ this.value = value;
+ }
+
+ public ResponderID(
+ X500Name value)
+ {
+ this.value = value;
+ }
+
+ public static ResponderID getInstance(
+ Object obj)
+ {
+ if (obj instanceof ResponderID)
+ {
+ return (ResponderID)obj;
+ }
+ else if (obj instanceof DEROctetString)
+ {
+ return new ResponderID((DEROctetString)obj);
+ }
+ else if (obj instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject o = (ASN1TaggedObject)obj;
+
+ if (o.getTagNo() == 1)
+ {
+ return new ResponderID(X500Name.getInstance(o, true));
+ }
+ else
+ {
+ return new ResponderID(ASN1OctetString.getInstance(o, true));
+ }
+ }
+
+ return new ResponderID(X500Name.getInstance(obj));
+ }
+
+ public static ResponderID getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject()); // must be explicitly tagged
+ }
+
+ public byte[] getKeyHash()
+ {
+ if (this.value instanceof ASN1OctetString)
+ {
+ ASN1OctetString octetString = (ASN1OctetString)this.value;
+ return octetString.getOctets();
+ }
+
+ return null;
+ }
+
+ public X500Name getName()
+ {
+ if (this.value instanceof ASN1OctetString)
+ {
+ return null;
+ }
+
+ return X500Name.getInstance(value);
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * ResponderID ::= CHOICE {
+ * byName [1] Name,
+ * byKey [2] KeyHash }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ if (value instanceof ASN1OctetString)
+ {
+ return new DERTaggedObject(true, 2, value);
+ }
+
+ return new DERTaggedObject(true, 1, value);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ocsp/ResponseBytes.java b/core/src/main/java/org/bouncycastle/asn1/ocsp/ResponseBytes.java
new file mode 100644
index 00000000..074294c6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ocsp/ResponseBytes.java
@@ -0,0 +1,82 @@
+package org.bouncycastle.asn1.ocsp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+public class ResponseBytes
+ extends ASN1Object
+{
+ ASN1ObjectIdentifier responseType;
+ ASN1OctetString response;
+
+ public ResponseBytes(
+ ASN1ObjectIdentifier responseType,
+ ASN1OctetString response)
+ {
+ this.responseType = responseType;
+ this.response = response;
+ }
+
+ public ResponseBytes(
+ ASN1Sequence seq)
+ {
+ responseType = (ASN1ObjectIdentifier)seq.getObjectAt(0);
+ response = (ASN1OctetString)seq.getObjectAt(1);
+ }
+
+ public static ResponseBytes getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static ResponseBytes getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof ResponseBytes)
+ {
+ return (ResponseBytes)obj;
+ }
+ else if (obj instanceof ASN1Sequence)
+ {
+ return new ResponseBytes((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
+ }
+
+ public ASN1ObjectIdentifier getResponseType()
+ {
+ return responseType;
+ }
+
+ public ASN1OctetString getResponse()
+ {
+ return response;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * ResponseBytes ::= SEQUENCE {
+ * responseType OBJECT IDENTIFIER,
+ * response OCTET STRING }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(responseType);
+ v.add(response);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ocsp/ResponseData.java b/core/src/main/java/org/bouncycastle/asn1/ocsp/ResponseData.java
new file mode 100644
index 00000000..e2a9f955
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ocsp/ResponseData.java
@@ -0,0 +1,182 @@
+package org.bouncycastle.asn1.ocsp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERGeneralizedTime;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.X509Extensions;
+
+public class ResponseData
+ extends ASN1Object
+{
+ private static final ASN1Integer V1 = new ASN1Integer(0);
+
+ private boolean versionPresent;
+
+ private ASN1Integer version;
+ private ResponderID responderID;
+ private ASN1GeneralizedTime producedAt;
+ private ASN1Sequence responses;
+ private Extensions responseExtensions;
+
+ public ResponseData(
+ ASN1Integer version,
+ ResponderID responderID,
+ ASN1GeneralizedTime producedAt,
+ ASN1Sequence responses,
+ Extensions responseExtensions)
+ {
+ this.version = version;
+ this.responderID = responderID;
+ this.producedAt = producedAt;
+ this.responses = responses;
+ this.responseExtensions = responseExtensions;
+ }
+
+ /**
+ * @deprecated use method taking Extensions
+ * @param responderID
+ * @param producedAt
+ * @param responses
+ * @param responseExtensions
+ */
+ public ResponseData(
+ ResponderID responderID,
+ DERGeneralizedTime producedAt,
+ ASN1Sequence responses,
+ X509Extensions responseExtensions)
+ {
+ this(V1, responderID, ASN1GeneralizedTime.getInstance(producedAt), responses, Extensions.getInstance(responseExtensions));
+ }
+
+ public ResponseData(
+ ResponderID responderID,
+ ASN1GeneralizedTime producedAt,
+ ASN1Sequence responses,
+ Extensions responseExtensions)
+ {
+ this(V1, responderID, producedAt, responses, responseExtensions);
+ }
+
+ private ResponseData(
+ ASN1Sequence seq)
+ {
+ int index = 0;
+
+ if (seq.getObjectAt(0) instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject o = (ASN1TaggedObject)seq.getObjectAt(0);
+
+ if (o.getTagNo() == 0)
+ {
+ this.versionPresent = true;
+ this.version = ASN1Integer.getInstance(
+ (ASN1TaggedObject)seq.getObjectAt(0), true);
+ index++;
+ }
+ else
+ {
+ this.version = V1;
+ }
+ }
+ else
+ {
+ this.version = V1;
+ }
+
+ this.responderID = ResponderID.getInstance(seq.getObjectAt(index++));
+ this.producedAt = ASN1GeneralizedTime.getInstance(seq.getObjectAt(index++));
+ this.responses = (ASN1Sequence)seq.getObjectAt(index++);
+
+ if (seq.size() > index)
+ {
+ this.responseExtensions = Extensions.getInstance(
+ (ASN1TaggedObject)seq.getObjectAt(index), true);
+ }
+ }
+
+ public static ResponseData getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static ResponseData getInstance(
+ Object obj)
+ {
+ if (obj instanceof ResponseData)
+ {
+ return (ResponseData)obj;
+ }
+ else if (obj != null)
+ {
+ return new ResponseData(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public ResponderID getResponderID()
+ {
+ return responderID;
+ }
+
+ public ASN1GeneralizedTime getProducedAt()
+ {
+ return producedAt;
+ }
+
+ public ASN1Sequence getResponses()
+ {
+ return responses;
+ }
+
+ public Extensions getResponseExtensions()
+ {
+ return responseExtensions;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * ResponseData ::= SEQUENCE {
+ * version [0] EXPLICIT Version DEFAULT v1,
+ * responderID ResponderID,
+ * producedAt GeneralizedTime,
+ * responses SEQUENCE OF SingleResponse,
+ * responseExtensions [1] EXPLICIT Extensions OPTIONAL }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (versionPresent || !version.equals(V1))
+ {
+ v.add(new DERTaggedObject(true, 0, version));
+ }
+
+ v.add(responderID);
+ v.add(producedAt);
+ v.add(responses);
+ if (responseExtensions != null)
+ {
+ v.add(new DERTaggedObject(true, 1, responseExtensions));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ocsp/RevokedInfo.java b/core/src/main/java/org/bouncycastle/asn1/ocsp/RevokedInfo.java
new file mode 100644
index 00000000..7279ae15
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ocsp/RevokedInfo.java
@@ -0,0 +1,92 @@
+package org.bouncycastle.asn1.ocsp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEREnumerated;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.CRLReason;
+
+public class RevokedInfo
+ extends ASN1Object
+{
+ private ASN1GeneralizedTime revocationTime;
+ private CRLReason revocationReason;
+
+ public RevokedInfo(
+ ASN1GeneralizedTime revocationTime,
+ CRLReason revocationReason)
+ {
+ this.revocationTime = revocationTime;
+ this.revocationReason = revocationReason;
+ }
+
+ private RevokedInfo(
+ ASN1Sequence seq)
+ {
+ this.revocationTime = ASN1GeneralizedTime.getInstance(seq.getObjectAt(0));
+
+ if (seq.size() > 1)
+ {
+ this.revocationReason = CRLReason.getInstance(DEREnumerated.getInstance(
+ (ASN1TaggedObject)seq.getObjectAt(1), true));
+ }
+ }
+
+ public static RevokedInfo getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static RevokedInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof RevokedInfo)
+ {
+ return (RevokedInfo)obj;
+ }
+ else if (obj != null)
+ {
+ return new RevokedInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public ASN1GeneralizedTime getRevocationTime()
+ {
+ return revocationTime;
+ }
+
+ public CRLReason getRevocationReason()
+ {
+ return revocationReason;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * RevokedInfo ::= SEQUENCE {
+ * revocationTime GeneralizedTime,
+ * revocationReason [0] EXPLICIT CRLReason OPTIONAL }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(revocationTime);
+ if (revocationReason != null)
+ {
+ v.add(new DERTaggedObject(true, 0, revocationReason));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ocsp/ServiceLocator.java b/core/src/main/java/org/bouncycastle/asn1/ocsp/ServiceLocator.java
new file mode 100644
index 00000000..dc9486fc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ocsp/ServiceLocator.java
@@ -0,0 +1,36 @@
+package org.bouncycastle.asn1.ocsp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x500.X500Name;
+
+public class ServiceLocator
+ extends ASN1Object
+{
+ X500Name issuer;
+ ASN1Primitive locator;
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * ServiceLocator ::= SEQUENCE {
+ * issuer Name,
+ * locator AuthorityInfoAccessSyntax OPTIONAL }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(issuer);
+
+ if (locator != null)
+ {
+ v.add(locator);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ocsp/Signature.java b/core/src/main/java/org/bouncycastle/asn1/ocsp/Signature.java
new file mode 100644
index 00000000..80bd740a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ocsp/Signature.java
@@ -0,0 +1,111 @@
+package org.bouncycastle.asn1.ocsp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class Signature
+ extends ASN1Object
+{
+ AlgorithmIdentifier signatureAlgorithm;
+ DERBitString signature;
+ ASN1Sequence certs;
+
+ public Signature(
+ AlgorithmIdentifier signatureAlgorithm,
+ DERBitString signature)
+ {
+ this.signatureAlgorithm = signatureAlgorithm;
+ this.signature = signature;
+ }
+
+ public Signature(
+ AlgorithmIdentifier signatureAlgorithm,
+ DERBitString signature,
+ ASN1Sequence certs)
+ {
+ this.signatureAlgorithm = signatureAlgorithm;
+ this.signature = signature;
+ this.certs = certs;
+ }
+
+ private Signature(
+ ASN1Sequence seq)
+ {
+ signatureAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(0));
+ signature = (DERBitString)seq.getObjectAt(1);
+
+ if (seq.size() == 3)
+ {
+ certs = ASN1Sequence.getInstance(
+ (ASN1TaggedObject)seq.getObjectAt(2), true);
+ }
+ }
+
+ public static Signature getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static Signature getInstance(
+ Object obj)
+ {
+ if (obj instanceof Signature)
+ {
+ return (Signature)obj;
+ }
+ else if (obj != null)
+ {
+ return new Signature(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public AlgorithmIdentifier getSignatureAlgorithm()
+ {
+ return signatureAlgorithm;
+ }
+
+ public DERBitString getSignature()
+ {
+ return signature;
+ }
+
+ public ASN1Sequence getCerts()
+ {
+ return certs;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * Signature ::= SEQUENCE {
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signature BIT STRING,
+ * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL}
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(signatureAlgorithm);
+ v.add(signature);
+
+ if (certs != null)
+ {
+ v.add(new DERTaggedObject(true, 0, certs));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ocsp/SingleResponse.java b/core/src/main/java/org/bouncycastle/asn1/ocsp/SingleResponse.java
new file mode 100644
index 00000000..ca5a5c42
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ocsp/SingleResponse.java
@@ -0,0 +1,181 @@
+package org.bouncycastle.asn1.ocsp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERGeneralizedTime;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.X509Extensions;
+
+public class SingleResponse
+ extends ASN1Object
+{
+ private CertID certID;
+ private CertStatus certStatus;
+ private ASN1GeneralizedTime thisUpdate;
+ private ASN1GeneralizedTime nextUpdate;
+ private Extensions singleExtensions;
+
+ /**
+ * @deprecated use method taking ASN1GeneralizedTime and Extensions
+ * @param certID
+ * @param certStatus
+ * @param thisUpdate
+ * @param nextUpdate
+ * @param singleExtensions
+ */
+ public SingleResponse(
+ CertID certID,
+ CertStatus certStatus,
+ DERGeneralizedTime thisUpdate,
+ DERGeneralizedTime nextUpdate,
+ X509Extensions singleExtensions)
+ {
+ this(certID, certStatus, thisUpdate, nextUpdate, Extensions.getInstance(singleExtensions));
+ }
+
+ /**
+ * @deprecated use method taking ASN1GeneralizedTime and Extensions
+ * @param certID
+ * @param certStatus
+ * @param thisUpdate
+ * @param nextUpdate
+ * @param singleExtensions
+ */
+ public SingleResponse(
+ CertID certID,
+ CertStatus certStatus,
+ DERGeneralizedTime thisUpdate,
+ DERGeneralizedTime nextUpdate,
+ Extensions singleExtensions)
+ {
+ this(certID, certStatus, ASN1GeneralizedTime.getInstance(thisUpdate), ASN1GeneralizedTime.getInstance(nextUpdate), Extensions.getInstance(singleExtensions));
+ }
+
+ public SingleResponse(
+ CertID certID,
+ CertStatus certStatus,
+ ASN1GeneralizedTime thisUpdate,
+ ASN1GeneralizedTime nextUpdate,
+ Extensions singleExtensions)
+ {
+ this.certID = certID;
+ this.certStatus = certStatus;
+ this.thisUpdate = thisUpdate;
+ this.nextUpdate = nextUpdate;
+ this.singleExtensions = singleExtensions;
+ }
+
+ private SingleResponse(
+ ASN1Sequence seq)
+ {
+ this.certID = CertID.getInstance(seq.getObjectAt(0));
+ this.certStatus = CertStatus.getInstance(seq.getObjectAt(1));
+ this.thisUpdate = ASN1GeneralizedTime.getInstance(seq.getObjectAt(2));
+
+ if (seq.size() > 4)
+ {
+ this.nextUpdate = ASN1GeneralizedTime.getInstance(
+ (ASN1TaggedObject)seq.getObjectAt(3), true);
+ this.singleExtensions = Extensions.getInstance(
+ (ASN1TaggedObject)seq.getObjectAt(4), true);
+ }
+ else if (seq.size() > 3)
+ {
+ ASN1TaggedObject o = (ASN1TaggedObject)seq.getObjectAt(3);
+
+ if (o.getTagNo() == 0)
+ {
+ this.nextUpdate = ASN1GeneralizedTime.getInstance(o, true);
+ }
+ else
+ {
+ this.singleExtensions = Extensions.getInstance(o, true);
+ }
+ }
+ }
+
+ public static SingleResponse getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static SingleResponse getInstance(
+ Object obj)
+ {
+ if (obj instanceof SingleResponse)
+ {
+ return (SingleResponse)obj;
+ }
+ else if (obj != null)
+ {
+ return new SingleResponse(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public CertID getCertID()
+ {
+ return certID;
+ }
+
+ public CertStatus getCertStatus()
+ {
+ return certStatus;
+ }
+
+ public ASN1GeneralizedTime getThisUpdate()
+ {
+ return thisUpdate;
+ }
+
+ public ASN1GeneralizedTime getNextUpdate()
+ {
+ return nextUpdate;
+ }
+
+ public Extensions getSingleExtensions()
+ {
+ return singleExtensions;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * SingleResponse ::= SEQUENCE {
+ * certID CertID,
+ * certStatus CertStatus,
+ * thisUpdate GeneralizedTime,
+ * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
+ * singleExtensions [1] EXPLICIT Extensions OPTIONAL }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(certID);
+ v.add(certStatus);
+ v.add(thisUpdate);
+
+ if (nextUpdate != null)
+ {
+ v.add(new DERTaggedObject(true, 0, nextUpdate));
+ }
+
+ if (singleExtensions != null)
+ {
+ v.add(new DERTaggedObject(true, 1, singleExtensions));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ocsp/TBSRequest.java b/core/src/main/java/org/bouncycastle/asn1/ocsp/TBSRequest.java
new file mode 100644
index 00000000..2a05705b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ocsp/TBSRequest.java
@@ -0,0 +1,172 @@
+package org.bouncycastle.asn1.ocsp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.X509Extensions;
+
+public class TBSRequest
+ extends ASN1Object
+{
+ private static final ASN1Integer V1 = new ASN1Integer(0);
+
+ ASN1Integer version;
+ GeneralName requestorName;
+ ASN1Sequence requestList;
+ Extensions requestExtensions;
+
+ boolean versionSet;
+
+ /**
+ * @deprecated use method taking Extensions
+ * @param requestorName
+ * @param requestList
+ * @param requestExtensions
+ */
+ public TBSRequest(
+ GeneralName requestorName,
+ ASN1Sequence requestList,
+ X509Extensions requestExtensions)
+ {
+ this.version = V1;
+ this.requestorName = requestorName;
+ this.requestList = requestList;
+ this.requestExtensions = Extensions.getInstance(requestExtensions);
+ }
+
+ public TBSRequest(
+ GeneralName requestorName,
+ ASN1Sequence requestList,
+ Extensions requestExtensions)
+ {
+ this.version = V1;
+ this.requestorName = requestorName;
+ this.requestList = requestList;
+ this.requestExtensions = requestExtensions;
+ }
+
+ private TBSRequest(
+ ASN1Sequence seq)
+ {
+ int index = 0;
+
+ if (seq.getObjectAt(0) instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject o = (ASN1TaggedObject)seq.getObjectAt(0);
+
+ if (o.getTagNo() == 0)
+ {
+ versionSet = true;
+ version = ASN1Integer.getInstance((ASN1TaggedObject)seq.getObjectAt(0), true);
+ index++;
+ }
+ else
+ {
+ version = V1;
+ }
+ }
+ else
+ {
+ version = V1;
+ }
+
+ if (seq.getObjectAt(index) instanceof ASN1TaggedObject)
+ {
+ requestorName = GeneralName.getInstance((ASN1TaggedObject)seq.getObjectAt(index++), true);
+ }
+
+ requestList = (ASN1Sequence)seq.getObjectAt(index++);
+
+ if (seq.size() == (index + 1))
+ {
+ requestExtensions = Extensions.getInstance((ASN1TaggedObject)seq.getObjectAt(index), true);
+ }
+ }
+
+ public static TBSRequest getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static TBSRequest getInstance(
+ Object obj)
+ {
+ if (obj instanceof TBSRequest)
+ {
+ return (TBSRequest)obj;
+ }
+ else if (obj != null)
+ {
+ return new TBSRequest(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public GeneralName getRequestorName()
+ {
+ return requestorName;
+ }
+
+ public ASN1Sequence getRequestList()
+ {
+ return requestList;
+ }
+
+ public Extensions getRequestExtensions()
+ {
+ return requestExtensions;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * TBSRequest ::= SEQUENCE {
+ * version [0] EXPLICIT Version DEFAULT v1,
+ * requestorName [1] EXPLICIT GeneralName OPTIONAL,
+ * requestList SEQUENCE OF Request,
+ * requestExtensions [2] EXPLICIT Extensions OPTIONAL }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ //
+ // if default don't include - unless explicitly provided. Not strictly correct
+ // but required for some requests
+ //
+ if (!version.equals(V1) || versionSet)
+ {
+ v.add(new DERTaggedObject(true, 0, version));
+ }
+
+ if (requestorName != null)
+ {
+ v.add(new DERTaggedObject(true, 1, requestorName));
+ }
+
+ v.add(requestList);
+
+ if (requestExtensions != null)
+ {
+ v.add(new DERTaggedObject(true, 2, requestExtensions));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/oiw/ElGamalParameter.java b/core/src/main/java/org/bouncycastle/asn1/oiw/ElGamalParameter.java
new file mode 100644
index 00000000..c6a2965b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/oiw/ElGamalParameter.java
@@ -0,0 +1,54 @@
+package org.bouncycastle.asn1.oiw;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class ElGamalParameter
+ extends ASN1Object
+{
+ ASN1Integer p, g;
+
+ public ElGamalParameter(
+ BigInteger p,
+ BigInteger g)
+ {
+ this.p = new ASN1Integer(p);
+ this.g = new ASN1Integer(g);
+ }
+
+ public ElGamalParameter(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ p = (ASN1Integer)e.nextElement();
+ g = (ASN1Integer)e.nextElement();
+ }
+
+ public BigInteger getP()
+ {
+ return p.getPositiveValue();
+ }
+
+ public BigInteger getG()
+ {
+ return g.getPositiveValue();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(p);
+ v.add(g);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java
new file mode 100644
index 00000000..c8ce26b5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java
@@ -0,0 +1,31 @@
+package org.bouncycastle.asn1.oiw;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface OIWObjectIdentifiers
+{
+ // id-SHA1 OBJECT IDENTIFIER ::=
+ // {iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 } //
+ static final ASN1ObjectIdentifier md4WithRSA = new ASN1ObjectIdentifier("1.3.14.3.2.2");
+ static final ASN1ObjectIdentifier md5WithRSA = new ASN1ObjectIdentifier("1.3.14.3.2.3");
+ static final ASN1ObjectIdentifier md4WithRSAEncryption = new ASN1ObjectIdentifier("1.3.14.3.2.4");
+
+ static final ASN1ObjectIdentifier desECB = new ASN1ObjectIdentifier("1.3.14.3.2.6");
+ static final ASN1ObjectIdentifier desCBC = new ASN1ObjectIdentifier("1.3.14.3.2.7");
+ static final ASN1ObjectIdentifier desOFB = new ASN1ObjectIdentifier("1.3.14.3.2.8");
+ static final ASN1ObjectIdentifier desCFB = new ASN1ObjectIdentifier("1.3.14.3.2.9");
+
+ static final ASN1ObjectIdentifier desEDE = new ASN1ObjectIdentifier("1.3.14.3.2.17");
+
+ static final ASN1ObjectIdentifier idSHA1 = new ASN1ObjectIdentifier("1.3.14.3.2.26");
+
+ static final ASN1ObjectIdentifier dsaWithSHA1 = new ASN1ObjectIdentifier("1.3.14.3.2.27");
+
+ static final ASN1ObjectIdentifier sha1WithRSA = new ASN1ObjectIdentifier("1.3.14.3.2.29");
+
+ // ElGamal Algorithm OBJECT IDENTIFIER ::=
+ // {iso(1) identified-organization(3) oiw(14) dirservsig(7) algorithm(2) encryption(1) 1 }
+ //
+ static final ASN1ObjectIdentifier elGamalAlgorithm = new ASN1ObjectIdentifier("1.3.14.7.2.1.1");
+
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/Attribute.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/Attribute.java
new file mode 100644
index 00000000..6374c980
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/Attribute.java
@@ -0,0 +1,88 @@
+package org.bouncycastle.asn1.pkcs;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.DERSequence;
+
+public class Attribute
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier attrType;
+ private ASN1Set attrValues;
+
+ /**
+ * return an Attribute object from the given object.
+ *
+ * @param o the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static Attribute getInstance(
+ Object o)
+ {
+ if (o == null || o instanceof Attribute)
+ {
+ return (Attribute)o;
+ }
+
+ if (o instanceof ASN1Sequence)
+ {
+ return new Attribute((ASN1Sequence)o);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory: " + o.getClass().getName());
+ }
+
+ public Attribute(
+ ASN1Sequence seq)
+ {
+ attrType = (ASN1ObjectIdentifier)seq.getObjectAt(0);
+ attrValues = (ASN1Set)seq.getObjectAt(1);
+ }
+
+ public Attribute(
+ ASN1ObjectIdentifier attrType,
+ ASN1Set attrValues)
+ {
+ this.attrType = attrType;
+ this.attrValues = attrValues;
+ }
+
+ public ASN1ObjectIdentifier getAttrType()
+ {
+ return attrType;
+ }
+
+ public ASN1Set getAttrValues()
+ {
+ return attrValues;
+ }
+
+ public ASN1Encodable[] getAttributeValues()
+ {
+ return attrValues.toArray();
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * Attribute ::= SEQUENCE {
+ * attrType OBJECT IDENTIFIER,
+ * attrValues SET OF AttributeValue
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(attrType);
+ v.add(attrValues);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/AuthenticatedSafe.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/AuthenticatedSafe.java
new file mode 100644
index 00000000..ea4779bb
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/AuthenticatedSafe.java
@@ -0,0 +1,74 @@
+package org.bouncycastle.asn1.pkcs;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.BERSequence;
+import org.bouncycastle.asn1.DLSequence;
+
+public class AuthenticatedSafe
+ extends ASN1Object
+{
+ private ContentInfo[] info;
+ private boolean isBer = true;
+
+ private AuthenticatedSafe(
+ ASN1Sequence seq)
+ {
+ info = new ContentInfo[seq.size()];
+
+ for (int i = 0; i != info.length; i++)
+ {
+ info[i] = ContentInfo.getInstance(seq.getObjectAt(i));
+ }
+
+ isBer = seq instanceof BERSequence;
+ }
+
+ public static AuthenticatedSafe getInstance(
+ Object o)
+ {
+ if (o instanceof AuthenticatedSafe)
+ {
+ return (AuthenticatedSafe)o;
+ }
+
+ if (o != null)
+ {
+ return new AuthenticatedSafe(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public AuthenticatedSafe(
+ ContentInfo[] info)
+ {
+ this.info = info;
+ }
+
+ public ContentInfo[] getContentInfo()
+ {
+ return info;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ for (int i = 0; i != info.length; i++)
+ {
+ v.add(info[i]);
+ }
+
+ if (isBer)
+ {
+ return new BERSequence(v);
+ }
+ else
+ {
+ return new DLSequence(v);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/CRLBag.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/CRLBag.java
new file mode 100644
index 00000000..b91c1a59
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/CRLBag.java
@@ -0,0 +1,82 @@
+package org.bouncycastle.asn1.pkcs;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class CRLBag
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier crlId;
+ private ASN1Encodable crlValue;
+
+ private CRLBag(
+ ASN1Sequence seq)
+ {
+ this.crlId = (ASN1ObjectIdentifier)seq.getObjectAt(0);
+ this.crlValue = ((DERTaggedObject)seq.getObjectAt(1)).getObject();
+ }
+
+ public static CRLBag getInstance(Object o)
+ {
+ if (o instanceof CRLBag)
+ {
+ return (CRLBag)o;
+ }
+ else if (o != null)
+ {
+ return new CRLBag(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public CRLBag(
+ ASN1ObjectIdentifier crlId,
+ ASN1Encodable crlValue)
+ {
+ this.crlId = crlId;
+ this.crlValue = crlValue;
+ }
+
+ public ASN1ObjectIdentifier getcrlId()
+ {
+ return crlId;
+ }
+
+ public ASN1Encodable getCRLValue()
+ {
+ return crlValue;
+ }
+
+ /**
+ * <pre>
+ CRLBag ::= SEQUENCE {
+ crlId BAG-TYPE.&id ({CRLTypes}),
+ crlValue [0] EXPLICIT BAG-TYPE.&Type ({CRLTypes}{@crlId})
+ }
+
+ x509CRL BAG-TYPE ::= {OCTET STRING IDENTIFIED BY {certTypes 1}
+ -- DER-encoded X.509 CRL stored in OCTET STRING
+
+ CRLTypes BAG-TYPE ::= {
+ x509CRL,
+ ... -- For future extensions
+ }
+ </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(crlId);
+ v.add(new DERTaggedObject(0, crlValue));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/CertBag.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/CertBag.java
new file mode 100644
index 00000000..4a730286
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/CertBag.java
@@ -0,0 +1,66 @@
+package org.bouncycastle.asn1.pkcs;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class CertBag
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier certId;
+ private ASN1Encodable certValue;
+
+ private CertBag(
+ ASN1Sequence seq)
+ {
+ this.certId = (ASN1ObjectIdentifier)seq.getObjectAt(0);
+ this.certValue = ((DERTaggedObject)seq.getObjectAt(1)).getObject();
+ }
+
+ public static CertBag getInstance(Object o)
+ {
+ if (o instanceof CertBag)
+ {
+ return (CertBag)o;
+ }
+ else if (o != null)
+ {
+ return new CertBag(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public CertBag(
+ ASN1ObjectIdentifier certId,
+ ASN1Encodable certValue)
+ {
+ this.certId = certId;
+ this.certValue = certValue;
+ }
+
+ public ASN1ObjectIdentifier getCertId()
+ {
+ return certId;
+ }
+
+ public ASN1Encodable getCertValue()
+ {
+ return certValue;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(certId);
+ v.add(new DERTaggedObject(0, certValue));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequest.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequest.java
new file mode 100644
index 00000000..987d4eba
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequest.java
@@ -0,0 +1,91 @@
+package org.bouncycastle.asn1.pkcs;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * PKCS10 Certification request object.
+ * <pre>
+ * CertificationRequest ::= SEQUENCE {
+ * certificationRequestInfo CertificationRequestInfo,
+ * signatureAlgorithm AlgorithmIdentifier{{ SignatureAlgorithms }},
+ * signature BIT STRING
+ * }
+ * </pre>
+ */
+public class CertificationRequest
+ extends ASN1Object
+{
+ protected CertificationRequestInfo reqInfo = null;
+ protected AlgorithmIdentifier sigAlgId = null;
+ protected DERBitString sigBits = null;
+
+ public static CertificationRequest getInstance(Object o)
+ {
+ if (o instanceof CertificationRequest)
+ {
+ return (CertificationRequest)o;
+ }
+
+ if (o != null)
+ {
+ return new CertificationRequest(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ protected CertificationRequest()
+ {
+ }
+
+ public CertificationRequest(
+ CertificationRequestInfo requestInfo,
+ AlgorithmIdentifier algorithm,
+ DERBitString signature)
+ {
+ this.reqInfo = requestInfo;
+ this.sigAlgId = algorithm;
+ this.sigBits = signature;
+ }
+
+ public CertificationRequest(
+ ASN1Sequence seq)
+ {
+ reqInfo = CertificationRequestInfo.getInstance(seq.getObjectAt(0));
+ sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
+ sigBits = (DERBitString)seq.getObjectAt(2);
+ }
+
+ public CertificationRequestInfo getCertificationRequestInfo()
+ {
+ return reqInfo;
+ }
+
+ public AlgorithmIdentifier getSignatureAlgorithm()
+ {
+ return sigAlgId;
+ }
+
+ public DERBitString getSignature()
+ {
+ return sigBits;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ // Construct the CertificateRequest
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(reqInfo);
+ v.add(sigAlgId);
+ v.add(sigBits);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java
new file mode 100644
index 00000000..c9c14fe4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java
@@ -0,0 +1,164 @@
+package org.bouncycastle.asn1.pkcs;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x509.X509Name;
+
+/**
+ * PKCS10 CertificationRequestInfo object.
+ * <pre>
+ * CertificationRequestInfo ::= SEQUENCE {
+ * version INTEGER { v1(0) } (v1,...),
+ * subject Name,
+ * subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
+ * attributes [0] Attributes{{ CRIAttributes }}
+ * }
+ *
+ * Attributes { ATTRIBUTE:IOSet } ::= SET OF Attribute{{ IOSet }}
+ *
+ * Attribute { ATTRIBUTE:IOSet } ::= SEQUENCE {
+ * type ATTRIBUTE.&id({IOSet}),
+ * values SET SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{\@type})
+ * }
+ * </pre>
+ */
+public class CertificationRequestInfo
+ extends ASN1Object
+{
+ ASN1Integer version = new ASN1Integer(0);
+ X500Name subject;
+ SubjectPublicKeyInfo subjectPKInfo;
+ ASN1Set attributes = null;
+
+ public static CertificationRequestInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof CertificationRequestInfo)
+ {
+ return (CertificationRequestInfo)obj;
+ }
+ else if (obj != null)
+ {
+ return new CertificationRequestInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Basic constructor.
+ * <p>
+ * Note: Early on a lot of CAs would only accept messages with attributes missing. As the ASN.1 def shows
+ * the attributes field is not optional so should always at least contain an empty set. If a fully compliant
+ * request is required, pass in an empty set, the class will otherwise interpret a null as it should
+ * encode the request with the field missing.
+ * </p>
+ *
+ * @param subject subject to be associated with the public key
+ * @param pkInfo public key to be associated with subject
+ * @param attributes any attributes to be associated with the request.
+ */
+ public CertificationRequestInfo(
+ X500Name subject,
+ SubjectPublicKeyInfo pkInfo,
+ ASN1Set attributes)
+ {
+ this.subject = subject;
+ this.subjectPKInfo = pkInfo;
+ this.attributes = attributes;
+
+ if ((subject == null) || (version == null) || (subjectPKInfo == null))
+ {
+ throw new IllegalArgumentException("Not all mandatory fields set in CertificationRequestInfo generator.");
+ }
+ }
+
+ /**
+ * @deprecated use X500Name method.
+ */
+ public CertificationRequestInfo(
+ X509Name subject,
+ SubjectPublicKeyInfo pkInfo,
+ ASN1Set attributes)
+ {
+ this.subject = X500Name.getInstance(subject.toASN1Primitive());
+ this.subjectPKInfo = pkInfo;
+ this.attributes = attributes;
+
+ if ((subject == null) || (version == null) || (subjectPKInfo == null))
+ {
+ throw new IllegalArgumentException("Not all mandatory fields set in CertificationRequestInfo generator.");
+ }
+ }
+
+ /**
+ * @deprecated use getInstance().
+ */
+ public CertificationRequestInfo(
+ ASN1Sequence seq)
+ {
+ version = (ASN1Integer)seq.getObjectAt(0);
+
+ subject = X500Name.getInstance(seq.getObjectAt(1));
+ subjectPKInfo = SubjectPublicKeyInfo.getInstance(seq.getObjectAt(2));
+
+ //
+ // some CertificationRequestInfo objects seem to treat this field
+ // as optional.
+ //
+ if (seq.size() > 3)
+ {
+ DERTaggedObject tagobj = (DERTaggedObject)seq.getObjectAt(3);
+ attributes = ASN1Set.getInstance(tagobj, false);
+ }
+
+ if ((subject == null) || (version == null) || (subjectPKInfo == null))
+ {
+ throw new IllegalArgumentException("Not all mandatory fields set in CertificationRequestInfo generator.");
+ }
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public X500Name getSubject()
+ {
+ return subject;
+ }
+
+ public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
+ {
+ return subjectPKInfo;
+ }
+
+ public ASN1Set getAttributes()
+ {
+ return attributes;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+ v.add(subject);
+ v.add(subjectPKInfo);
+
+ if (attributes != null)
+ {
+ v.add(new DERTaggedObject(false, 0, attributes));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/ContentInfo.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/ContentInfo.java
new file mode 100644
index 00000000..1ee920fd
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/ContentInfo.java
@@ -0,0 +1,102 @@
+package org.bouncycastle.asn1.pkcs;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.BERSequence;
+import org.bouncycastle.asn1.BERTaggedObject;
+import org.bouncycastle.asn1.DLSequence;
+
+public class ContentInfo
+ extends ASN1Object
+ implements PKCSObjectIdentifiers
+{
+ private ASN1ObjectIdentifier contentType;
+ private ASN1Encodable content;
+ private boolean isBer = true;
+
+ public static ContentInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof ContentInfo)
+ {
+ return (ContentInfo)obj;
+ }
+
+ if (obj != null)
+ {
+ return new ContentInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private ContentInfo(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ contentType = (ASN1ObjectIdentifier)e.nextElement();
+
+ if (e.hasMoreElements())
+ {
+ content = ((ASN1TaggedObject)e.nextElement()).getObject();
+ }
+
+ isBer = seq instanceof BERSequence;
+ }
+
+ public ContentInfo(
+ ASN1ObjectIdentifier contentType,
+ ASN1Encodable content)
+ {
+ this.contentType = contentType;
+ this.content = content;
+ }
+
+ public ASN1ObjectIdentifier getContentType()
+ {
+ return contentType;
+ }
+
+ public ASN1Encodable getContent()
+ {
+ return content;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * ContentInfo ::= SEQUENCE {
+ * contentType ContentType,
+ * content
+ * [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(contentType);
+
+ if (content != null)
+ {
+ v.add(new BERTaggedObject(true, 0, content));
+ }
+
+ if (isBer)
+ {
+ return new BERSequence(v);
+ }
+ else
+ {
+ return new DLSequence(v);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/DHParameter.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/DHParameter.java
new file mode 100644
index 00000000..fa22f792
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/DHParameter.java
@@ -0,0 +1,104 @@
+package org.bouncycastle.asn1.pkcs;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class DHParameter
+ extends ASN1Object
+{
+ ASN1Integer p, g, l;
+
+ public DHParameter(
+ BigInteger p,
+ BigInteger g,
+ int l)
+ {
+ this.p = new ASN1Integer(p);
+ this.g = new ASN1Integer(g);
+
+ if (l != 0)
+ {
+ this.l = new ASN1Integer(l);
+ }
+ else
+ {
+ this.l = null;
+ }
+ }
+
+ public static DHParameter getInstance(
+ Object obj)
+ {
+ if (obj instanceof DHParameter)
+ {
+ return (DHParameter)obj;
+ }
+
+ if (obj != null)
+ {
+ return new DHParameter(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private DHParameter(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ p = ASN1Integer.getInstance(e.nextElement());
+ g = ASN1Integer.getInstance(e.nextElement());
+
+ if (e.hasMoreElements())
+ {
+ l = (ASN1Integer)e.nextElement();
+ }
+ else
+ {
+ l = null;
+ }
+ }
+
+ public BigInteger getP()
+ {
+ return p.getPositiveValue();
+ }
+
+ public BigInteger getG()
+ {
+ return g.getPositiveValue();
+ }
+
+ public BigInteger getL()
+ {
+ if (l == null)
+ {
+ return null;
+ }
+
+ return l.getPositiveValue();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(p);
+ v.add(g);
+
+ if (this.getL() != null)
+ {
+ v.add(l);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedData.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedData.java
new file mode 100644
index 00000000..e0f5efdf
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedData.java
@@ -0,0 +1,115 @@
+package org.bouncycastle.asn1.pkcs;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.BERSequence;
+import org.bouncycastle.asn1.BERTaggedObject;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * The EncryptedData object.
+ * <pre>
+ * EncryptedData ::= SEQUENCE {
+ * version Version,
+ * encryptedContentInfo EncryptedContentInfo
+ * }
+ *
+ *
+ * EncryptedContentInfo ::= SEQUENCE {
+ * contentType ContentType,
+ * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
+ * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
+ * }
+ *
+ * EncryptedContent ::= OCTET STRING
+ * </pre>
+ */
+public class EncryptedData
+ extends ASN1Object
+{
+ ASN1Sequence data;
+ ASN1ObjectIdentifier bagId;
+ ASN1Primitive bagValue;
+
+ public static EncryptedData getInstance(
+ Object obj)
+ {
+ if (obj instanceof EncryptedData)
+ {
+ return (EncryptedData)obj;
+ }
+
+ if (obj != null)
+ {
+ return new EncryptedData(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private EncryptedData(
+ ASN1Sequence seq)
+ {
+ int version = ((ASN1Integer)seq.getObjectAt(0)).getValue().intValue();
+
+ if (version != 0)
+ {
+ throw new IllegalArgumentException("sequence not version 0");
+ }
+
+ this.data = ASN1Sequence.getInstance(seq.getObjectAt(1));
+ }
+
+ public EncryptedData(
+ ASN1ObjectIdentifier contentType,
+ AlgorithmIdentifier encryptionAlgorithm,
+ ASN1Encodable content)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(contentType);
+ v.add(encryptionAlgorithm.toASN1Primitive());
+ v.add(new BERTaggedObject(false, 0, content));
+
+ data = new BERSequence(v);
+ }
+
+ public ASN1ObjectIdentifier getContentType()
+ {
+ return ASN1ObjectIdentifier.getInstance(data.getObjectAt(0));
+ }
+
+ public AlgorithmIdentifier getEncryptionAlgorithm()
+ {
+ return AlgorithmIdentifier.getInstance(data.getObjectAt(1));
+ }
+
+ public ASN1OctetString getContent()
+ {
+ if (data.size() == 3)
+ {
+ ASN1TaggedObject o = ASN1TaggedObject.getInstance(data.getObjectAt(2));
+
+ return ASN1OctetString.getInstance(o, false);
+ }
+
+ return null;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new ASN1Integer(0));
+ v.add(data);
+
+ return new BERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java
new file mode 100644
index 00000000..acbe04a9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java
@@ -0,0 +1,86 @@
+package org.bouncycastle.asn1.pkcs;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class EncryptedPrivateKeyInfo
+ extends ASN1Object
+{
+ private AlgorithmIdentifier algId;
+ private ASN1OctetString data;
+
+ private EncryptedPrivateKeyInfo(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ algId = AlgorithmIdentifier.getInstance(e.nextElement());
+ data = ASN1OctetString.getInstance(e.nextElement());
+ }
+
+ public EncryptedPrivateKeyInfo(
+ AlgorithmIdentifier algId,
+ byte[] encoding)
+ {
+ this.algId = algId;
+ this.data = new DEROctetString(encoding);
+ }
+
+ public static EncryptedPrivateKeyInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof EncryptedPrivateKeyInfo)
+ {
+ return (EncryptedPrivateKeyInfo)obj;
+ }
+ else if (obj != null)
+ {
+ return new EncryptedPrivateKeyInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public AlgorithmIdentifier getEncryptionAlgorithm()
+ {
+ return algId;
+ }
+
+ public byte[] getEncryptedData()
+ {
+ return data.getOctets();
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * EncryptedPrivateKeyInfo ::= SEQUENCE {
+ * encryptionAlgorithm AlgorithmIdentifier {{KeyEncryptionAlgorithms}},
+ * encryptedData EncryptedData
+ * }
+ *
+ * EncryptedData ::= OCTET STRING
+ *
+ * KeyEncryptionAlgorithms ALGORITHM-IDENTIFIER ::= {
+ * ... -- For local profiles
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(algId);
+ v.add(data);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/EncryptionScheme.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/EncryptionScheme.java
new file mode 100644
index 00000000..c885a6c6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/EncryptionScheme.java
@@ -0,0 +1,56 @@
+package org.bouncycastle.asn1.pkcs;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class EncryptionScheme
+ extends ASN1Object
+{
+ private AlgorithmIdentifier algId;
+
+ public EncryptionScheme(
+ ASN1ObjectIdentifier objectId,
+ ASN1Encodable parameters)
+ {
+ this.algId = new AlgorithmIdentifier(objectId, parameters);
+ }
+
+ private EncryptionScheme(
+ ASN1Sequence seq)
+ {
+ this.algId = AlgorithmIdentifier.getInstance(seq);
+ }
+
+ public static final EncryptionScheme getInstance(Object obj)
+ {
+ if (obj instanceof EncryptionScheme)
+ {
+ return (EncryptionScheme)obj;
+ }
+ else if (obj != null)
+ {
+ return new EncryptionScheme(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public ASN1ObjectIdentifier getAlgorithm()
+ {
+ return algId.getAlgorithm();
+ }
+
+ public ASN1Encodable getParameters()
+ {
+ return algId.getParameters();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return algId.toASN1Primitive();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/IssuerAndSerialNumber.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/IssuerAndSerialNumber.java
new file mode 100644
index 00000000..6cbf907a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/IssuerAndSerialNumber.java
@@ -0,0 +1,85 @@
+package org.bouncycastle.asn1.pkcs;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.X509Name;
+
+public class IssuerAndSerialNumber
+ extends ASN1Object
+{
+ X500Name name;
+ ASN1Integer certSerialNumber;
+
+ public static IssuerAndSerialNumber getInstance(
+ Object obj)
+ {
+ if (obj instanceof IssuerAndSerialNumber)
+ {
+ return (IssuerAndSerialNumber)obj;
+ }
+ else if (obj != null)
+ {
+ return new IssuerAndSerialNumber(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private IssuerAndSerialNumber(
+ ASN1Sequence seq)
+ {
+ this.name = X500Name.getInstance(seq.getObjectAt(0));
+ this.certSerialNumber = (ASN1Integer)seq.getObjectAt(1);
+ }
+
+ public IssuerAndSerialNumber(
+ X509Name name,
+ BigInteger certSerialNumber)
+ {
+ this.name = X500Name.getInstance(name.toASN1Primitive());
+ this.certSerialNumber = new ASN1Integer(certSerialNumber);
+ }
+
+ public IssuerAndSerialNumber(
+ X509Name name,
+ ASN1Integer certSerialNumber)
+ {
+ this.name = X500Name.getInstance(name.toASN1Primitive());
+ this.certSerialNumber = certSerialNumber;
+ }
+
+ public IssuerAndSerialNumber(
+ X500Name name,
+ BigInteger certSerialNumber)
+ {
+ this.name = name;
+ this.certSerialNumber = new ASN1Integer(certSerialNumber);
+ }
+
+ public X500Name getName()
+ {
+ return name;
+ }
+
+ public ASN1Integer getCertificateSerialNumber()
+ {
+ return certSerialNumber;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(name);
+ v.add(certSerialNumber);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/KeyDerivationFunc.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/KeyDerivationFunc.java
new file mode 100644
index 00000000..3b408362
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/KeyDerivationFunc.java
@@ -0,0 +1,56 @@
+package org.bouncycastle.asn1.pkcs;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class KeyDerivationFunc
+ extends ASN1Object
+{
+ private AlgorithmIdentifier algId;
+
+ public KeyDerivationFunc(
+ ASN1ObjectIdentifier objectId,
+ ASN1Encodable parameters)
+ {
+ this.algId = new AlgorithmIdentifier(objectId, parameters);
+ }
+
+ private KeyDerivationFunc(
+ ASN1Sequence seq)
+ {
+ this.algId = AlgorithmIdentifier.getInstance(seq);
+ }
+
+ public static final KeyDerivationFunc getInstance(Object obj)
+ {
+ if (obj instanceof KeyDerivationFunc)
+ {
+ return (KeyDerivationFunc)obj;
+ }
+ else if (obj != null)
+ {
+ return new KeyDerivationFunc(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public ASN1ObjectIdentifier getAlgorithm()
+ {
+ return algId.getAlgorithm();
+ }
+
+ public ASN1Encodable getParameters()
+ {
+ return algId.getParameters();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return algId.toASN1Primitive();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/MacData.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/MacData.java
new file mode 100644
index 00000000..1d8f582c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/MacData.java
@@ -0,0 +1,106 @@
+package org.bouncycastle.asn1.pkcs;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.DigestInfo;
+
+public class MacData
+ extends ASN1Object
+{
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ DigestInfo digInfo;
+ byte[] salt;
+ BigInteger iterationCount;
+
+ public static MacData getInstance(
+ Object obj)
+ {
+ if (obj instanceof MacData)
+ {
+ return (MacData)obj;
+ }
+ else if (obj != null)
+ {
+ return new MacData(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private MacData(
+ ASN1Sequence seq)
+ {
+ this.digInfo = DigestInfo.getInstance(seq.getObjectAt(0));
+
+ this.salt = ((ASN1OctetString)seq.getObjectAt(1)).getOctets();
+
+ if (seq.size() == 3)
+ {
+ this.iterationCount = ((ASN1Integer)seq.getObjectAt(2)).getValue();
+ }
+ else
+ {
+ this.iterationCount = ONE;
+ }
+ }
+
+ public MacData(
+ DigestInfo digInfo,
+ byte[] salt,
+ int iterationCount)
+ {
+ this.digInfo = digInfo;
+ this.salt = salt;
+ this.iterationCount = BigInteger.valueOf(iterationCount);
+ }
+
+ public DigestInfo getMac()
+ {
+ return digInfo;
+ }
+
+ public byte[] getSalt()
+ {
+ return salt;
+ }
+
+ public BigInteger getIterationCount()
+ {
+ return iterationCount;
+ }
+
+ /**
+ * <pre>
+ * MacData ::= SEQUENCE {
+ * mac DigestInfo,
+ * macSalt OCTET STRING,
+ * iterations INTEGER DEFAULT 1
+ * -- Note: The default is for historic reasons and its use is deprecated. A
+ * -- higher value, like 1024 is recommended.
+ * </pre>
+ * @return the basic ASN1Primitive construction.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(digInfo);
+ v.add(new DEROctetString(salt));
+
+ if (!iterationCount.equals(ONE))
+ {
+ v.add(new ASN1Integer(iterationCount));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/PBEParameter.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/PBEParameter.java
new file mode 100644
index 00000000..06180dfe
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/PBEParameter.java
@@ -0,0 +1,73 @@
+package org.bouncycastle.asn1.pkcs;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+
+public class PBEParameter
+ extends ASN1Object
+{
+ ASN1Integer iterations;
+ ASN1OctetString salt;
+
+ public PBEParameter(
+ byte[] salt,
+ int iterations)
+ {
+ if (salt.length != 8)
+ {
+ throw new IllegalArgumentException("salt length must be 8");
+ }
+ this.salt = new DEROctetString(salt);
+ this.iterations = new ASN1Integer(iterations);
+ }
+
+ private PBEParameter(
+ ASN1Sequence seq)
+ {
+ salt = (ASN1OctetString)seq.getObjectAt(0);
+ iterations = (ASN1Integer)seq.getObjectAt(1);
+ }
+
+ public static PBEParameter getInstance(
+ Object obj)
+ {
+ if (obj instanceof PBEParameter)
+ {
+ return (PBEParameter)obj;
+ }
+ else if (obj != null)
+ {
+ return new PBEParameter(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public BigInteger getIterationCount()
+ {
+ return iterations.getValue();
+ }
+
+ public byte[] getSalt()
+ {
+ return salt.getOctets();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(salt);
+ v.add(iterations);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/PBES2Algorithms.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/PBES2Algorithms.java
new file mode 100644
index 00000000..db44a82a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/PBES2Algorithms.java
@@ -0,0 +1,77 @@
+package org.bouncycastle.asn1.pkcs;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * @deprecated - use AlgorithmIdentifier and PBES2Parameters
+ */
+public class PBES2Algorithms
+ extends AlgorithmIdentifier implements PKCSObjectIdentifiers
+{
+ private ASN1ObjectIdentifier objectId;
+ private KeyDerivationFunc func;
+ private EncryptionScheme scheme;
+
+ public PBES2Algorithms(
+ ASN1Sequence obj)
+ {
+ super(obj);
+
+ Enumeration e = obj.getObjects();
+
+ objectId = (ASN1ObjectIdentifier)e.nextElement();
+
+ ASN1Sequence seq = (ASN1Sequence)e.nextElement();
+
+ e = seq.getObjects();
+
+ ASN1Sequence funcSeq = (ASN1Sequence)e.nextElement();
+
+ if (funcSeq.getObjectAt(0).equals(id_PBKDF2))
+ {
+ func = new KeyDerivationFunc(id_PBKDF2, PBKDF2Params.getInstance(funcSeq.getObjectAt(1)));
+ }
+ else
+ {
+ func = KeyDerivationFunc.getInstance(funcSeq);
+ }
+
+ scheme = EncryptionScheme.getInstance(e.nextElement());
+ }
+
+ public ASN1ObjectIdentifier getObjectId()
+ {
+ return objectId;
+ }
+
+ public KeyDerivationFunc getKeyDerivationFunc()
+ {
+ return func;
+ }
+
+ public EncryptionScheme getEncryptionScheme()
+ {
+ return scheme;
+ }
+
+ public ASN1Primitive getASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ ASN1EncodableVector subV = new ASN1EncodableVector();
+
+ v.add(objectId);
+
+ subV.add(func);
+ subV.add(scheme);
+ v.add(new DERSequence(subV));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/PBES2Parameters.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/PBES2Parameters.java
new file mode 100644
index 00000000..b47e9cdd
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/PBES2Parameters.java
@@ -0,0 +1,77 @@
+package org.bouncycastle.asn1.pkcs;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class PBES2Parameters
+ extends ASN1Object
+ implements PKCSObjectIdentifiers
+{
+ private KeyDerivationFunc func;
+ private EncryptionScheme scheme;
+
+ public static PBES2Parameters getInstance(
+ Object obj)
+ {
+ if (obj instanceof PBES2Parameters)
+ {
+ return (PBES2Parameters)obj;
+ }
+ if (obj != null)
+ {
+ return new PBES2Parameters(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public PBES2Parameters(KeyDerivationFunc keyDevFunc, EncryptionScheme encScheme)
+ {
+ this.func = keyDevFunc;
+ this.scheme = encScheme;
+ }
+
+ private PBES2Parameters(
+ ASN1Sequence obj)
+ {
+ Enumeration e = obj.getObjects();
+ ASN1Sequence funcSeq = ASN1Sequence.getInstance(((ASN1Encodable)e.nextElement()).toASN1Primitive());
+
+ if (funcSeq.getObjectAt(0).equals(id_PBKDF2))
+ {
+ func = new KeyDerivationFunc(id_PBKDF2, PBKDF2Params.getInstance(funcSeq.getObjectAt(1)));
+ }
+ else
+ {
+ func = KeyDerivationFunc.getInstance(funcSeq);
+ }
+
+ scheme = EncryptionScheme.getInstance(e.nextElement());
+ }
+
+ public KeyDerivationFunc getKeyDerivationFunc()
+ {
+ return func;
+ }
+
+ public EncryptionScheme getEncryptionScheme()
+ {
+ return scheme;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(func);
+ v.add(scheme);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java
new file mode 100644
index 00000000..65c0fa85
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.asn1.pkcs;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+
+public class PBKDF2Params
+ extends ASN1Object
+{
+ private ASN1OctetString octStr;
+ private ASN1Integer iterationCount;
+ private ASN1Integer keyLength;
+
+ public static PBKDF2Params getInstance(
+ Object obj)
+ {
+ if (obj instanceof PBKDF2Params)
+ {
+ return (PBKDF2Params)obj;
+ }
+
+ if (obj != null)
+ {
+ return new PBKDF2Params(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public PBKDF2Params(
+ byte[] salt,
+ int iterationCount)
+ {
+ this.octStr = new DEROctetString(salt);
+ this.iterationCount = new ASN1Integer(iterationCount);
+ }
+
+ public PBKDF2Params(
+ byte[] salt,
+ int iterationCount,
+ int keyLength)
+ {
+ this(salt, iterationCount);
+
+ this.keyLength = new ASN1Integer(keyLength);
+ }
+
+ private PBKDF2Params(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ octStr = (ASN1OctetString)e.nextElement();
+ iterationCount = (ASN1Integer)e.nextElement();
+
+ if (e.hasMoreElements())
+ {
+ keyLength = (ASN1Integer)e.nextElement();
+ }
+ else
+ {
+ keyLength = null;
+ }
+ }
+
+ public byte[] getSalt()
+ {
+ return octStr.getOctets();
+ }
+
+ public BigInteger getIterationCount()
+ {
+ return iterationCount.getValue();
+ }
+
+ public BigInteger getKeyLength()
+ {
+ if (keyLength != null)
+ {
+ return keyLength.getValue();
+ }
+
+ return null;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(octStr);
+ v.add(iterationCount);
+
+ if (keyLength != null)
+ {
+ v.add(keyLength);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/PKCS12PBEParams.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/PKCS12PBEParams.java
new file mode 100644
index 00000000..0ddf5c34
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/PKCS12PBEParams.java
@@ -0,0 +1,69 @@
+package org.bouncycastle.asn1.pkcs;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+
+public class PKCS12PBEParams
+ extends ASN1Object
+{
+ ASN1Integer iterations;
+ ASN1OctetString iv;
+
+ public PKCS12PBEParams(
+ byte[] salt,
+ int iterations)
+ {
+ this.iv = new DEROctetString(salt);
+ this.iterations = new ASN1Integer(iterations);
+ }
+
+ private PKCS12PBEParams(
+ ASN1Sequence seq)
+ {
+ iv = (ASN1OctetString)seq.getObjectAt(0);
+ iterations = ASN1Integer.getInstance(seq.getObjectAt(1));
+ }
+
+ public static PKCS12PBEParams getInstance(
+ Object obj)
+ {
+ if (obj instanceof PKCS12PBEParams)
+ {
+ return (PKCS12PBEParams)obj;
+ }
+ else if (obj != null)
+ {
+ return new PKCS12PBEParams(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public BigInteger getIterations()
+ {
+ return iterations.getValue();
+ }
+
+ public byte[] getIV()
+ {
+ return iv.getOctets();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(iv);
+ v.add(iterations);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
new file mode 100644
index 00000000..405d0b4a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
@@ -0,0 +1,258 @@
+package org.bouncycastle.asn1.pkcs;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface PKCSObjectIdentifiers
+{
+ //
+ // pkcs-1 OBJECT IDENTIFIER ::= {
+ // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 }
+ //
+ static final ASN1ObjectIdentifier pkcs_1 = new ASN1ObjectIdentifier("1.2.840.113549.1.1");
+ static final ASN1ObjectIdentifier rsaEncryption = pkcs_1.branch("1");
+ static final ASN1ObjectIdentifier md2WithRSAEncryption = pkcs_1.branch("2");
+ static final ASN1ObjectIdentifier md4WithRSAEncryption = pkcs_1.branch("3");
+ static final ASN1ObjectIdentifier md5WithRSAEncryption = pkcs_1.branch("4");
+ static final ASN1ObjectIdentifier sha1WithRSAEncryption = pkcs_1.branch("5");
+ static final ASN1ObjectIdentifier srsaOAEPEncryptionSET = pkcs_1.branch("6");
+ static final ASN1ObjectIdentifier id_RSAES_OAEP = pkcs_1.branch("7");
+ static final ASN1ObjectIdentifier id_mgf1 = pkcs_1.branch("8");
+ static final ASN1ObjectIdentifier id_pSpecified = pkcs_1.branch("9");
+ static final ASN1ObjectIdentifier id_RSASSA_PSS = pkcs_1.branch("10");
+ static final ASN1ObjectIdentifier sha256WithRSAEncryption = pkcs_1.branch("11");
+ static final ASN1ObjectIdentifier sha384WithRSAEncryption = pkcs_1.branch("12");
+ static final ASN1ObjectIdentifier sha512WithRSAEncryption = pkcs_1.branch("13");
+ static final ASN1ObjectIdentifier sha224WithRSAEncryption = pkcs_1.branch("14");
+
+ //
+ // pkcs-3 OBJECT IDENTIFIER ::= {
+ // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 3 }
+ //
+ static final ASN1ObjectIdentifier pkcs_3 = new ASN1ObjectIdentifier("1.2.840.113549.1.3");
+ static final ASN1ObjectIdentifier dhKeyAgreement = pkcs_3.branch("1");
+
+ //
+ // pkcs-5 OBJECT IDENTIFIER ::= {
+ // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 5 }
+ //
+ static final ASN1ObjectIdentifier pkcs_5 = new ASN1ObjectIdentifier("1.2.840.113549.1.5");
+
+ static final ASN1ObjectIdentifier pbeWithMD2AndDES_CBC = pkcs_5.branch("1");
+ static final ASN1ObjectIdentifier pbeWithMD2AndRC2_CBC = pkcs_5.branch("4");
+ static final ASN1ObjectIdentifier pbeWithMD5AndDES_CBC = pkcs_5.branch("3");
+ static final ASN1ObjectIdentifier pbeWithMD5AndRC2_CBC = pkcs_5.branch("6");
+ static final ASN1ObjectIdentifier pbeWithSHA1AndDES_CBC = pkcs_5.branch("10");
+ static final ASN1ObjectIdentifier pbeWithSHA1AndRC2_CBC = pkcs_5.branch("11");
+
+ static final ASN1ObjectIdentifier id_PBES2 = pkcs_5.branch("13");
+
+ static final ASN1ObjectIdentifier id_PBKDF2 = pkcs_5.branch("12");
+
+ //
+ // encryptionAlgorithm OBJECT IDENTIFIER ::= {
+ // iso(1) member-body(2) us(840) rsadsi(113549) 3 }
+ //
+ static final ASN1ObjectIdentifier encryptionAlgorithm = new ASN1ObjectIdentifier("1.2.840.113549.3");
+
+ static final ASN1ObjectIdentifier des_EDE3_CBC = encryptionAlgorithm.branch("7");
+ static final ASN1ObjectIdentifier RC2_CBC = encryptionAlgorithm.branch("2");
+ static final ASN1ObjectIdentifier rc4 = encryptionAlgorithm.branch("4");
+
+ //
+ // object identifiers for digests
+ //
+ static final ASN1ObjectIdentifier digestAlgorithm = new ASN1ObjectIdentifier("1.2.840.113549.2");
+ //
+ // md2 OBJECT IDENTIFIER ::=
+ // {iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 2}
+ //
+ static final ASN1ObjectIdentifier md2 = digestAlgorithm.branch("2");
+
+ //
+ // md4 OBJECT IDENTIFIER ::=
+ // {iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 4}
+ //
+ static final ASN1ObjectIdentifier md4 = digestAlgorithm.branch("4");
+
+ //
+ // md5 OBJECT IDENTIFIER ::=
+ // {iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 5}
+ //
+ static final ASN1ObjectIdentifier md5 = digestAlgorithm.branch("5");
+
+ static final ASN1ObjectIdentifier id_hmacWithSHA1 = digestAlgorithm.branch("7");
+ static final ASN1ObjectIdentifier id_hmacWithSHA224 = digestAlgorithm.branch("8");
+ static final ASN1ObjectIdentifier id_hmacWithSHA256 = digestAlgorithm.branch("9");
+ static final ASN1ObjectIdentifier id_hmacWithSHA384 = digestAlgorithm.branch("10");
+ static final ASN1ObjectIdentifier id_hmacWithSHA512 = digestAlgorithm.branch("11");
+
+ //
+ // pkcs-7 OBJECT IDENTIFIER ::= {
+ // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 7 }
+ //
+ static final String pkcs_7 = "1.2.840.113549.1.7";
+ static final ASN1ObjectIdentifier data = new ASN1ObjectIdentifier(pkcs_7 + ".1");
+ static final ASN1ObjectIdentifier signedData = new ASN1ObjectIdentifier(pkcs_7 + ".2");
+ static final ASN1ObjectIdentifier envelopedData = new ASN1ObjectIdentifier(pkcs_7 + ".3");
+ static final ASN1ObjectIdentifier signedAndEnvelopedData = new ASN1ObjectIdentifier(pkcs_7 + ".4");
+ static final ASN1ObjectIdentifier digestedData = new ASN1ObjectIdentifier(pkcs_7 + ".5");
+ static final ASN1ObjectIdentifier encryptedData = new ASN1ObjectIdentifier(pkcs_7 + ".6");
+
+ //
+ // pkcs-9 OBJECT IDENTIFIER ::= {
+ // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 }
+ //
+ static final ASN1ObjectIdentifier pkcs_9 = new ASN1ObjectIdentifier("1.2.840.113549.1.9");
+
+ static final ASN1ObjectIdentifier pkcs_9_at_emailAddress = pkcs_9.branch("1");
+ static final ASN1ObjectIdentifier pkcs_9_at_unstructuredName = pkcs_9.branch("2");
+ static final ASN1ObjectIdentifier pkcs_9_at_contentType = pkcs_9.branch("3");
+ static final ASN1ObjectIdentifier pkcs_9_at_messageDigest = pkcs_9.branch("4");
+ static final ASN1ObjectIdentifier pkcs_9_at_signingTime = pkcs_9.branch("5");
+ static final ASN1ObjectIdentifier pkcs_9_at_counterSignature = pkcs_9.branch("6");
+ static final ASN1ObjectIdentifier pkcs_9_at_challengePassword = pkcs_9.branch("7");
+ static final ASN1ObjectIdentifier pkcs_9_at_unstructuredAddress = pkcs_9.branch("8");
+ static final ASN1ObjectIdentifier pkcs_9_at_extendedCertificateAttributes = pkcs_9.branch("9");
+
+ static final ASN1ObjectIdentifier pkcs_9_at_signingDescription = pkcs_9.branch("13");
+ static final ASN1ObjectIdentifier pkcs_9_at_extensionRequest = pkcs_9.branch("14");
+ static final ASN1ObjectIdentifier pkcs_9_at_smimeCapabilities = pkcs_9.branch("15");
+
+ static final ASN1ObjectIdentifier pkcs_9_at_friendlyName = pkcs_9.branch("20");
+ static final ASN1ObjectIdentifier pkcs_9_at_localKeyId = pkcs_9.branch("21");
+
+ /** @deprecated use x509Certificate instead */
+ static final ASN1ObjectIdentifier x509certType = pkcs_9.branch("22.1");
+
+ static final ASN1ObjectIdentifier certTypes = pkcs_9.branch("22");
+ static final ASN1ObjectIdentifier x509Certificate = certTypes.branch("1");
+ static final ASN1ObjectIdentifier sdsiCertificate = certTypes.branch("2");
+
+ static final ASN1ObjectIdentifier crlTypes = pkcs_9.branch("23");
+ static final ASN1ObjectIdentifier x509Crl = crlTypes.branch("1");
+
+ static final ASN1ObjectIdentifier id_alg_PWRI_KEK = pkcs_9.branch("16.3.9");
+
+ //
+ // SMIME capability sub oids.
+ //
+ static final ASN1ObjectIdentifier preferSignedData = pkcs_9.branch("15.1");
+ static final ASN1ObjectIdentifier canNotDecryptAny = pkcs_9.branch("15.2");
+ static final ASN1ObjectIdentifier sMIMECapabilitiesVersions = pkcs_9.branch("15.3");
+
+ //
+ // id-ct OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840)
+ // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) ct(1)}
+ //
+ static final ASN1ObjectIdentifier id_ct = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.1");
+
+ static final ASN1ObjectIdentifier id_ct_authData = id_ct.branch("2");
+ static final ASN1ObjectIdentifier id_ct_TSTInfo = id_ct.branch("4");
+ static final ASN1ObjectIdentifier id_ct_compressedData = id_ct.branch("9");
+ static final ASN1ObjectIdentifier id_ct_authEnvelopedData = id_ct.branch("23");
+ static final ASN1ObjectIdentifier id_ct_timestampedData = id_ct.branch("31");
+
+ //
+ // id-cti OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840)
+ // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) cti(6)}
+ //
+ static final ASN1ObjectIdentifier id_cti = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.6");
+
+ static final ASN1ObjectIdentifier id_cti_ets_proofOfOrigin = id_cti.branch("1");
+ static final ASN1ObjectIdentifier id_cti_ets_proofOfReceipt = id_cti.branch("2");
+ static final ASN1ObjectIdentifier id_cti_ets_proofOfDelivery = id_cti.branch("3");
+ static final ASN1ObjectIdentifier id_cti_ets_proofOfSender = id_cti.branch("4");
+ static final ASN1ObjectIdentifier id_cti_ets_proofOfApproval = id_cti.branch("5");
+ static final ASN1ObjectIdentifier id_cti_ets_proofOfCreation = id_cti.branch("6");
+
+ //
+ // id-aa OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840)
+ // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) attributes(2)}
+ //
+ static final ASN1ObjectIdentifier id_aa = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.2");
+
+
+ static final ASN1ObjectIdentifier id_aa_receiptRequest = id_aa.branch("1");
+
+ static final ASN1ObjectIdentifier id_aa_contentHint = id_aa.branch("4"); // See RFC 2634
+ static final ASN1ObjectIdentifier id_aa_msgSigDigest = id_aa.branch("5");
+ static final ASN1ObjectIdentifier id_aa_contentReference = id_aa.branch("10");
+ /*
+ * id-aa-encrypKeyPref OBJECT IDENTIFIER ::= {id-aa 11}
+ *
+ */
+ static final ASN1ObjectIdentifier id_aa_encrypKeyPref = id_aa.branch("11");
+ static final ASN1ObjectIdentifier id_aa_signingCertificate = id_aa.branch("12");
+ static final ASN1ObjectIdentifier id_aa_signingCertificateV2 = id_aa.branch("47");
+
+ static final ASN1ObjectIdentifier id_aa_contentIdentifier = id_aa.branch("7"); // See RFC 2634
+
+ /*
+ * RFC 3126
+ */
+ static final ASN1ObjectIdentifier id_aa_signatureTimeStampToken = id_aa.branch("14");
+
+ static final ASN1ObjectIdentifier id_aa_ets_sigPolicyId = id_aa.branch("15");
+ static final ASN1ObjectIdentifier id_aa_ets_commitmentType = id_aa.branch("16");
+ static final ASN1ObjectIdentifier id_aa_ets_signerLocation = id_aa.branch("17");
+ static final ASN1ObjectIdentifier id_aa_ets_signerAttr = id_aa.branch("18");
+ static final ASN1ObjectIdentifier id_aa_ets_otherSigCert = id_aa.branch("19");
+ static final ASN1ObjectIdentifier id_aa_ets_contentTimestamp = id_aa.branch("20");
+ static final ASN1ObjectIdentifier id_aa_ets_certificateRefs = id_aa.branch("21");
+ static final ASN1ObjectIdentifier id_aa_ets_revocationRefs = id_aa.branch("22");
+ static final ASN1ObjectIdentifier id_aa_ets_certValues = id_aa.branch("23");
+ static final ASN1ObjectIdentifier id_aa_ets_revocationValues = id_aa.branch("24");
+ static final ASN1ObjectIdentifier id_aa_ets_escTimeStamp = id_aa.branch("25");
+ static final ASN1ObjectIdentifier id_aa_ets_certCRLTimestamp = id_aa.branch("26");
+ static final ASN1ObjectIdentifier id_aa_ets_archiveTimestamp = id_aa.branch("27");
+
+ /** @deprecated use id_aa_ets_sigPolicyId instead */
+ static final ASN1ObjectIdentifier id_aa_sigPolicyId = id_aa_ets_sigPolicyId;
+ /** @deprecated use id_aa_ets_commitmentType instead */
+ static final ASN1ObjectIdentifier id_aa_commitmentType = id_aa_ets_commitmentType;
+ /** @deprecated use id_aa_ets_signerLocation instead */
+ static final ASN1ObjectIdentifier id_aa_signerLocation = id_aa_ets_signerLocation;
+ /** @deprecated use id_aa_ets_otherSigCert instead */
+ static final ASN1ObjectIdentifier id_aa_otherSigCert = id_aa_ets_otherSigCert;
+
+ //
+ // id-spq OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840)
+ // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) id-spq(5)}
+ //
+ final String id_spq = "1.2.840.113549.1.9.16.5";
+
+ static final ASN1ObjectIdentifier id_spq_ets_uri = new ASN1ObjectIdentifier(id_spq + ".1");
+ static final ASN1ObjectIdentifier id_spq_ets_unotice = new ASN1ObjectIdentifier(id_spq + ".2");
+
+ //
+ // pkcs-12 OBJECT IDENTIFIER ::= {
+ // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 12 }
+ //
+ static final ASN1ObjectIdentifier pkcs_12 = new ASN1ObjectIdentifier("1.2.840.113549.1.12");
+ static final ASN1ObjectIdentifier bagtypes = pkcs_12.branch("10.1");
+
+ static final ASN1ObjectIdentifier keyBag = bagtypes.branch("1");
+ static final ASN1ObjectIdentifier pkcs8ShroudedKeyBag = bagtypes.branch("2");
+ static final ASN1ObjectIdentifier certBag = bagtypes.branch("3");
+ static final ASN1ObjectIdentifier crlBag = bagtypes.branch("4");
+ static final ASN1ObjectIdentifier secretBag = bagtypes.branch("5");
+ static final ASN1ObjectIdentifier safeContentsBag = bagtypes.branch("6");
+
+ static final ASN1ObjectIdentifier pkcs_12PbeIds = pkcs_12.branch("1");
+
+ static final ASN1ObjectIdentifier pbeWithSHAAnd128BitRC4 = pkcs_12PbeIds.branch("1");
+ static final ASN1ObjectIdentifier pbeWithSHAAnd40BitRC4 = pkcs_12PbeIds.branch("2");
+ static final ASN1ObjectIdentifier pbeWithSHAAnd3_KeyTripleDES_CBC = pkcs_12PbeIds.branch("3");
+ static final ASN1ObjectIdentifier pbeWithSHAAnd2_KeyTripleDES_CBC = pkcs_12PbeIds.branch("4");
+ static final ASN1ObjectIdentifier pbeWithSHAAnd128BitRC2_CBC = pkcs_12PbeIds.branch("5");
+ static final ASN1ObjectIdentifier pbeWithSHAAnd40BitRC2_CBC = pkcs_12PbeIds.branch("6");
+
+ /**
+ * @deprecated use pbeWithSHAAnd40BitRC2_CBC
+ */
+ static final ASN1ObjectIdentifier pbewithSHAAnd40BitRC2_CBC = pkcs_12PbeIds.branch("6");
+
+ static final ASN1ObjectIdentifier id_alg_CMS3DESwrap = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.6");
+ static final ASN1ObjectIdentifier id_alg_CMSRC2wrap = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.7");
+}
+
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/Pfx.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/Pfx.java
new file mode 100644
index 00000000..7885a795
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/Pfx.java
@@ -0,0 +1,87 @@
+package org.bouncycastle.asn1.pkcs;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.BERSequence;
+
+/**
+ * the infamous Pfx from PKCS12
+ */
+public class Pfx
+ extends ASN1Object
+ implements PKCSObjectIdentifiers
+{
+ private ContentInfo contentInfo;
+ private MacData macData = null;
+
+ private Pfx(
+ ASN1Sequence seq)
+ {
+ BigInteger version = ((ASN1Integer)seq.getObjectAt(0)).getValue();
+ if (version.intValue() != 3)
+ {
+ throw new IllegalArgumentException("wrong version for PFX PDU");
+ }
+
+ contentInfo = ContentInfo.getInstance(seq.getObjectAt(1));
+
+ if (seq.size() == 3)
+ {
+ macData = MacData.getInstance(seq.getObjectAt(2));
+ }
+ }
+
+ public static Pfx getInstance(
+ Object obj)
+ {
+ if (obj instanceof Pfx)
+ {
+ return (Pfx)obj;
+ }
+
+ if (obj != null)
+ {
+ return new Pfx(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public Pfx(
+ ContentInfo contentInfo,
+ MacData macData)
+ {
+ this.contentInfo = contentInfo;
+ this.macData = macData;
+ }
+
+ public ContentInfo getAuthSafe()
+ {
+ return contentInfo;
+ }
+
+ public MacData getMacData()
+ {
+ return macData;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new ASN1Integer(3));
+ v.add(contentInfo);
+
+ if (macData != null)
+ {
+ v.add(macData);
+ }
+
+ return new BERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java
new file mode 100644
index 00000000..dad86502
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java
@@ -0,0 +1,164 @@
+package org.bouncycastle.asn1.pkcs;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class PrivateKeyInfo
+ extends ASN1Object
+{
+ private ASN1OctetString privKey;
+ private AlgorithmIdentifier algId;
+ private ASN1Set attributes;
+
+ public static PrivateKeyInfo getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static PrivateKeyInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof PrivateKeyInfo)
+ {
+ return (PrivateKeyInfo)obj;
+ }
+ else if (obj != null)
+ {
+ return new PrivateKeyInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public PrivateKeyInfo(
+ AlgorithmIdentifier algId,
+ ASN1Encodable privateKey)
+ throws IOException
+ {
+ this(algId, privateKey, null);
+ }
+
+ public PrivateKeyInfo(
+ AlgorithmIdentifier algId,
+ ASN1Encodable privateKey,
+ ASN1Set attributes)
+ throws IOException
+ {
+ this.privKey = new DEROctetString(privateKey.toASN1Primitive().getEncoded(ASN1Encoding.DER));
+ this.algId = algId;
+ this.attributes = attributes;
+ }
+
+ /**
+ * @deprectaed use PrivateKeyInfo.getInstance()
+ * @param seq
+ */
+ public PrivateKeyInfo(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ BigInteger version = ((ASN1Integer)e.nextElement()).getValue();
+ if (version.intValue() != 0)
+ {
+ throw new IllegalArgumentException("wrong version for private key info");
+ }
+
+ algId = AlgorithmIdentifier.getInstance(e.nextElement());
+ privKey = ASN1OctetString.getInstance(e.nextElement());
+
+ if (e.hasMoreElements())
+ {
+ attributes = ASN1Set.getInstance((ASN1TaggedObject)e.nextElement(), false);
+ }
+ }
+
+ public AlgorithmIdentifier getPrivateKeyAlgorithm()
+ {
+ return algId;
+ }
+ /**
+ * @deprecated use getPrivateKeyAlgorithm()
+ */
+ public AlgorithmIdentifier getAlgorithmId()
+ {
+ return algId;
+ }
+
+ public ASN1Encodable parsePrivateKey()
+ throws IOException
+ {
+ return ASN1Primitive.fromByteArray(privKey.getOctets());
+ }
+
+ /**
+ * @deprecated use parsePrivateKey()
+ */
+ public ASN1Primitive getPrivateKey()
+ {
+ try
+ {
+ return parsePrivateKey().toASN1Primitive();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("unable to parse private key");
+ }
+ }
+
+ public ASN1Set getAttributes()
+ {
+ return attributes;
+ }
+
+ /**
+ * write out an RSA private key with its associated information
+ * as described in PKCS8.
+ * <pre>
+ * PrivateKeyInfo ::= SEQUENCE {
+ * version Version,
+ * privateKeyAlgorithm AlgorithmIdentifier {{PrivateKeyAlgorithms}},
+ * privateKey PrivateKey,
+ * attributes [0] IMPLICIT Attributes OPTIONAL
+ * }
+ * Version ::= INTEGER {v1(0)} (v1,...)
+ *
+ * PrivateKey ::= OCTET STRING
+ *
+ * Attributes ::= SET OF Attribute
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new ASN1Integer(0));
+ v.add(algId);
+ v.add(privKey);
+
+ if (attributes != null)
+ {
+ v.add(new DERTaggedObject(false, 0, attributes));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/RC2CBCParameter.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/RC2CBCParameter.java
new file mode 100644
index 00000000..0a116f71
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/RC2CBCParameter.java
@@ -0,0 +1,93 @@
+package org.bouncycastle.asn1.pkcs;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+
+public class RC2CBCParameter
+ extends ASN1Object
+{
+ ASN1Integer version;
+ ASN1OctetString iv;
+
+ public static RC2CBCParameter getInstance(
+ Object o)
+ {
+ if (o instanceof RC2CBCParameter)
+ {
+ return (RC2CBCParameter)o;
+ }
+ if (o != null)
+ {
+ return new RC2CBCParameter(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public RC2CBCParameter(
+ byte[] iv)
+ {
+ this.version = null;
+ this.iv = new DEROctetString(iv);
+ }
+
+ public RC2CBCParameter(
+ int parameterVersion,
+ byte[] iv)
+ {
+ this.version = new ASN1Integer(parameterVersion);
+ this.iv = new DEROctetString(iv);
+ }
+
+ private RC2CBCParameter(
+ ASN1Sequence seq)
+ {
+ if (seq.size() == 1)
+ {
+ version = null;
+ iv = (ASN1OctetString)seq.getObjectAt(0);
+ }
+ else
+ {
+ version = (ASN1Integer)seq.getObjectAt(0);
+ iv = (ASN1OctetString)seq.getObjectAt(1);
+ }
+ }
+
+ public BigInteger getRC2ParameterVersion()
+ {
+ if (version == null)
+ {
+ return null;
+ }
+
+ return version.getValue();
+ }
+
+ public byte[] getIV()
+ {
+ return iv.getOctets();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (version != null)
+ {
+ v.add(version);
+ }
+
+ v.add(iv);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java
new file mode 100644
index 00000000..515b515a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java
@@ -0,0 +1,151 @@
+package org.bouncycastle.asn1.pkcs;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class RSAESOAEPparams
+ extends ASN1Object
+{
+ private AlgorithmIdentifier hashAlgorithm;
+ private AlgorithmIdentifier maskGenAlgorithm;
+ private AlgorithmIdentifier pSourceAlgorithm;
+
+ public final static AlgorithmIdentifier DEFAULT_HASH_ALGORITHM = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE);
+ public final static AlgorithmIdentifier DEFAULT_MASK_GEN_FUNCTION = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, DEFAULT_HASH_ALGORITHM);
+ public final static AlgorithmIdentifier DEFAULT_P_SOURCE_ALGORITHM = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_pSpecified, new DEROctetString(new byte[0]));
+
+ public static RSAESOAEPparams getInstance(
+ Object obj)
+ {
+ if (obj instanceof RSAESOAEPparams)
+ {
+ return (RSAESOAEPparams)obj;
+ }
+ else if (obj != null)
+ {
+ return new RSAESOAEPparams(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * The default version
+ */
+ public RSAESOAEPparams()
+ {
+ hashAlgorithm = DEFAULT_HASH_ALGORITHM;
+ maskGenAlgorithm = DEFAULT_MASK_GEN_FUNCTION;
+ pSourceAlgorithm = DEFAULT_P_SOURCE_ALGORITHM;
+ }
+
+ public RSAESOAEPparams(
+ AlgorithmIdentifier hashAlgorithm,
+ AlgorithmIdentifier maskGenAlgorithm,
+ AlgorithmIdentifier pSourceAlgorithm)
+ {
+ this.hashAlgorithm = hashAlgorithm;
+ this.maskGenAlgorithm = maskGenAlgorithm;
+ this.pSourceAlgorithm = pSourceAlgorithm;
+ }
+
+ public RSAESOAEPparams(
+ ASN1Sequence seq)
+ {
+ hashAlgorithm = DEFAULT_HASH_ALGORITHM;
+ maskGenAlgorithm = DEFAULT_MASK_GEN_FUNCTION;
+ pSourceAlgorithm = DEFAULT_P_SOURCE_ALGORITHM;
+
+ for (int i = 0; i != seq.size(); i++)
+ {
+ ASN1TaggedObject o = (ASN1TaggedObject)seq.getObjectAt(i);
+
+ switch (o.getTagNo())
+ {
+ case 0:
+ hashAlgorithm = AlgorithmIdentifier.getInstance(o, true);
+ break;
+ case 1:
+ maskGenAlgorithm = AlgorithmIdentifier.getInstance(o, true);
+ break;
+ case 2:
+ pSourceAlgorithm = AlgorithmIdentifier.getInstance(o, true);
+ break;
+ default:
+ throw new IllegalArgumentException("unknown tag");
+ }
+ }
+ }
+
+ public AlgorithmIdentifier getHashAlgorithm()
+ {
+ return hashAlgorithm;
+ }
+
+ public AlgorithmIdentifier getMaskGenAlgorithm()
+ {
+ return maskGenAlgorithm;
+ }
+
+ public AlgorithmIdentifier getPSourceAlgorithm()
+ {
+ return pSourceAlgorithm;
+ }
+
+ /**
+ * <pre>
+ * RSAES-OAEP-params ::= SEQUENCE {
+ * hashAlgorithm [0] OAEP-PSSDigestAlgorithms DEFAULT sha1,
+ * maskGenAlgorithm [1] PKCS1MGFAlgorithms DEFAULT mgf1SHA1,
+ * pSourceAlgorithm [2] PKCS1PSourceAlgorithms DEFAULT pSpecifiedEmpty
+ * }
+ *
+ * OAEP-PSSDigestAlgorithms ALGORITHM-IDENTIFIER ::= {
+ * { OID id-sha1 PARAMETERS NULL }|
+ * { OID id-sha256 PARAMETERS NULL }|
+ * { OID id-sha384 PARAMETERS NULL }|
+ * { OID id-sha512 PARAMETERS NULL },
+ * ... -- Allows for future expansion --
+ * }
+ * PKCS1MGFAlgorithms ALGORITHM-IDENTIFIER ::= {
+ * { OID id-mgf1 PARAMETERS OAEP-PSSDigestAlgorithms },
+ * ... -- Allows for future expansion --
+ * }
+ * PKCS1PSourceAlgorithms ALGORITHM-IDENTIFIER ::= {
+ * { OID id-pSpecified PARAMETERS OCTET STRING },
+ * ... -- Allows for future expansion --
+ * }
+ * </pre>
+ * @return the asn1 primitive representing the parameters.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (!hashAlgorithm.equals(DEFAULT_HASH_ALGORITHM))
+ {
+ v.add(new DERTaggedObject(true, 0, hashAlgorithm));
+ }
+
+ if (!maskGenAlgorithm.equals(DEFAULT_MASK_GEN_FUNCTION))
+ {
+ v.add(new DERTaggedObject(true, 1, maskGenAlgorithm));
+ }
+
+ if (!pSourceAlgorithm.equals(DEFAULT_P_SOURCE_ALGORITHM))
+ {
+ v.add(new DERTaggedObject(true, 2, pSourceAlgorithm));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/RSAPrivateKey.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/RSAPrivateKey.java
new file mode 100644
index 00000000..36992cf5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/RSAPrivateKey.java
@@ -0,0 +1,187 @@
+package org.bouncycastle.asn1.pkcs;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+public class RSAPrivateKey
+ extends ASN1Object
+{
+ private BigInteger version;
+ private BigInteger modulus;
+ private BigInteger publicExponent;
+ private BigInteger privateExponent;
+ private BigInteger prime1;
+ private BigInteger prime2;
+ private BigInteger exponent1;
+ private BigInteger exponent2;
+ private BigInteger coefficient;
+ private ASN1Sequence otherPrimeInfos = null;
+
+ public static RSAPrivateKey getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static RSAPrivateKey getInstance(
+ Object obj)
+ {
+ if (obj instanceof RSAPrivateKey)
+ {
+ return (RSAPrivateKey)obj;
+ }
+
+ if (obj != null)
+ {
+ return new RSAPrivateKey(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public RSAPrivateKey(
+ BigInteger modulus,
+ BigInteger publicExponent,
+ BigInteger privateExponent,
+ BigInteger prime1,
+ BigInteger prime2,
+ BigInteger exponent1,
+ BigInteger exponent2,
+ BigInteger coefficient)
+ {
+ this.version = BigInteger.valueOf(0);
+ this.modulus = modulus;
+ this.publicExponent = publicExponent;
+ this.privateExponent = privateExponent;
+ this.prime1 = prime1;
+ this.prime2 = prime2;
+ this.exponent1 = exponent1;
+ this.exponent2 = exponent2;
+ this.coefficient = coefficient;
+ }
+
+ private RSAPrivateKey(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ BigInteger v = ((ASN1Integer)e.nextElement()).getValue();
+ if (v.intValue() != 0 && v.intValue() != 1)
+ {
+ throw new IllegalArgumentException("wrong version for RSA private key");
+ }
+
+ version = v;
+ modulus = ((ASN1Integer)e.nextElement()).getValue();
+ publicExponent = ((ASN1Integer)e.nextElement()).getValue();
+ privateExponent = ((ASN1Integer)e.nextElement()).getValue();
+ prime1 = ((ASN1Integer)e.nextElement()).getValue();
+ prime2 = ((ASN1Integer)e.nextElement()).getValue();
+ exponent1 = ((ASN1Integer)e.nextElement()).getValue();
+ exponent2 = ((ASN1Integer)e.nextElement()).getValue();
+ coefficient = ((ASN1Integer)e.nextElement()).getValue();
+
+ if (e.hasMoreElements())
+ {
+ otherPrimeInfos = (ASN1Sequence)e.nextElement();
+ }
+ }
+
+ public BigInteger getVersion()
+ {
+ return version;
+ }
+
+ public BigInteger getModulus()
+ {
+ return modulus;
+ }
+
+ public BigInteger getPublicExponent()
+ {
+ return publicExponent;
+ }
+
+ public BigInteger getPrivateExponent()
+ {
+ return privateExponent;
+ }
+
+ public BigInteger getPrime1()
+ {
+ return prime1;
+ }
+
+ public BigInteger getPrime2()
+ {
+ return prime2;
+ }
+
+ public BigInteger getExponent1()
+ {
+ return exponent1;
+ }
+
+ public BigInteger getExponent2()
+ {
+ return exponent2;
+ }
+
+ public BigInteger getCoefficient()
+ {
+ return coefficient;
+ }
+
+ /**
+ * This outputs the key in PKCS1v2 format.
+ * <pre>
+ * RSAPrivateKey ::= SEQUENCE {
+ * version Version,
+ * modulus INTEGER, -- n
+ * publicExponent INTEGER, -- e
+ * privateExponent INTEGER, -- d
+ * prime1 INTEGER, -- p
+ * prime2 INTEGER, -- q
+ * exponent1 INTEGER, -- d mod (p-1)
+ * exponent2 INTEGER, -- d mod (q-1)
+ * coefficient INTEGER, -- (inverse of q) mod p
+ * otherPrimeInfos OtherPrimeInfos OPTIONAL
+ * }
+ *
+ * Version ::= INTEGER { two-prime(0), multi(1) }
+ * (CONSTRAINED BY {-- version must be multi if otherPrimeInfos present --})
+ * </pre>
+ * <p>
+ * This routine is written to output PKCS1 version 2.1, private keys.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new ASN1Integer(version)); // version
+ v.add(new ASN1Integer(getModulus()));
+ v.add(new ASN1Integer(getPublicExponent()));
+ v.add(new ASN1Integer(getPrivateExponent()));
+ v.add(new ASN1Integer(getPrime1()));
+ v.add(new ASN1Integer(getPrime2()));
+ v.add(new ASN1Integer(getExponent1()));
+ v.add(new ASN1Integer(getExponent2()));
+ v.add(new ASN1Integer(getCoefficient()));
+
+ if (otherPrimeInfos != null)
+ {
+ v.add(otherPrimeInfos);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/RSAPrivateKeyStructure.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/RSAPrivateKeyStructure.java
new file mode 100644
index 00000000..5912d5ea
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/RSAPrivateKeyStructure.java
@@ -0,0 +1,189 @@
+package org.bouncycastle.asn1.pkcs;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * @deprecated use RSAPrivateKey
+ */
+public class RSAPrivateKeyStructure
+ extends ASN1Object
+{
+ private int version;
+ private BigInteger modulus;
+ private BigInteger publicExponent;
+ private BigInteger privateExponent;
+ private BigInteger prime1;
+ private BigInteger prime2;
+ private BigInteger exponent1;
+ private BigInteger exponent2;
+ private BigInteger coefficient;
+ private ASN1Sequence otherPrimeInfos = null;
+
+ public static RSAPrivateKeyStructure getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static RSAPrivateKeyStructure getInstance(
+ Object obj)
+ {
+ if (obj instanceof RSAPrivateKeyStructure)
+ {
+ return (RSAPrivateKeyStructure)obj;
+ }
+ else if (obj instanceof ASN1Sequence)
+ {
+ return new RSAPrivateKeyStructure((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
+ }
+
+ public RSAPrivateKeyStructure(
+ BigInteger modulus,
+ BigInteger publicExponent,
+ BigInteger privateExponent,
+ BigInteger prime1,
+ BigInteger prime2,
+ BigInteger exponent1,
+ BigInteger exponent2,
+ BigInteger coefficient)
+ {
+ this.version = 0;
+ this.modulus = modulus;
+ this.publicExponent = publicExponent;
+ this.privateExponent = privateExponent;
+ this.prime1 = prime1;
+ this.prime2 = prime2;
+ this.exponent1 = exponent1;
+ this.exponent2 = exponent2;
+ this.coefficient = coefficient;
+ }
+
+ public RSAPrivateKeyStructure(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ BigInteger v = ((ASN1Integer)e.nextElement()).getValue();
+ if (v.intValue() != 0 && v.intValue() != 1)
+ {
+ throw new IllegalArgumentException("wrong version for RSA private key");
+ }
+
+ version = v.intValue();
+ modulus = ((ASN1Integer)e.nextElement()).getValue();
+ publicExponent = ((ASN1Integer)e.nextElement()).getValue();
+ privateExponent = ((ASN1Integer)e.nextElement()).getValue();
+ prime1 = ((ASN1Integer)e.nextElement()).getValue();
+ prime2 = ((ASN1Integer)e.nextElement()).getValue();
+ exponent1 = ((ASN1Integer)e.nextElement()).getValue();
+ exponent2 = ((ASN1Integer)e.nextElement()).getValue();
+ coefficient = ((ASN1Integer)e.nextElement()).getValue();
+
+ if (e.hasMoreElements())
+ {
+ otherPrimeInfos = (ASN1Sequence)e.nextElement();
+ }
+ }
+
+ public int getVersion()
+ {
+ return version;
+ }
+
+ public BigInteger getModulus()
+ {
+ return modulus;
+ }
+
+ public BigInteger getPublicExponent()
+ {
+ return publicExponent;
+ }
+
+ public BigInteger getPrivateExponent()
+ {
+ return privateExponent;
+ }
+
+ public BigInteger getPrime1()
+ {
+ return prime1;
+ }
+
+ public BigInteger getPrime2()
+ {
+ return prime2;
+ }
+
+ public BigInteger getExponent1()
+ {
+ return exponent1;
+ }
+
+ public BigInteger getExponent2()
+ {
+ return exponent2;
+ }
+
+ public BigInteger getCoefficient()
+ {
+ return coefficient;
+ }
+
+ /**
+ * This outputs the key in PKCS1v2 format.
+ * <pre>
+ * RSAPrivateKey ::= SEQUENCE {
+ * version Version,
+ * modulus INTEGER, -- n
+ * publicExponent INTEGER, -- e
+ * privateExponent INTEGER, -- d
+ * prime1 INTEGER, -- p
+ * prime2 INTEGER, -- q
+ * exponent1 INTEGER, -- d mod (p-1)
+ * exponent2 INTEGER, -- d mod (q-1)
+ * coefficient INTEGER, -- (inverse of q) mod p
+ * otherPrimeInfos OtherPrimeInfos OPTIONAL
+ * }
+ *
+ * Version ::= INTEGER { two-prime(0), multi(1) }
+ * (CONSTRAINED BY {-- version must be multi if otherPrimeInfos present --})
+ * </pre>
+ * <p>
+ * This routine is written to output PKCS1 version 2.1, private keys.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new ASN1Integer(version)); // version
+ v.add(new ASN1Integer(getModulus()));
+ v.add(new ASN1Integer(getPublicExponent()));
+ v.add(new ASN1Integer(getPrivateExponent()));
+ v.add(new ASN1Integer(getPrime1()));
+ v.add(new ASN1Integer(getPrime2()));
+ v.add(new ASN1Integer(getExponent1()));
+ v.add(new ASN1Integer(getExponent2()));
+ v.add(new ASN1Integer(getCoefficient()));
+
+ if (otherPrimeInfos != null)
+ {
+ v.add(otherPrimeInfos);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/RSAPublicKey.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/RSAPublicKey.java
new file mode 100644
index 00000000..6c432985
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/RSAPublicKey.java
@@ -0,0 +1,95 @@
+package org.bouncycastle.asn1.pkcs;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+public class RSAPublicKey
+ extends ASN1Object
+{
+ private BigInteger modulus;
+ private BigInteger publicExponent;
+
+ public static RSAPublicKey getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static RSAPublicKey getInstance(
+ Object obj)
+ {
+ if (obj instanceof RSAPublicKey)
+ {
+ return (RSAPublicKey)obj;
+ }
+
+ if (obj != null)
+ {
+ return new RSAPublicKey(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public RSAPublicKey(
+ BigInteger modulus,
+ BigInteger publicExponent)
+ {
+ this.modulus = modulus;
+ this.publicExponent = publicExponent;
+ }
+
+ private RSAPublicKey(
+ ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ Enumeration e = seq.getObjects();
+
+ modulus = ASN1Integer.getInstance(e.nextElement()).getPositiveValue();
+ publicExponent = ASN1Integer.getInstance(e.nextElement()).getPositiveValue();
+ }
+
+ public BigInteger getModulus()
+ {
+ return modulus;
+ }
+
+ public BigInteger getPublicExponent()
+ {
+ return publicExponent;
+ }
+
+ /**
+ * This outputs the key in PKCS1v2 format.
+ * <pre>
+ * RSAPublicKey ::= SEQUENCE {
+ * modulus INTEGER, -- n
+ * publicExponent INTEGER, -- e
+ * }
+ * </pre>
+ * <p>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new ASN1Integer(getModulus()));
+ v.add(new ASN1Integer(getPublicExponent()));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/RSASSAPSSparams.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/RSASSAPSSparams.java
new file mode 100644
index 00000000..dc91c9c1
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/RSASSAPSSparams.java
@@ -0,0 +1,172 @@
+package org.bouncycastle.asn1.pkcs;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class RSASSAPSSparams
+ extends ASN1Object
+{
+ private AlgorithmIdentifier hashAlgorithm;
+ private AlgorithmIdentifier maskGenAlgorithm;
+ private ASN1Integer saltLength;
+ private ASN1Integer trailerField;
+
+ public final static AlgorithmIdentifier DEFAULT_HASH_ALGORITHM = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE);
+ public final static AlgorithmIdentifier DEFAULT_MASK_GEN_FUNCTION = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, DEFAULT_HASH_ALGORITHM);
+ public final static ASN1Integer DEFAULT_SALT_LENGTH = new ASN1Integer(20);
+ public final static ASN1Integer DEFAULT_TRAILER_FIELD = new ASN1Integer(1);
+
+ public static RSASSAPSSparams getInstance(
+ Object obj)
+ {
+ if (obj instanceof RSASSAPSSparams)
+ {
+ return (RSASSAPSSparams)obj;
+ }
+ else if (obj != null)
+ {
+ return new RSASSAPSSparams(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * The default version
+ */
+ public RSASSAPSSparams()
+ {
+ hashAlgorithm = DEFAULT_HASH_ALGORITHM;
+ maskGenAlgorithm = DEFAULT_MASK_GEN_FUNCTION;
+ saltLength = DEFAULT_SALT_LENGTH;
+ trailerField = DEFAULT_TRAILER_FIELD;
+ }
+
+ public RSASSAPSSparams(
+ AlgorithmIdentifier hashAlgorithm,
+ AlgorithmIdentifier maskGenAlgorithm,
+ ASN1Integer saltLength,
+ ASN1Integer trailerField)
+ {
+ this.hashAlgorithm = hashAlgorithm;
+ this.maskGenAlgorithm = maskGenAlgorithm;
+ this.saltLength = saltLength;
+ this.trailerField = trailerField;
+ }
+
+ private RSASSAPSSparams(
+ ASN1Sequence seq)
+ {
+ hashAlgorithm = DEFAULT_HASH_ALGORITHM;
+ maskGenAlgorithm = DEFAULT_MASK_GEN_FUNCTION;
+ saltLength = DEFAULT_SALT_LENGTH;
+ trailerField = DEFAULT_TRAILER_FIELD;
+
+ for (int i = 0; i != seq.size(); i++)
+ {
+ ASN1TaggedObject o = (ASN1TaggedObject)seq.getObjectAt(i);
+
+ switch (o.getTagNo())
+ {
+ case 0:
+ hashAlgorithm = AlgorithmIdentifier.getInstance(o, true);
+ break;
+ case 1:
+ maskGenAlgorithm = AlgorithmIdentifier.getInstance(o, true);
+ break;
+ case 2:
+ saltLength = ASN1Integer.getInstance(o, true);
+ break;
+ case 3:
+ trailerField = ASN1Integer.getInstance(o, true);
+ break;
+ default:
+ throw new IllegalArgumentException("unknown tag");
+ }
+ }
+ }
+
+ public AlgorithmIdentifier getHashAlgorithm()
+ {
+ return hashAlgorithm;
+ }
+
+ public AlgorithmIdentifier getMaskGenAlgorithm()
+ {
+ return maskGenAlgorithm;
+ }
+
+ public BigInteger getSaltLength()
+ {
+ return saltLength.getValue();
+ }
+
+ public BigInteger getTrailerField()
+ {
+ return trailerField.getValue();
+ }
+
+ /**
+ * <pre>
+ * RSASSA-PSS-params ::= SEQUENCE {
+ * hashAlgorithm [0] OAEP-PSSDigestAlgorithms DEFAULT sha1,
+ * maskGenAlgorithm [1] PKCS1MGFAlgorithms DEFAULT mgf1SHA1,
+ * saltLength [2] INTEGER DEFAULT 20,
+ * trailerField [3] TrailerField DEFAULT trailerFieldBC
+ * }
+ *
+ * OAEP-PSSDigestAlgorithms ALGORITHM-IDENTIFIER ::= {
+ * { OID id-sha1 PARAMETERS NULL }|
+ * { OID id-sha256 PARAMETERS NULL }|
+ * { OID id-sha384 PARAMETERS NULL }|
+ * { OID id-sha512 PARAMETERS NULL },
+ * ... -- Allows for future expansion --
+ * }
+ *
+ * PKCS1MGFAlgorithms ALGORITHM-IDENTIFIER ::= {
+ * { OID id-mgf1 PARAMETERS OAEP-PSSDigestAlgorithms },
+ * ... -- Allows for future expansion --
+ * }
+ *
+ * TrailerField ::= INTEGER { trailerFieldBC(1) }
+ * </pre>
+ * @return the asn1 primitive representing the parameters.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (!hashAlgorithm.equals(DEFAULT_HASH_ALGORITHM))
+ {
+ v.add(new DERTaggedObject(true, 0, hashAlgorithm));
+ }
+
+ if (!maskGenAlgorithm.equals(DEFAULT_MASK_GEN_FUNCTION))
+ {
+ v.add(new DERTaggedObject(true, 1, maskGenAlgorithm));
+ }
+
+ if (!saltLength.equals(DEFAULT_SALT_LENGTH))
+ {
+ v.add(new DERTaggedObject(true, 2, saltLength));
+ }
+
+ if (!trailerField.equals(DEFAULT_TRAILER_FIELD))
+ {
+ v.add(new DERTaggedObject(true, 3, trailerField));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/SafeBag.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/SafeBag.java
new file mode 100644
index 00000000..00ca0a20
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/SafeBag.java
@@ -0,0 +1,96 @@
+package org.bouncycastle.asn1.pkcs;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DLSequence;
+import org.bouncycastle.asn1.DLTaggedObject;
+
+public class SafeBag
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier bagId;
+ private ASN1Encodable bagValue;
+ private ASN1Set bagAttributes;
+
+ public SafeBag(
+ ASN1ObjectIdentifier oid,
+ ASN1Encodable obj)
+ {
+ this.bagId = oid;
+ this.bagValue = obj;
+ this.bagAttributes = null;
+ }
+
+ public SafeBag(
+ ASN1ObjectIdentifier oid,
+ ASN1Encodable obj,
+ ASN1Set bagAttributes)
+ {
+ this.bagId = oid;
+ this.bagValue = obj;
+ this.bagAttributes = bagAttributes;
+ }
+
+ public static SafeBag getInstance(
+ Object obj)
+ {
+ if (obj instanceof SafeBag)
+ {
+ return (SafeBag)obj;
+ }
+
+ if (obj != null)
+ {
+ return new SafeBag(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private SafeBag(
+ ASN1Sequence seq)
+ {
+ this.bagId = (ASN1ObjectIdentifier)seq.getObjectAt(0);
+ this.bagValue = ((ASN1TaggedObject)seq.getObjectAt(1)).getObject();
+ if (seq.size() == 3)
+ {
+ this.bagAttributes = (ASN1Set)seq.getObjectAt(2);
+ }
+ }
+
+ public ASN1ObjectIdentifier getBagId()
+ {
+ return bagId;
+ }
+
+ public ASN1Encodable getBagValue()
+ {
+ return bagValue;
+ }
+
+ public ASN1Set getBagAttributes()
+ {
+ return bagAttributes;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(bagId);
+ v.add(new DLTaggedObject(true, 0, bagValue));
+
+ if (bagAttributes != null)
+ {
+ v.add(bagAttributes);
+ }
+
+ return new DLSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/SignedData.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/SignedData.java
new file mode 100644
index 00000000..3d3089bc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/SignedData.java
@@ -0,0 +1,167 @@
+package org.bouncycastle.asn1.pkcs;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.BERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * a PKCS#7 signed data object.
+ */
+public class SignedData
+ extends ASN1Object
+ implements PKCSObjectIdentifiers
+{
+ private ASN1Integer version;
+ private ASN1Set digestAlgorithms;
+ private ContentInfo contentInfo;
+ private ASN1Set certificates;
+ private ASN1Set crls;
+ private ASN1Set signerInfos;
+
+ public static SignedData getInstance(
+ Object o)
+ {
+ if (o instanceof SignedData)
+ {
+ return (SignedData)o;
+ }
+ else if (o != null)
+ {
+ return new SignedData(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public SignedData(
+ ASN1Integer _version,
+ ASN1Set _digestAlgorithms,
+ ContentInfo _contentInfo,
+ ASN1Set _certificates,
+ ASN1Set _crls,
+ ASN1Set _signerInfos)
+ {
+ version = _version;
+ digestAlgorithms = _digestAlgorithms;
+ contentInfo = _contentInfo;
+ certificates = _certificates;
+ crls = _crls;
+ signerInfos = _signerInfos;
+ }
+
+ public SignedData(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ version = (ASN1Integer)e.nextElement();
+ digestAlgorithms = ((ASN1Set)e.nextElement());
+ contentInfo = ContentInfo.getInstance(e.nextElement());
+
+ while (e.hasMoreElements())
+ {
+ ASN1Primitive o = (ASN1Primitive)e.nextElement();
+
+ //
+ // an interesting feature of SignedData is that there appear to be varying implementations...
+ // for the moment we ignore anything which doesn't fit.
+ //
+ if (o instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject tagged = (ASN1TaggedObject)o;
+
+ switch (tagged.getTagNo())
+ {
+ case 0:
+ certificates = ASN1Set.getInstance(tagged, false);
+ break;
+ case 1:
+ crls = ASN1Set.getInstance(tagged, false);
+ break;
+ default:
+ throw new IllegalArgumentException("unknown tag value " + tagged.getTagNo());
+ }
+ }
+ else
+ {
+ signerInfos = (ASN1Set)o;
+ }
+ }
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public ASN1Set getDigestAlgorithms()
+ {
+ return digestAlgorithms;
+ }
+
+ public ContentInfo getContentInfo()
+ {
+ return contentInfo;
+ }
+
+ public ASN1Set getCertificates()
+ {
+ return certificates;
+ }
+
+ public ASN1Set getCRLs()
+ {
+ return crls;
+ }
+
+ public ASN1Set getSignerInfos()
+ {
+ return signerInfos;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * SignedData ::= SEQUENCE {
+ * version Version,
+ * digestAlgorithms DigestAlgorithmIdentifiers,
+ * contentInfo ContentInfo,
+ * certificates
+ * [0] IMPLICIT ExtendedCertificatesAndCertificates
+ * OPTIONAL,
+ * crls
+ * [1] IMPLICIT CertificateRevocationLists OPTIONAL,
+ * signerInfos SignerInfos }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+ v.add(digestAlgorithms);
+ v.add(contentInfo);
+
+ if (certificates != null)
+ {
+ v.add(new DERTaggedObject(false, 0, certificates));
+ }
+
+ if (crls != null)
+ {
+ v.add(new DERTaggedObject(false, 1, crls));
+ }
+
+ v.add(signerInfos);
+
+ return new BERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/SignerInfo.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/SignerInfo.java
new file mode 100644
index 00000000..ab5d78ab
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/SignerInfo.java
@@ -0,0 +1,178 @@
+package org.bouncycastle.asn1.pkcs;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * a PKCS#7 signer info object.
+ */
+public class SignerInfo
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private IssuerAndSerialNumber issuerAndSerialNumber;
+ private AlgorithmIdentifier digAlgorithm;
+ private ASN1Set authenticatedAttributes;
+ private AlgorithmIdentifier digEncryptionAlgorithm;
+ private ASN1OctetString encryptedDigest;
+ private ASN1Set unauthenticatedAttributes;
+
+ public static SignerInfo getInstance(
+ Object o)
+ {
+ if (o instanceof SignerInfo)
+ {
+ return (SignerInfo)o;
+ }
+ else if (o instanceof ASN1Sequence)
+ {
+ return new SignerInfo((ASN1Sequence)o);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory: " + o.getClass().getName());
+ }
+
+ public SignerInfo(
+ ASN1Integer version,
+ IssuerAndSerialNumber issuerAndSerialNumber,
+ AlgorithmIdentifier digAlgorithm,
+ ASN1Set authenticatedAttributes,
+ AlgorithmIdentifier digEncryptionAlgorithm,
+ ASN1OctetString encryptedDigest,
+ ASN1Set unauthenticatedAttributes)
+ {
+ this.version = version;
+ this.issuerAndSerialNumber = issuerAndSerialNumber;
+ this.digAlgorithm = digAlgorithm;
+ this.authenticatedAttributes = authenticatedAttributes;
+ this.digEncryptionAlgorithm = digEncryptionAlgorithm;
+ this.encryptedDigest = encryptedDigest;
+ this.unauthenticatedAttributes = unauthenticatedAttributes;
+ }
+
+ public SignerInfo(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ version = (ASN1Integer)e.nextElement();
+ issuerAndSerialNumber = IssuerAndSerialNumber.getInstance(e.nextElement());
+ digAlgorithm = AlgorithmIdentifier.getInstance(e.nextElement());
+
+ Object obj = e.nextElement();
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ authenticatedAttributes = ASN1Set.getInstance((ASN1TaggedObject)obj, false);
+
+ digEncryptionAlgorithm = AlgorithmIdentifier.getInstance(e.nextElement());
+ }
+ else
+ {
+ authenticatedAttributes = null;
+ digEncryptionAlgorithm = AlgorithmIdentifier.getInstance(obj);
+ }
+
+ encryptedDigest = DEROctetString.getInstance(e.nextElement());
+
+ if (e.hasMoreElements())
+ {
+ unauthenticatedAttributes = ASN1Set.getInstance((ASN1TaggedObject)e.nextElement(), false);
+ }
+ else
+ {
+ unauthenticatedAttributes = null;
+ }
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public IssuerAndSerialNumber getIssuerAndSerialNumber()
+ {
+ return issuerAndSerialNumber;
+ }
+
+ public ASN1Set getAuthenticatedAttributes()
+ {
+ return authenticatedAttributes;
+ }
+
+ public AlgorithmIdentifier getDigestAlgorithm()
+ {
+ return digAlgorithm;
+ }
+
+ public ASN1OctetString getEncryptedDigest()
+ {
+ return encryptedDigest;
+ }
+
+ public AlgorithmIdentifier getDigestEncryptionAlgorithm()
+ {
+ return digEncryptionAlgorithm;
+ }
+
+ public ASN1Set getUnauthenticatedAttributes()
+ {
+ return unauthenticatedAttributes;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * SignerInfo ::= SEQUENCE {
+ * version Version,
+ * issuerAndSerialNumber IssuerAndSerialNumber,
+ * digestAlgorithm DigestAlgorithmIdentifier,
+ * authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL,
+ * digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
+ * encryptedDigest EncryptedDigest,
+ * unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL
+ * }
+ *
+ * EncryptedDigest ::= OCTET STRING
+ *
+ * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
+ *
+ * DigestEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+ v.add(issuerAndSerialNumber);
+ v.add(digAlgorithm);
+
+ if (authenticatedAttributes != null)
+ {
+ v.add(new DERTaggedObject(false, 0, authenticatedAttributes));
+ }
+
+ v.add(digEncryptionAlgorithm);
+ v.add(encryptedDigest);
+
+ if (unauthenticatedAttributes != null)
+ {
+ v.add(new DERTaggedObject(false, 1, unauthenticatedAttributes));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKey.java b/core/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKey.java
new file mode 100644
index 00000000..4bf6b2bb
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKey.java
@@ -0,0 +1,143 @@
+package org.bouncycastle.asn1.sec;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.util.BigIntegers;
+
+/**
+ * the elliptic curve private key object from SEC 1
+ */
+public class ECPrivateKey
+ extends ASN1Object
+{
+ private ASN1Sequence seq;
+
+ private ECPrivateKey(
+ ASN1Sequence seq)
+ {
+ this.seq = seq;
+ }
+
+ public static ECPrivateKey getInstance(
+ Object obj)
+ {
+ if (obj instanceof ECPrivateKey)
+ {
+ return (ECPrivateKey)obj;
+ }
+
+ if (obj != null)
+ {
+ return new ECPrivateKey(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public ECPrivateKey(
+ BigInteger key)
+ {
+ byte[] bytes = BigIntegers.asUnsignedByteArray(key);
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new ASN1Integer(1));
+ v.add(new DEROctetString(bytes));
+
+ seq = new DERSequence(v);
+ }
+
+ public ECPrivateKey(
+ BigInteger key,
+ ASN1Object parameters)
+ {
+ this(key, null, parameters);
+ }
+
+ public ECPrivateKey(
+ BigInteger key,
+ DERBitString publicKey,
+ ASN1Object parameters)
+ {
+ byte[] bytes = BigIntegers.asUnsignedByteArray(key);
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new ASN1Integer(1));
+ v.add(new DEROctetString(bytes));
+
+ if (parameters != null)
+ {
+ v.add(new DERTaggedObject(true, 0, parameters));
+ }
+
+ if (publicKey != null)
+ {
+ v.add(new DERTaggedObject(true, 1, publicKey));
+ }
+
+ seq = new DERSequence(v);
+ }
+
+ public BigInteger getKey()
+ {
+ ASN1OctetString octs = (ASN1OctetString)seq.getObjectAt(1);
+
+ return new BigInteger(1, octs.getOctets());
+ }
+
+ public DERBitString getPublicKey()
+ {
+ return (DERBitString)getObjectInTag(1);
+ }
+
+ public ASN1Primitive getParameters()
+ {
+ return getObjectInTag(0);
+ }
+
+ private ASN1Primitive getObjectInTag(int tagNo)
+ {
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ ASN1Encodable obj = (ASN1Encodable)e.nextElement();
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject tag = (ASN1TaggedObject)obj;
+ if (tag.getTagNo() == tagNo)
+ {
+ return tag.getObject().toASN1Primitive();
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * ECPrivateKey ::= SEQUENCE {
+ * version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
+ * privateKey OCTET STRING,
+ * parameters [0] Parameters OPTIONAL,
+ * publicKey [1] BIT STRING OPTIONAL }
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return seq;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKeyStructure.java b/core/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKeyStructure.java
new file mode 100644
index 00000000..3b1bcc38
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKeyStructure.java
@@ -0,0 +1,128 @@
+package org.bouncycastle.asn1.sec;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.util.BigIntegers;
+
+/**
+ * the elliptic curve private key object from SEC 1
+ * @deprecated use ECPrivateKey
+ */
+public class ECPrivateKeyStructure
+ extends ASN1Object
+{
+ private ASN1Sequence seq;
+
+ public ECPrivateKeyStructure(
+ ASN1Sequence seq)
+ {
+ this.seq = seq;
+ }
+
+ public ECPrivateKeyStructure(
+ BigInteger key)
+ {
+ byte[] bytes = BigIntegers.asUnsignedByteArray(key);
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new ASN1Integer(1));
+ v.add(new DEROctetString(bytes));
+
+ seq = new DERSequence(v);
+ }
+
+ public ECPrivateKeyStructure(
+ BigInteger key,
+ ASN1Encodable parameters)
+ {
+ this(key, null, parameters);
+ }
+
+ public ECPrivateKeyStructure(
+ BigInteger key,
+ DERBitString publicKey,
+ ASN1Encodable parameters)
+ {
+ byte[] bytes = BigIntegers.asUnsignedByteArray(key);
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new ASN1Integer(1));
+ v.add(new DEROctetString(bytes));
+
+ if (parameters != null)
+ {
+ v.add(new DERTaggedObject(true, 0, parameters));
+ }
+
+ if (publicKey != null)
+ {
+ v.add(new DERTaggedObject(true, 1, publicKey));
+ }
+
+ seq = new DERSequence(v);
+ }
+
+ public BigInteger getKey()
+ {
+ ASN1OctetString octs = (ASN1OctetString)seq.getObjectAt(1);
+
+ return new BigInteger(1, octs.getOctets());
+ }
+
+ public DERBitString getPublicKey()
+ {
+ return (DERBitString)getObjectInTag(1);
+ }
+
+ public ASN1Primitive getParameters()
+ {
+ return getObjectInTag(0);
+ }
+
+ private ASN1Primitive getObjectInTag(int tagNo)
+ {
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ ASN1Encodable obj = (ASN1Encodable)e.nextElement();
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject tag = (ASN1TaggedObject)obj;
+ if (tag.getTagNo() == tagNo)
+ {
+ return (ASN1Primitive)((ASN1Encodable)tag.getObject()).toASN1Primitive();
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * ECPrivateKey ::= SEQUENCE {
+ * version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
+ * privateKey OCTET STRING,
+ * parameters [0] Parameters OPTIONAL,
+ * publicKey [1] BIT STRING OPTIONAL }
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return seq;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/sec/SECNamedCurves.java b/core/src/main/java/org/bouncycastle/asn1/sec/SECNamedCurves.java
new file mode 100644
index 00000000..44c811bb
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/sec/SECNamedCurves.java
@@ -0,0 +1,1029 @@
+package org.bouncycastle.asn1.sec;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.asn1.x9.X9ECParametersHolder;
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SECNamedCurves
+{
+ private static BigInteger fromHex(
+ String hex)
+ {
+ return new BigInteger(1, Hex.decode(hex));
+ }
+
+ /*
+ * secp112r1
+ */
+ static X9ECParametersHolder secp112r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ // p = (2^128 - 3) / 76439
+ BigInteger p = fromHex("DB7C2ABF62E35E668076BEAD208B");
+ BigInteger a = fromHex("DB7C2ABF62E35E668076BEAD2088");
+ BigInteger b = fromHex("659EF8BA043916EEDE8911702B22");
+ byte[] S = Hex.decode("00F50B028E4D696E676875615175290472783FB1");
+ BigInteger n = fromHex("DB7C2ABF62E35E7628DFAC6561C5");
+ BigInteger h = BigInteger.valueOf(1);
+
+ ECCurve curve = new ECCurve.Fp(p, a, b);
+ //ECPoint G = curve.decodePoint(Hex.decode("02"
+ //+ "09487239995A5EE76B55F9C2F098"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "09487239995A5EE76B55F9C2F098"
+ + "A89CE5AF8724C0A23E0E0FF77500"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * secp112r2
+ */
+ static X9ECParametersHolder secp112r2 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ // p = (2^128 - 3) / 76439
+ BigInteger p = fromHex("DB7C2ABF62E35E668076BEAD208B");
+ BigInteger a = fromHex("6127C24C05F38A0AAAF65C0EF02C");
+ BigInteger b = fromHex("51DEF1815DB5ED74FCC34C85D709");
+ byte[] S = Hex.decode("002757A1114D696E6768756151755316C05E0BD4");
+ BigInteger n = fromHex("36DF0AAFD8B8D7597CA10520D04B");
+ BigInteger h = BigInteger.valueOf(4);
+
+ ECCurve curve = new ECCurve.Fp(p, a, b);
+ //ECPoint G = curve.decodePoint(Hex.decode("03"
+ //+ "4BA30AB5E892B4E1649DD0928643"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "4BA30AB5E892B4E1649DD0928643"
+ + "ADCD46F5882E3747DEF36E956E97"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * secp128r1
+ */
+ static X9ECParametersHolder secp128r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ // p = 2^128 - 2^97 - 1
+ BigInteger p = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF");
+ BigInteger a = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC");
+ BigInteger b = fromHex("E87579C11079F43DD824993C2CEE5ED3");
+ byte[] S = Hex.decode("000E0D4D696E6768756151750CC03A4473D03679");
+ BigInteger n = fromHex("FFFFFFFE0000000075A30D1B9038A115");
+ BigInteger h = BigInteger.valueOf(1);
+
+ ECCurve curve = new ECCurve.Fp(p, a, b);
+ //ECPoint G = curve.decodePoint(Hex.decode("03"
+ //+ "161FF7528B899B2D0C28607CA52C5B86"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "161FF7528B899B2D0C28607CA52C5B86"
+ + "CF5AC8395BAFEB13C02DA292DDED7A83"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * secp128r2
+ */
+ static X9ECParametersHolder secp128r2 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ // p = 2^128 - 2^97 - 1
+ BigInteger p = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF");
+ BigInteger a = fromHex("D6031998D1B3BBFEBF59CC9BBFF9AEE1");
+ BigInteger b = fromHex("5EEEFCA380D02919DC2C6558BB6D8A5D");
+ byte[] S = Hex.decode("004D696E67687561517512D8F03431FCE63B88F4");
+ BigInteger n = fromHex("3FFFFFFF7FFFFFFFBE0024720613B5A3");
+ BigInteger h = BigInteger.valueOf(4);
+
+ ECCurve curve = new ECCurve.Fp(p, a, b);
+ //ECPoint G = curve.decodePoint(Hex.decode("02"
+ //+ "7B6AA5D85E572983E6FB32A7CDEBC140"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "7B6AA5D85E572983E6FB32A7CDEBC140"
+ + "27B6916A894D3AEE7106FE805FC34B44"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * secp160k1
+ */
+ static X9ECParametersHolder secp160k1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ // p = 2^160 - 2^32 - 2^14 - 2^12 - 2^9 - 2^8 - 2^7 - 2^3 - 2^2 - 1
+ BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73");
+ BigInteger a = ECConstants.ZERO;
+ BigInteger b = BigInteger.valueOf(7);
+ byte[] S = null;
+ BigInteger n = fromHex("0100000000000000000001B8FA16DFAB9ACA16B6B3");
+ BigInteger h = BigInteger.valueOf(1);
+
+ ECCurve curve = new ECCurve.Fp(p, a, b);
+// ECPoint G = curve.decodePoint(Hex.decode("02"
+// + "3B4C382CE37AA192A4019E763036F4F5DD4D7EBB"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "3B4C382CE37AA192A4019E763036F4F5DD4D7EBB"
+ + "938CF935318FDCED6BC28286531733C3F03C4FEE"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * secp160r1
+ */
+ static X9ECParametersHolder secp160r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ // p = 2^160 - 2^31 - 1
+ BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF");
+ BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC");
+ BigInteger b = fromHex("1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45");
+ byte[] S = Hex.decode("1053CDE42C14D696E67687561517533BF3F83345");
+ BigInteger n = fromHex("0100000000000000000001F4C8F927AED3CA752257");
+ BigInteger h = BigInteger.valueOf(1);
+
+ ECCurve curve = new ECCurve.Fp(p, a, b);
+ //ECPoint G = curve.decodePoint(Hex.decode("02"
+ //+ "4A96B5688EF573284664698968C38BB913CBFC82"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "4A96B5688EF573284664698968C38BB913CBFC82"
+ + "23A628553168947D59DCC912042351377AC5FB32"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * secp160r2
+ */
+ static X9ECParametersHolder secp160r2 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ // p = 2^160 - 2^32 - 2^14 - 2^12 - 2^9 - 2^8 - 2^7 - 2^3 - 2^2 - 1
+ BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73");
+ BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC70");
+ BigInteger b = fromHex("B4E134D3FB59EB8BAB57274904664D5AF50388BA");
+ byte[] S = Hex.decode("B99B99B099B323E02709A4D696E6768756151751");
+ BigInteger n = fromHex("0100000000000000000000351EE786A818F3A1A16B");
+ BigInteger h = BigInteger.valueOf(1);
+
+ ECCurve curve = new ECCurve.Fp(p, a, b);
+ //ECPoint G = curve.decodePoint(Hex.decode("02"
+ //+ "52DCB034293A117E1F4FF11B30F7199D3144CE6D"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "52DCB034293A117E1F4FF11B30F7199D3144CE6D"
+ + "FEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * secp192k1
+ */
+ static X9ECParametersHolder secp192k1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ // p = 2^192 - 2^32 - 2^12 - 2^8 - 2^7 - 2^6 - 2^3 - 1
+ BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37");
+ BigInteger a = ECConstants.ZERO;
+ BigInteger b = BigInteger.valueOf(3);
+ byte[] S = null;
+ BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D");
+ BigInteger h = BigInteger.valueOf(1);
+
+ ECCurve curve = new ECCurve.Fp(p, a, b);
+ //ECPoint G = curve.decodePoint(Hex.decode("03"
+ //+ "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D"
+ + "9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * secp192r1
+ */
+ static X9ECParametersHolder secp192r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ // p = 2^192 - 2^64 - 1
+ BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF");
+ BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC");
+ BigInteger b = fromHex("64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1");
+ byte[] S = Hex.decode("3045AE6FC8422F64ED579528D38120EAE12196D5");
+ BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831");
+ BigInteger h = BigInteger.valueOf(1);
+
+ ECCurve curve = new ECCurve.Fp(p, a, b);
+ //ECPoint G = curve.decodePoint(Hex.decode("03"
+ //+ "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012"
+ + "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * secp224k1
+ */
+ static X9ECParametersHolder secp224k1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ // p = 2^224 - 2^32 - 2^12 - 2^11 - 2^9 - 2^7 - 2^4 - 2 - 1
+ BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D");
+ BigInteger a = ECConstants.ZERO;
+ BigInteger b = BigInteger.valueOf(5);
+ byte[] S = null;
+ BigInteger n = fromHex("010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7");
+ BigInteger h = BigInteger.valueOf(1);
+
+ ECCurve curve = new ECCurve.Fp(p, a, b);
+ //ECPoint G = curve.decodePoint(Hex.decode("03"
+ //+ "A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C"
+ + "7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * secp224r1
+ */
+ static X9ECParametersHolder secp224r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ // p = 2^224 - 2^96 + 1
+ BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001");
+ BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE");
+ BigInteger b = fromHex("B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4");
+ byte[] S = Hex.decode("BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5");
+ BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D");
+ BigInteger h = BigInteger.valueOf(1);
+
+ ECCurve curve = new ECCurve.Fp(p, a, b);
+ //ECPoint G = curve.decodePoint(Hex.decode("02"
+ //+ "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21"
+ + "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * secp256k1
+ */
+ static X9ECParametersHolder secp256k1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ // p = 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1
+ BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F");
+ BigInteger a = ECConstants.ZERO;
+ BigInteger b = BigInteger.valueOf(7);
+ byte[] S = null;
+ BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141");
+ BigInteger h = BigInteger.valueOf(1);
+
+ ECCurve curve = new ECCurve.Fp(p, a, b);
+ //ECPoint G = curve.decodePoint(Hex.decode("02"
+ //+ "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"
+ + "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * secp256r1
+ */
+ static X9ECParametersHolder secp256r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ // p = 2^224 (2^32 - 1) + 2^192 + 2^96 - 1
+ BigInteger p = fromHex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF");
+ BigInteger a = fromHex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC");
+ BigInteger b = fromHex("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B");
+ byte[] S = Hex.decode("C49D360886E704936A6678E1139D26B7819F7E90");
+ BigInteger n = fromHex("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
+ BigInteger h = BigInteger.valueOf(1);
+
+ ECCurve curve = new ECCurve.Fp(p, a, b);
+ //ECPoint G = curve.decodePoint(Hex.decode("03"
+ //+ "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"
+ + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * secp384r1
+ */
+ static X9ECParametersHolder secp384r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ // p = 2^384 - 2^128 - 2^96 + 2^32 - 1
+ BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF");
+ BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC");
+ BigInteger b = fromHex("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF");
+ byte[] S = Hex.decode("A335926AA319A27A1D00896A6773A4827ACDAC73");
+ BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973");
+ BigInteger h = BigInteger.valueOf(1);
+
+ ECCurve curve = new ECCurve.Fp(p, a, b);
+ //ECPoint G = curve.decodePoint(Hex.decode("03"
+ //+ "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7"
+ + "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * secp521r1
+ */
+ static X9ECParametersHolder secp521r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ // p = 2^521 - 1
+ BigInteger p = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+ BigInteger a = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC");
+ BigInteger b = fromHex("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00");
+ byte[] S = Hex.decode("D09E8800291CB85396CC6717393284AAA0DA64BA");
+ BigInteger n = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409");
+ BigInteger h = BigInteger.valueOf(1);
+
+ ECCurve curve = new ECCurve.Fp(p, a, b);
+ //ECPoint G = curve.decodePoint(Hex.decode("02"
+ //+ "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66"
+ + "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * sect113r1
+ */
+ static X9ECParametersHolder sect113r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ int m = 113;
+ int k = 9;
+
+ BigInteger a = fromHex("003088250CA6E7C7FE649CE85820F7");
+ BigInteger b = fromHex("00E8BEE4D3E2260744188BE0E9C723");
+ byte[] S = Hex.decode("10E723AB14D696E6768756151756FEBF8FCB49A9");
+ BigInteger n = fromHex("0100000000000000D9CCEC8A39E56F");
+ BigInteger h = BigInteger.valueOf(2);
+
+ ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h);
+ //ECPoint G = curve.decodePoint(Hex.decode("03"
+ //+ "009D73616F35F4AB1407D73562C10F"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "009D73616F35F4AB1407D73562C10F"
+ + "00A52830277958EE84D1315ED31886"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * sect113r2
+ */
+ static X9ECParametersHolder sect113r2 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ int m = 113;
+ int k = 9;
+
+ BigInteger a = fromHex("00689918DBEC7E5A0DD6DFC0AA55C7");
+ BigInteger b = fromHex("0095E9A9EC9B297BD4BF36E059184F");
+ byte[] S = Hex.decode("10C0FB15760860DEF1EEF4D696E676875615175D");
+ BigInteger n = fromHex("010000000000000108789B2496AF93");
+ BigInteger h = BigInteger.valueOf(2);
+
+ ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h);
+ //ECPoint G = curve.decodePoint(Hex.decode("03"
+ //+ "01A57A6A7B26CA5EF52FCDB8164797"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "01A57A6A7B26CA5EF52FCDB8164797"
+ + "00B3ADC94ED1FE674C06E695BABA1D"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * sect131r1
+ */
+ static X9ECParametersHolder sect131r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ int m = 131;
+ int k1 = 2;
+ int k2 = 3;
+ int k3 = 8;
+
+ BigInteger a = fromHex("07A11B09A76B562144418FF3FF8C2570B8");
+ BigInteger b = fromHex("0217C05610884B63B9C6C7291678F9D341");
+ byte[] S = Hex.decode("4D696E676875615175985BD3ADBADA21B43A97E2");
+ BigInteger n = fromHex("0400000000000000023123953A9464B54D");
+ BigInteger h = BigInteger.valueOf(2);
+
+ ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h);
+ //ECPoint G = curve.decodePoint(Hex.decode("03"
+ //+ "0081BAF91FDF9833C40F9C181343638399"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "0081BAF91FDF9833C40F9C181343638399"
+ + "078C6E7EA38C001F73C8134B1B4EF9E150"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * sect131r2
+ */
+ static X9ECParametersHolder sect131r2 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ int m = 131;
+ int k1 = 2;
+ int k2 = 3;
+ int k3 = 8;
+
+ BigInteger a = fromHex("03E5A88919D7CAFCBF415F07C2176573B2");
+ BigInteger b = fromHex("04B8266A46C55657AC734CE38F018F2192");
+ byte[] S = Hex.decode("985BD3ADBAD4D696E676875615175A21B43A97E3");
+ BigInteger n = fromHex("0400000000000000016954A233049BA98F");
+ BigInteger h = BigInteger.valueOf(2);
+
+ ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h);
+ //ECPoint G = curve.decodePoint(Hex.decode("03"
+ //+ "0356DCD8F2F95031AD652D23951BB366A8"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "0356DCD8F2F95031AD652D23951BB366A8"
+ + "0648F06D867940A5366D9E265DE9EB240F"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * sect163k1
+ */
+ static X9ECParametersHolder sect163k1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ int m = 163;
+ int k1 = 3;
+ int k2 = 6;
+ int k3 = 7;
+
+ BigInteger a = BigInteger.valueOf(1);
+ BigInteger b = BigInteger.valueOf(1);
+ byte[] S = null;
+ BigInteger n = fromHex("04000000000000000000020108A2E0CC0D99F8A5EF");
+ BigInteger h = BigInteger.valueOf(2);
+
+ ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h);
+ //ECPoint G = curve.decodePoint(Hex.decode("03"
+ //+ "02FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE8"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "02FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE8"
+ + "0289070FB05D38FF58321F2E800536D538CCDAA3D9"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * sect163r1
+ */
+ static X9ECParametersHolder sect163r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ int m = 163;
+ int k1 = 3;
+ int k2 = 6;
+ int k3 = 7;
+
+ BigInteger a = fromHex("07B6882CAAEFA84F9554FF8428BD88E246D2782AE2");
+ BigInteger b = fromHex("0713612DCDDCB40AAB946BDA29CA91F73AF958AFD9");
+ byte[] S = Hex.decode("24B7B137C8A14D696E6768756151756FD0DA2E5C");
+ BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFF48AAB689C29CA710279B");
+ BigInteger h = BigInteger.valueOf(2);
+
+ ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h);
+ //ECPoint G = curve.decodePoint(Hex.decode("03"
+ //+ "0369979697AB43897789566789567F787A7876A654"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "0369979697AB43897789566789567F787A7876A654"
+ + "00435EDB42EFAFB2989D51FEFCE3C80988F41FF883"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * sect163r2
+ */
+ static X9ECParametersHolder sect163r2 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ int m = 163;
+ int k1 = 3;
+ int k2 = 6;
+ int k3 = 7;
+
+ BigInteger a = BigInteger.valueOf(1);
+ BigInteger b = fromHex("020A601907B8C953CA1481EB10512F78744A3205FD");
+ byte[] S = Hex.decode("85E25BFE5C86226CDB12016F7553F9D0E693A268");
+ BigInteger n = fromHex("040000000000000000000292FE77E70C12A4234C33");
+ BigInteger h = BigInteger.valueOf(2);
+
+ ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h);
+ //ECPoint G = curve.decodePoint(Hex.decode("03"
+ //+ "03F0EBA16286A2D57EA0991168D4994637E8343E36"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "03F0EBA16286A2D57EA0991168D4994637E8343E36"
+ + "00D51FBC6C71A0094FA2CDD545B11C5C0C797324F1"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * sect193r1
+ */
+ static X9ECParametersHolder sect193r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ int m = 193;
+ int k = 15;
+
+ BigInteger a = fromHex("0017858FEB7A98975169E171F77B4087DE098AC8A911DF7B01");
+ BigInteger b = fromHex("00FDFB49BFE6C3A89FACADAA7A1E5BBC7CC1C2E5D831478814");
+ byte[] S = Hex.decode("103FAEC74D696E676875615175777FC5B191EF30");
+ BigInteger n = fromHex("01000000000000000000000000C7F34A778F443ACC920EBA49");
+ BigInteger h = BigInteger.valueOf(2);
+
+ ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h);
+ //ECPoint G = curve.decodePoint(Hex.decode("03"
+ //+ "01F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E1"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "01F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E1"
+ + "0025E399F2903712CCF3EA9E3A1AD17FB0B3201B6AF7CE1B05"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * sect193r2
+ */
+ static X9ECParametersHolder sect193r2 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ int m = 193;
+ int k = 15;
+
+ BigInteger a = fromHex("0163F35A5137C2CE3EA6ED8667190B0BC43ECD69977702709B");
+ BigInteger b = fromHex("00C9BB9E8927D4D64C377E2AB2856A5B16E3EFB7F61D4316AE");
+ byte[] S = Hex.decode("10B7B4D696E676875615175137C8A16FD0DA2211");
+ BigInteger n = fromHex("010000000000000000000000015AAB561B005413CCD4EE99D5");
+ BigInteger h = BigInteger.valueOf(2);
+
+ ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h);
+ //ECPoint G = curve.decodePoint(Hex.decode("03"
+ //+ "00D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "00D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F"
+ + "01CE94335607C304AC29E7DEFBD9CA01F596F927224CDECF6C"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * sect233k1
+ */
+ static X9ECParametersHolder sect233k1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ int m = 233;
+ int k = 74;
+
+ BigInteger a = ECConstants.ZERO;
+ BigInteger b = BigInteger.valueOf(1);
+ byte[] S = null;
+ BigInteger n = fromHex("8000000000000000000000000000069D5BB915BCD46EFB1AD5F173ABDF");
+ BigInteger h = BigInteger.valueOf(4);
+
+ ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h);
+ //ECPoint G = curve.decodePoint(Hex.decode("02"
+ //+ "017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD6126"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD6126"
+ + "01DB537DECE819B7F70F555A67C427A8CD9BF18AEB9B56E0C11056FAE6A3"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * sect233r1
+ */
+ static X9ECParametersHolder sect233r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ int m = 233;
+ int k = 74;
+
+ BigInteger a = BigInteger.valueOf(1);
+ BigInteger b = fromHex("0066647EDE6C332C7F8C0923BB58213B333B20E9CE4281FE115F7D8F90AD");
+ byte[] S = Hex.decode("74D59FF07F6B413D0EA14B344B20A2DB049B50C3");
+ BigInteger n = fromHex("01000000000000000000000000000013E974E72F8A6922031D2603CFE0D7");
+ BigInteger h = BigInteger.valueOf(2);
+
+ ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h);
+ //ECPoint G = curve.decodePoint(Hex.decode("03"
+ //+ "00FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "00FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B"
+ + "01006A08A41903350678E58528BEBF8A0BEFF867A7CA36716F7E01F81052"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * sect239k1
+ */
+ static X9ECParametersHolder sect239k1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ int m = 239;
+ int k = 158;
+
+ BigInteger a = ECConstants.ZERO;
+ BigInteger b = BigInteger.valueOf(1);
+ byte[] S = null;
+ BigInteger n = fromHex("2000000000000000000000000000005A79FEC67CB6E91F1C1DA800E478A5");
+ BigInteger h = BigInteger.valueOf(4);
+
+ ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h);
+ //ECPoint G = curve.decodePoint(Hex.decode("03"
+ //+ "29A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "29A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC"
+ + "76310804F12E549BDB011C103089E73510ACB275FC312A5DC6B76553F0CA"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * sect283k1
+ */
+ static X9ECParametersHolder sect283k1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ int m = 283;
+ int k1 = 5;
+ int k2 = 7;
+ int k3 = 12;
+
+ BigInteger a = ECConstants.ZERO;
+ BigInteger b = BigInteger.valueOf(1);
+ byte[] S = null;
+ BigInteger n = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9AE2ED07577265DFF7F94451E061E163C61");
+ BigInteger h = BigInteger.valueOf(4);
+
+ ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h);
+ //ECPoint G = curve.decodePoint(Hex.decode("02"
+ //+ "0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836"
+ + "01CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * sect283r1
+ */
+ static X9ECParametersHolder sect283r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ int m = 283;
+ int k1 = 5;
+ int k2 = 7;
+ int k3 = 12;
+
+ BigInteger a = BigInteger.valueOf(1);
+ BigInteger b = fromHex("027B680AC8B8596DA5A4AF8A19A0303FCA97FD7645309FA2A581485AF6263E313B79A2F5");
+ byte[] S = Hex.decode("77E2B07370EB0F832A6DD5B62DFC88CD06BB84BE");
+ BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307");
+ BigInteger h = BigInteger.valueOf(2);
+
+ ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h);
+ //ECPoint G = curve.decodePoint(Hex.decode("03"
+ //+ "05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053"
+ + "03676854FE24141CB98FE6D4B20D02B4516FF702350EDDB0826779C813F0DF45BE8112F4"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * sect409k1
+ */
+ static X9ECParametersHolder sect409k1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ int m = 409;
+ int k = 87;
+
+ BigInteger a = ECConstants.ZERO;
+ BigInteger b = BigInteger.valueOf(1);
+ byte[] S = null;
+ BigInteger n = fromHex("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F83B2D4EA20400EC4557D5ED3E3E7CA5B4B5C83B8E01E5FCF");
+ BigInteger h = BigInteger.valueOf(4);
+
+ ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h);
+ //ECPoint G = curve.decodePoint(Hex.decode("03"
+ //+ "0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746"
+ + "01E369050B7C4E42ACBA1DACBF04299C3460782F918EA427E6325165E9EA10E3DA5F6C42E9C55215AA9CA27A5863EC48D8E0286B"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * sect409r1
+ */
+ static X9ECParametersHolder sect409r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ int m = 409;
+ int k = 87;
+
+ BigInteger a = BigInteger.valueOf(1);
+ BigInteger b = fromHex("0021A5C2C8EE9FEB5C4B9A753B7B476B7FD6422EF1F3DD674761FA99D6AC27C8A9A197B272822F6CD57A55AA4F50AE317B13545F");
+ byte[] S = Hex.decode("4099B5A457F9D69F79213D094C4BCD4D4262210B");
+ BigInteger n = fromHex("010000000000000000000000000000000000000000000000000001E2AAD6A612F33307BE5FA47C3C9E052F838164CD37D9A21173");
+ BigInteger h = BigInteger.valueOf(2);
+
+ ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h);
+ //ECPoint G = curve.decodePoint(Hex.decode("03"
+ //+ "015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7"
+ + "0061B1CFAB6BE5F32BBFA78324ED106A7636B9C5A7BD198D0158AA4F5488D08F38514F1FDF4B4F40D2181B3681C364BA0273C706"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * sect571k1
+ */
+ static X9ECParametersHolder sect571k1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ int m = 571;
+ int k1 = 2;
+ int k2 = 5;
+ int k3 = 10;
+
+ BigInteger a = ECConstants.ZERO;
+ BigInteger b = BigInteger.valueOf(1);
+ byte[] S = null;
+ BigInteger n = fromHex("020000000000000000000000000000000000000000000000000000000000000000000000131850E1F19A63E4B391A8DB917F4138B630D84BE5D639381E91DEB45CFE778F637C1001");
+ BigInteger h = BigInteger.valueOf(4);
+
+ ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h);
+ //ECPoint G = curve.decodePoint(Hex.decode("02"
+ //+ "026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA44370958493B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA44370958493B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972"
+ + "0349DC807F4FBF374F4AEADE3BCA95314DD58CEC9F307A54FFC61EFC006D8A2C9D4979C0AC44AEA74FBEBBB9F772AEDCB620B01A7BA7AF1B320430C8591984F601CD4C143EF1C7A3"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+ /*
+ * sect571r1
+ */
+ static X9ECParametersHolder sect571r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ int m = 571;
+ int k1 = 2;
+ int k2 = 5;
+ int k3 = 10;
+
+ BigInteger a = BigInteger.valueOf(1);
+ BigInteger b = fromHex("02F40E7E2221F295DE297117B7F3D62F5C6A97FFCB8CEFF1CD6BA8CE4A9A18AD84FFABBD8EFA59332BE7AD6756A66E294AFD185A78FF12AA520E4DE739BACA0C7FFEFF7F2955727A");
+ byte[] S = Hex.decode("2AA058F73A0E33AB486B0F610410C53A7F132310");
+ BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE661CE18FF55987308059B186823851EC7DD9CA1161DE93D5174D66E8382E9BB2FE84E47");
+ BigInteger h = BigInteger.valueOf(2);
+
+ ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h);
+ //ECPoint G = curve.decodePoint(Hex.decode("03"
+ //+ "0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19"));
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19"
+ + "037BF27342DA639B6DCCFFFEB73D69D78C6C27A6009CBBCA1980F8533921E8A684423E43BAB08A576291AF8F461BB2A8B3531D2F0485C19B16E2F1516E23DD3C1A4827AF1B8AC15B"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+
+ static final Hashtable objIds = new Hashtable();
+ static final Hashtable curves = new Hashtable();
+ static final Hashtable names = new Hashtable();
+
+ static void defineCurve(String name, ASN1ObjectIdentifier oid, X9ECParametersHolder holder)
+ {
+ objIds.put(name, oid);
+ names.put(oid, name);
+ curves.put(oid, holder);
+ }
+
+ static
+ {
+ defineCurve("secp112r1", SECObjectIdentifiers.secp112r1, secp112r1);
+ defineCurve("secp112r2", SECObjectIdentifiers.secp112r2, secp112r2);
+ defineCurve("secp128r1", SECObjectIdentifiers.secp128r1, secp128r1);
+ defineCurve("secp128r2", SECObjectIdentifiers.secp128r2, secp128r2);
+ defineCurve("secp160k1", SECObjectIdentifiers.secp160k1, secp160k1);
+ defineCurve("secp160r1", SECObjectIdentifiers.secp160r1, secp160r1);
+ defineCurve("secp160r2", SECObjectIdentifiers.secp160r2, secp160r2);
+ defineCurve("secp192k1", SECObjectIdentifiers.secp192k1, secp192k1);
+ defineCurve("secp192r1", SECObjectIdentifiers.secp192r1, secp192r1);
+ defineCurve("secp224k1", SECObjectIdentifiers.secp224k1, secp224k1);
+ defineCurve("secp224r1", SECObjectIdentifiers.secp224r1, secp224r1);
+ defineCurve("secp256k1", SECObjectIdentifiers.secp256k1, secp256k1);
+ defineCurve("secp256r1", SECObjectIdentifiers.secp256r1, secp256r1);
+ defineCurve("secp384r1", SECObjectIdentifiers.secp384r1, secp384r1);
+ defineCurve("secp521r1", SECObjectIdentifiers.secp521r1, secp521r1);
+
+ defineCurve("sect113r1", SECObjectIdentifiers.sect113r1, sect113r1);
+ defineCurve("sect113r2", SECObjectIdentifiers.sect113r2, sect113r2);
+ defineCurve("sect131r1", SECObjectIdentifiers.sect131r1, sect131r1);
+ defineCurve("sect131r2", SECObjectIdentifiers.sect131r2, sect131r2);
+ defineCurve("sect163k1", SECObjectIdentifiers.sect163k1, sect163k1);
+ defineCurve("sect163r1", SECObjectIdentifiers.sect163r1, sect163r1);
+ defineCurve("sect163r2", SECObjectIdentifiers.sect163r2, sect163r2);
+ defineCurve("sect193r1", SECObjectIdentifiers.sect193r1, sect193r1);
+ defineCurve("sect193r2", SECObjectIdentifiers.sect193r2, sect193r2);
+ defineCurve("sect233k1", SECObjectIdentifiers.sect233k1, sect233k1);
+ defineCurve("sect233r1", SECObjectIdentifiers.sect233r1, sect233r1);
+ defineCurve("sect239k1", SECObjectIdentifiers.sect239k1, sect239k1);
+ defineCurve("sect283k1", SECObjectIdentifiers.sect283k1, sect283k1);
+ defineCurve("sect283r1", SECObjectIdentifiers.sect283r1, sect283r1);
+ defineCurve("sect409k1", SECObjectIdentifiers.sect409k1, sect409k1);
+ defineCurve("sect409r1", SECObjectIdentifiers.sect409r1, sect409r1);
+ defineCurve("sect571k1", SECObjectIdentifiers.sect571k1, sect571k1);
+ defineCurve("sect571r1", SECObjectIdentifiers.sect571r1, sect571r1);
+ }
+
+ public static X9ECParameters getByName(
+ String name)
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)objIds.get(Strings.toLowerCase(name));
+
+ if (oid != null)
+ {
+ return getByOID(oid);
+ }
+
+ return null;
+ }
+
+ /**
+ * return the X9ECParameters object for the named curve represented by
+ * the passed in object identifier. Null if the curve isn't present.
+ *
+ * @param oid an object identifier representing a named curve, if present.
+ */
+ public static X9ECParameters getByOID(
+ ASN1ObjectIdentifier oid)
+ {
+ X9ECParametersHolder holder = (X9ECParametersHolder)curves.get(oid);
+
+ if (holder != null)
+ {
+ return holder.getParameters();
+ }
+
+ return null;
+ }
+
+ /**
+ * return the object identifier signified by the passed in name. Null
+ * if there is no object identifier associated with name.
+ *
+ * @return the object identifier associated with name, if present.
+ */
+ public static ASN1ObjectIdentifier getOID(
+ String name)
+ {
+ return (ASN1ObjectIdentifier)objIds.get(Strings.toLowerCase(name));
+ }
+
+ /**
+ * return the named curve name represented by the given object identifier.
+ */
+ public static String getName(
+ ASN1ObjectIdentifier oid)
+ {
+ return (String)names.get(oid);
+ }
+
+ /**
+ * returns an enumeration containing the name strings for curves
+ * contained in this structure.
+ */
+ public static Enumeration getNames()
+ {
+ return objIds.keys();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/sec/SECObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/sec/SECObjectIdentifiers.java
new file mode 100644
index 00000000..8b19cd68
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/sec/SECObjectIdentifiers.java
@@ -0,0 +1,50 @@
+package org.bouncycastle.asn1.sec;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+
+public interface SECObjectIdentifiers
+{
+ /**
+ * ellipticCurve OBJECT IDENTIFIER ::= {
+ * iso(1) identified-organization(3) certicom(132) curve(0)
+ * }
+ */
+ static final ASN1ObjectIdentifier ellipticCurve = new ASN1ObjectIdentifier("1.3.132.0");
+
+ static final ASN1ObjectIdentifier sect163k1 = ellipticCurve.branch("1");
+ static final ASN1ObjectIdentifier sect163r1 = ellipticCurve.branch("2");
+ static final ASN1ObjectIdentifier sect239k1 = ellipticCurve.branch("3");
+ static final ASN1ObjectIdentifier sect113r1 = ellipticCurve.branch("4");
+ static final ASN1ObjectIdentifier sect113r2 = ellipticCurve.branch("5");
+ static final ASN1ObjectIdentifier secp112r1 = ellipticCurve.branch("6");
+ static final ASN1ObjectIdentifier secp112r2 = ellipticCurve.branch("7");
+ static final ASN1ObjectIdentifier secp160r1 = ellipticCurve.branch("8");
+ static final ASN1ObjectIdentifier secp160k1 = ellipticCurve.branch("9");
+ static final ASN1ObjectIdentifier secp256k1 = ellipticCurve.branch("10");
+ static final ASN1ObjectIdentifier sect163r2 = ellipticCurve.branch("15");
+ static final ASN1ObjectIdentifier sect283k1 = ellipticCurve.branch("16");
+ static final ASN1ObjectIdentifier sect283r1 = ellipticCurve.branch("17");
+ static final ASN1ObjectIdentifier sect131r1 = ellipticCurve.branch("22");
+ static final ASN1ObjectIdentifier sect131r2 = ellipticCurve.branch("23");
+ static final ASN1ObjectIdentifier sect193r1 = ellipticCurve.branch("24");
+ static final ASN1ObjectIdentifier sect193r2 = ellipticCurve.branch("25");
+ static final ASN1ObjectIdentifier sect233k1 = ellipticCurve.branch("26");
+ static final ASN1ObjectIdentifier sect233r1 = ellipticCurve.branch("27");
+ static final ASN1ObjectIdentifier secp128r1 = ellipticCurve.branch("28");
+ static final ASN1ObjectIdentifier secp128r2 = ellipticCurve.branch("29");
+ static final ASN1ObjectIdentifier secp160r2 = ellipticCurve.branch("30");
+ static final ASN1ObjectIdentifier secp192k1 = ellipticCurve.branch("31");
+ static final ASN1ObjectIdentifier secp224k1 = ellipticCurve.branch("32");
+ static final ASN1ObjectIdentifier secp224r1 = ellipticCurve.branch("33");
+ static final ASN1ObjectIdentifier secp384r1 = ellipticCurve.branch("34");
+ static final ASN1ObjectIdentifier secp521r1 = ellipticCurve.branch("35");
+ static final ASN1ObjectIdentifier sect409k1 = ellipticCurve.branch("36");
+ static final ASN1ObjectIdentifier sect409r1 = ellipticCurve.branch("37");
+ static final ASN1ObjectIdentifier sect571k1 = ellipticCurve.branch("38");
+ static final ASN1ObjectIdentifier sect571r1 = ellipticCurve.branch("39");
+
+ static final ASN1ObjectIdentifier secp192r1 = X9ObjectIdentifiers.prime192v1;
+ static final ASN1ObjectIdentifier secp256r1 = X9ObjectIdentifiers.prime256v1;
+
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/smime/SMIMEAttributes.java b/core/src/main/java/org/bouncycastle/asn1/smime/SMIMEAttributes.java
new file mode 100644
index 00000000..eec29e65
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/smime/SMIMEAttributes.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.asn1.smime;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+
+public interface SMIMEAttributes
+{
+ public static final ASN1ObjectIdentifier smimeCapabilities = PKCSObjectIdentifiers.pkcs_9_at_smimeCapabilities;
+ public static final ASN1ObjectIdentifier encrypKeyPref = PKCSObjectIdentifiers.id_aa_encrypKeyPref;
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/smime/SMIMECapabilities.java b/core/src/main/java/org/bouncycastle/asn1/smime/SMIMECapabilities.java
new file mode 100644
index 00000000..f4558db5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/smime/SMIMECapabilities.java
@@ -0,0 +1,115 @@
+package org.bouncycastle.asn1.smime;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.cms.Attribute;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+
+/**
+ * Handler class for dealing with S/MIME Capabilities
+ */
+public class SMIMECapabilities
+ extends ASN1Object
+{
+ /**
+ * general preferences
+ */
+ public static final ASN1ObjectIdentifier preferSignedData = PKCSObjectIdentifiers.preferSignedData;
+ public static final ASN1ObjectIdentifier canNotDecryptAny = PKCSObjectIdentifiers.canNotDecryptAny;
+ public static final ASN1ObjectIdentifier sMIMECapabilitesVersions = PKCSObjectIdentifiers.sMIMECapabilitiesVersions;
+
+ /**
+ * encryption algorithms preferences
+ */
+ public static final ASN1ObjectIdentifier dES_CBC = new ASN1ObjectIdentifier("1.3.14.3.2.7");
+ public static final ASN1ObjectIdentifier dES_EDE3_CBC = PKCSObjectIdentifiers.des_EDE3_CBC;
+ public static final ASN1ObjectIdentifier rC2_CBC = PKCSObjectIdentifiers.RC2_CBC;
+
+ private ASN1Sequence capabilities;
+
+ /**
+ * return an Attribute object from the given object.
+ *
+ * @param o the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static SMIMECapabilities getInstance(
+ Object o)
+ {
+ if (o == null || o instanceof SMIMECapabilities)
+ {
+ return (SMIMECapabilities)o;
+ }
+
+ if (o instanceof ASN1Sequence)
+ {
+ return new SMIMECapabilities((ASN1Sequence)o);
+ }
+
+ if (o instanceof Attribute)
+ {
+ return new SMIMECapabilities(
+ (ASN1Sequence)(((Attribute)o).getAttrValues().getObjectAt(0)));
+ }
+
+ throw new IllegalArgumentException("unknown object in factory: " + o.getClass().getName());
+ }
+
+ public SMIMECapabilities(
+ ASN1Sequence seq)
+ {
+ capabilities = seq;
+ }
+
+ /**
+ * returns a vector with 0 or more objects of all the capabilities
+ * matching the passed in capability OID. If the OID passed is null the
+ * entire set is returned.
+ */
+ public Vector getCapabilities(
+ ASN1ObjectIdentifier capability)
+ {
+ Enumeration e = capabilities.getObjects();
+ Vector list = new Vector();
+
+ if (capability == null)
+ {
+ while (e.hasMoreElements())
+ {
+ SMIMECapability cap = SMIMECapability.getInstance(e.nextElement());
+
+ list.addElement(cap);
+ }
+ }
+ else
+ {
+ while (e.hasMoreElements())
+ {
+ SMIMECapability cap = SMIMECapability.getInstance(e.nextElement());
+
+ if (capability.equals(cap.getCapabilityID()))
+ {
+ list.addElement(cap);
+ }
+ }
+ }
+
+ return list;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * SMIMECapabilities ::= SEQUENCE OF SMIMECapability
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return capabilities;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/smime/SMIMECapabilitiesAttribute.java b/core/src/main/java/org/bouncycastle/asn1/smime/SMIMECapabilitiesAttribute.java
new file mode 100644
index 00000000..cfad31ec
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/smime/SMIMECapabilitiesAttribute.java
@@ -0,0 +1,16 @@
+package org.bouncycastle.asn1.smime;
+
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.cms.Attribute;
+
+public class SMIMECapabilitiesAttribute
+ extends Attribute
+{
+ public SMIMECapabilitiesAttribute(
+ SMIMECapabilityVector capabilities)
+ {
+ super(SMIMEAttributes.smimeCapabilities,
+ new DERSet(new DERSequence(capabilities.toASN1EncodableVector())));
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/smime/SMIMECapability.java b/core/src/main/java/org/bouncycastle/asn1/smime/SMIMECapability.java
new file mode 100644
index 00000000..f70d28ae
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/smime/SMIMECapability.java
@@ -0,0 +1,103 @@
+package org.bouncycastle.asn1.smime;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+
+public class SMIMECapability
+ extends ASN1Object
+{
+ /**
+ * general preferences
+ */
+ public static final ASN1ObjectIdentifier preferSignedData = PKCSObjectIdentifiers.preferSignedData;
+ public static final ASN1ObjectIdentifier canNotDecryptAny = PKCSObjectIdentifiers.canNotDecryptAny;
+ public static final ASN1ObjectIdentifier sMIMECapabilitiesVersions = PKCSObjectIdentifiers.sMIMECapabilitiesVersions;
+
+ /**
+ * encryption algorithms preferences
+ */
+ public static final ASN1ObjectIdentifier dES_CBC = new ASN1ObjectIdentifier("1.3.14.3.2.7");
+ public static final ASN1ObjectIdentifier dES_EDE3_CBC = PKCSObjectIdentifiers.des_EDE3_CBC;
+ public static final ASN1ObjectIdentifier rC2_CBC = PKCSObjectIdentifiers.RC2_CBC;
+ public static final ASN1ObjectIdentifier aES128_CBC = NISTObjectIdentifiers.id_aes128_CBC;
+ public static final ASN1ObjectIdentifier aES192_CBC = NISTObjectIdentifiers.id_aes192_CBC;
+ public static final ASN1ObjectIdentifier aES256_CBC = NISTObjectIdentifiers.id_aes256_CBC;
+
+ private ASN1ObjectIdentifier capabilityID;
+ private ASN1Encodable parameters;
+
+ public SMIMECapability(
+ ASN1Sequence seq)
+ {
+ capabilityID = (ASN1ObjectIdentifier)seq.getObjectAt(0);
+
+ if (seq.size() > 1)
+ {
+ parameters = (ASN1Primitive)seq.getObjectAt(1);
+ }
+ }
+
+ public SMIMECapability(
+ ASN1ObjectIdentifier capabilityID,
+ ASN1Encodable parameters)
+ {
+ this.capabilityID = capabilityID;
+ this.parameters = parameters;
+ }
+
+ public static SMIMECapability getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof SMIMECapability)
+ {
+ return (SMIMECapability)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new SMIMECapability((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid SMIMECapability");
+ }
+
+ public ASN1ObjectIdentifier getCapabilityID()
+ {
+ return capabilityID;
+ }
+
+ public ASN1Encodable getParameters()
+ {
+ return parameters;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * SMIMECapability ::= SEQUENCE {
+ * capabilityID OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY capabilityID OPTIONAL
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(capabilityID);
+
+ if (parameters != null)
+ {
+ v.add(parameters);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/smime/SMIMECapabilityVector.java b/core/src/main/java/org/bouncycastle/asn1/smime/SMIMECapabilityVector.java
new file mode 100644
index 00000000..965a9965
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/smime/SMIMECapabilityVector.java
@@ -0,0 +1,50 @@
+package org.bouncycastle.asn1.smime;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * Handler for creating a vector S/MIME Capabilities
+ */
+public class SMIMECapabilityVector
+{
+ private ASN1EncodableVector capabilities = new ASN1EncodableVector();
+
+ public void addCapability(
+ ASN1ObjectIdentifier capability)
+ {
+ capabilities.add(new DERSequence(capability));
+ }
+
+ public void addCapability(
+ ASN1ObjectIdentifier capability,
+ int value)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(capability);
+ v.add(new ASN1Integer(value));
+
+ capabilities.add(new DERSequence(v));
+ }
+
+ public void addCapability(
+ ASN1ObjectIdentifier capability,
+ ASN1Encodable params)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(capability);
+ v.add(params);
+
+ capabilities.add(new DERSequence(v));
+ }
+
+ public ASN1EncodableVector toASN1EncodableVector()
+ {
+ return capabilities;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/smime/SMIMEEncryptionKeyPreferenceAttribute.java b/core/src/main/java/org/bouncycastle/asn1/smime/SMIMEEncryptionKeyPreferenceAttribute.java
new file mode 100644
index 00000000..1e5b5396
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/smime/SMIMEEncryptionKeyPreferenceAttribute.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.asn1.smime;
+
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.cms.Attribute;
+import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
+import org.bouncycastle.asn1.cms.RecipientKeyIdentifier;
+
+/**
+ * The SMIMEEncryptionKeyPreference object.
+ * <pre>
+ * SMIMEEncryptionKeyPreference ::= CHOICE {
+ * issuerAndSerialNumber [0] IssuerAndSerialNumber,
+ * receipentKeyId [1] RecipientKeyIdentifier,
+ * subjectAltKeyIdentifier [2] SubjectKeyIdentifier
+ * }
+ * </pre>
+ */
+public class SMIMEEncryptionKeyPreferenceAttribute
+ extends Attribute
+{
+ public SMIMEEncryptionKeyPreferenceAttribute(
+ IssuerAndSerialNumber issAndSer)
+ {
+ super(SMIMEAttributes.encrypKeyPref,
+ new DERSet(new DERTaggedObject(false, 0, issAndSer)));
+ }
+
+ public SMIMEEncryptionKeyPreferenceAttribute(
+ RecipientKeyIdentifier rKeyId)
+ {
+
+ super(SMIMEAttributes.encrypKeyPref,
+ new DERSet(new DERTaggedObject(false, 1, rKeyId)));
+ }
+
+ /**
+ * @param sKeyId the subjectKeyIdentifier value (normally the X.509 one)
+ */
+ public SMIMEEncryptionKeyPreferenceAttribute(
+ ASN1OctetString sKeyId)
+ {
+
+ super(SMIMEAttributes.encrypKeyPref,
+ new DERSet(new DERTaggedObject(false, 2, sKeyId)));
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTNamedCurves.java b/core/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTNamedCurves.java
new file mode 100644
index 00000000..17f04919
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTNamedCurves.java
@@ -0,0 +1,351 @@
+package org.bouncycastle.asn1.teletrust;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.asn1.x9.X9ECParametersHolder;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Hex;
+
+/**
+ * elliptic curves defined in "ECC Brainpool Standard Curves and Curve Generation"
+ * http://www.ecc-brainpool.org/download/draft_pkix_additional_ecc_dp.txt
+ */
+public class TeleTrusTNamedCurves
+{
+ static X9ECParametersHolder brainpoolP160r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ ECCurve curve = new ECCurve.Fp(
+ new BigInteger("E95E4A5F737059DC60DFC7AD95B3D8139515620F", 16), // q
+ new BigInteger("340E7BE2A280EB74E2BE61BADA745D97E8F7C300", 16), // a
+ new BigInteger("1E589A8595423412134FAA2DBDEC95C8D8675E58", 16)); // b
+
+ return new X9ECParameters(
+ curve,
+ curve.decodePoint(Hex.decode("04BED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC31667CB477A1A8EC338F94741669C976316DA6321")), // G
+ new BigInteger("E95E4A5F737059DC60DF5991D45029409E60FC09", 16), //n
+ new BigInteger("01", 16)); // h
+ }
+ };
+
+ static X9ECParametersHolder brainpoolP160t1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ ECCurve curve = new ECCurve.Fp(
+ // new BigInteger("24DBFF5DEC9B986BBFE5295A29BFBAE45E0F5D0B", 16), // Z
+ new BigInteger("E95E4A5F737059DC60DFC7AD95B3D8139515620F", 16), // q
+ new BigInteger("E95E4A5F737059DC60DFC7AD95B3D8139515620C", 16), // a'
+ new BigInteger("7A556B6DAE535B7B51ED2C4D7DAA7A0B5C55F380", 16)); // b'
+
+ return new X9ECParameters(
+ curve,
+ curve.decodePoint(Hex.decode("04B199B13B9B34EFC1397E64BAEB05ACC265FF2378ADD6718B7C7C1961F0991B842443772152C9E0AD")), // G
+ new BigInteger("E95E4A5F737059DC60DF5991D45029409E60FC09", 16), //n
+ new BigInteger("01", 16)); // h
+ }
+ };
+
+ static X9ECParametersHolder brainpoolP192r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ ECCurve curve = new ECCurve.Fp(
+ new BigInteger("C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297", 16), // q
+ new BigInteger("6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF", 16), // a
+ new BigInteger("469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9", 16)); // b
+
+ return new X9ECParameters(
+ curve,
+ curve.decodePoint(Hex.decode("04C0A0647EAAB6A48753B033C56CB0F0900A2F5C4853375FD614B690866ABD5BB88B5F4828C1490002E6773FA2FA299B8F")), // G
+ new BigInteger("C302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1", 16), //n
+ new BigInteger("01", 16)); // h
+ }
+ };
+
+ static X9ECParametersHolder brainpoolP192t1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ ECCurve curve = new ECCurve.Fp(
+ //new BigInteger("1B6F5CC8DB4DC7AF19458A9CB80DC2295E5EB9C3732104CB") //Z
+ new BigInteger("C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297", 16), // q
+ new BigInteger("C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86294", 16), // a'
+ new BigInteger("13D56FFAEC78681E68F9DEB43B35BEC2FB68542E27897B79", 16)); // b'
+
+ return new X9ECParameters(
+ curve,
+ curve.decodePoint(Hex.decode("043AE9E58C82F63C30282E1FE7BBF43FA72C446AF6F4618129097E2C5667C2223A902AB5CA449D0084B7E5B3DE7CCC01C9")), // G'
+ new BigInteger("C302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1", 16), //n
+ new BigInteger("01", 16)); // h
+ }
+ };
+
+ static X9ECParametersHolder brainpoolP224r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ ECCurve curve = new ECCurve.Fp(
+ new BigInteger("D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF", 16), // q
+ new BigInteger("68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43", 16), // a
+ new BigInteger("2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B", 16)); // b
+
+ return new X9ECParameters(
+ curve,
+ curve.decodePoint(Hex.decode("040D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD")), // G
+ new BigInteger("D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F", 16), //n
+ new BigInteger("01", 16)); // n
+ }
+ };
+ static X9ECParametersHolder brainpoolP224t1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ ECCurve curve = new ECCurve.Fp(
+ //new BigInteger("2DF271E14427A346910CF7A2E6CFA7B3F484E5C2CCE1C8B730E28B3F") //Z
+ new BigInteger("D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF", 16), // q
+ new BigInteger("D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FC", 16), // a'
+ new BigInteger("4B337D934104CD7BEF271BF60CED1ED20DA14C08B3BB64F18A60888D", 16)); // b'
+
+ return new X9ECParameters(
+ curve,
+ curve.decodePoint(Hex.decode("046AB1E344CE25FF3896424E7FFE14762ECB49F8928AC0C76029B4D5800374E9F5143E568CD23F3F4D7C0D4B1E41C8CC0D1C6ABD5F1A46DB4C")), // G'
+ new BigInteger("D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F", 16), //n
+ new BigInteger("01", 16)); // h
+ }
+ };
+ static X9ECParametersHolder brainpoolP256r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ ECCurve curve = new ECCurve.Fp(
+ new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377", 16), // q
+ new BigInteger("7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9", 16), // a
+ new BigInteger("26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6", 16)); // b
+
+ return new X9ECParameters(
+ curve,
+ curve.decodePoint(Hex.decode("048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997")), // G
+ new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7", 16), //n
+ new BigInteger("01", 16)); // h
+ }
+ };
+ static X9ECParametersHolder brainpoolP256t1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ ECCurve curve = new ECCurve.Fp(
+ //new BigInteger("3E2D4BD9597B58639AE7AA669CAB9837CF5CF20A2C852D10F655668DFC150EF0") //Z
+ new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377", 16), // q
+ new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5374", 16), // a'
+ new BigInteger("662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04", 16)); // b'
+
+ return new X9ECParameters(
+ curve,
+ curve.decodePoint(Hex.decode("04A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE")), // G'
+ new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7", 16), //n
+ new BigInteger("01", 16)); // h
+ }
+ };
+ static X9ECParametersHolder brainpoolP320r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ ECCurve curve = new ECCurve.Fp(
+ new BigInteger("D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E27", 16), // q
+ new BigInteger("3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9F492F375A97D860EB4", 16), // a
+ new BigInteger("520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD884539816F5EB4AC8FB1F1A6", 16)); // b
+
+ return new X9ECParameters(
+ curve,
+ curve.decodePoint(Hex.decode("0443BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599C710AF8D0D39E2061114FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6AC7D35245D1692E8EE1")), // G
+ new BigInteger("D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D482EC7EE8658E98691555B44C59311", 16), //n
+ new BigInteger("01", 16)); // h
+ }
+ };
+ static X9ECParametersHolder brainpoolP320t1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ ECCurve curve = new ECCurve.Fp(
+ //new BigInteger("15F75CAF668077F7E85B42EB01F0A81FF56ECD6191D55CB82B7D861458A18FEFC3E5AB7496F3C7B1") //Z
+ new BigInteger("D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E27", 16), // q
+ new BigInteger("D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E24", 16), // a'
+ new BigInteger("A7F561E038EB1ED560B3D147DB782013064C19F27ED27C6780AAF77FB8A547CEB5B4FEF422340353", 16)); // b'
+
+ return new X9ECParameters(
+ curve,
+ curve.decodePoint(Hex.decode("04925BE9FB01AFC6FB4D3E7D4990010F813408AB106C4F09CB7EE07868CC136FFF3357F624A21BED5263BA3A7A27483EBF6671DBEF7ABB30EBEE084E58A0B077AD42A5A0989D1EE71B1B9BC0455FB0D2C3")), // G'
+ new BigInteger("D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D482EC7EE8658E98691555B44C59311", 16), //n
+ new BigInteger("01", 16)); // h
+ }
+ };
+ static X9ECParametersHolder brainpoolP384r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ ECCurve curve = new ECCurve.Fp(
+ new BigInteger("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53", 16), // q
+ new BigInteger("7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503AD4EB04A8C7DD22CE2826", 16), // a
+ new BigInteger("4A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DBC9943AB78696FA504C11", 16)); // b
+
+ return new X9ECParameters(
+ curve,
+ curve.decodePoint(Hex.decode("041D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315")), // G
+ new BigInteger("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565", 16), //n
+ new BigInteger("01", 16)); // h
+ }
+ };
+ static X9ECParametersHolder brainpoolP384t1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ ECCurve curve = new ECCurve.Fp(
+ //new BigInteger("41DFE8DD399331F7166A66076734A89CD0D2BCDB7D068E44E1F378F41ECBAE97D2D63DBC87BCCDDCCC5DA39E8589291C") //Z
+ new BigInteger("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53", 16), // q
+ new BigInteger("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC50", 16), // a'
+ new BigInteger("7F519EADA7BDA81BD826DBA647910F8C4B9346ED8CCDC64E4B1ABD11756DCE1D2074AA263B88805CED70355A33B471EE", 16)); // b'
+
+ return new X9ECParameters(
+ curve,
+ curve.decodePoint(Hex.decode("0418DE98B02DB9A306F2AFCD7235F72A819B80AB12EBD653172476FECD462AABFFC4FF191B946A5F54D8D0AA2F418808CC25AB056962D30651A114AFD2755AD336747F93475B7A1FCA3B88F2B6A208CCFE469408584DC2B2912675BF5B9E582928")), // G'
+ new BigInteger("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565", 16), //n
+ new BigInteger("01", 16)); // h
+ }
+ };
+ static X9ECParametersHolder brainpoolP512r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ ECCurve curve = new ECCurve.Fp(
+ new BigInteger("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3", 16), // q
+ new BigInteger("7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA", 16), // a
+ new BigInteger("3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723", 16)); // b
+
+ return new X9ECParameters(
+ curve,
+ curve.decodePoint(Hex.decode("0481AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F8227DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892")), // G
+ new BigInteger("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069", 16), //n
+ new BigInteger("01", 16)); // h
+ }
+ };
+ static X9ECParametersHolder brainpoolP512t1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ ECCurve curve = new ECCurve.Fp(
+ //new BigInteger("12EE58E6764838B69782136F0F2D3BA06E27695716054092E60A80BEDB212B64E585D90BCE13761F85C3F1D2A64E3BE8FEA2220F01EBA5EEB0F35DBD29D922AB") //Z
+ new BigInteger("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3", 16), // q
+ new BigInteger("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F0", 16), // a'
+ new BigInteger("7CBBBCF9441CFAB76E1890E46884EAE321F70C0BCB4981527897504BEC3E36A62BCDFA2304976540F6450085F2DAE145C22553B465763689180EA2571867423E", 16)); // b'
+
+ return new X9ECParameters(
+ curve,
+ curve.decodePoint(Hex.decode("04640ECE5C12788717B9C1BA06CBC2A6FEBA85842458C56DDE9DB1758D39C0313D82BA51735CDB3EA499AA77A7D6943A64F7A3F25FE26F06B51BAA2696FA9035DA5B534BD595F5AF0FA2C892376C84ACE1BB4E3019B71634C01131159CAE03CEE9D9932184BEEF216BD71DF2DADF86A627306ECFF96DBB8BACE198B61E00F8B332")), // G'
+ new BigInteger("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069", 16), //n
+ new BigInteger("01", 16)); // h
+ }
+ };
+
+ static final Hashtable objIds = new Hashtable();
+ static final Hashtable curves = new Hashtable();
+ static final Hashtable names = new Hashtable();
+
+ static void defineCurve(String name, ASN1ObjectIdentifier oid, X9ECParametersHolder holder)
+ {
+ objIds.put(name, oid);
+ names.put(oid, name);
+ curves.put(oid, holder);
+ }
+
+ static
+ {
+ defineCurve("brainpoolp160r1", TeleTrusTObjectIdentifiers.brainpoolP160r1, brainpoolP160r1);
+ defineCurve("brainpoolp160t1", TeleTrusTObjectIdentifiers.brainpoolP160t1, brainpoolP160t1);
+ defineCurve("brainpoolp192r1", TeleTrusTObjectIdentifiers.brainpoolP192r1, brainpoolP192r1);
+ defineCurve("brainpoolp192t1", TeleTrusTObjectIdentifiers.brainpoolP192t1, brainpoolP192t1);
+ defineCurve("brainpoolp224r1", TeleTrusTObjectIdentifiers.brainpoolP224r1, brainpoolP224r1);
+ defineCurve("brainpoolp224t1", TeleTrusTObjectIdentifiers.brainpoolP224t1, brainpoolP224t1);
+ defineCurve("brainpoolp256r1", TeleTrusTObjectIdentifiers.brainpoolP256r1, brainpoolP256r1);
+ defineCurve("brainpoolp256t1", TeleTrusTObjectIdentifiers.brainpoolP256t1, brainpoolP256t1);
+ defineCurve("brainpoolp320r1", TeleTrusTObjectIdentifiers.brainpoolP320r1, brainpoolP320r1);
+ defineCurve("brainpoolp320t1", TeleTrusTObjectIdentifiers.brainpoolP320t1, brainpoolP320t1);
+ defineCurve("brainpoolp384r1", TeleTrusTObjectIdentifiers.brainpoolP384r1, brainpoolP384r1);
+ defineCurve("brainpoolp384t1", TeleTrusTObjectIdentifiers.brainpoolP384t1, brainpoolP384t1);
+ defineCurve("brainpoolp512r1", TeleTrusTObjectIdentifiers.brainpoolP512r1, brainpoolP512r1);
+ defineCurve("brainpoolp512t1", TeleTrusTObjectIdentifiers.brainpoolP512t1, brainpoolP512t1);
+ }
+
+ public static X9ECParameters getByName(
+ String name)
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)objIds.get(Strings.toLowerCase(name));
+
+ if (oid != null)
+ {
+ return getByOID(oid);
+ }
+
+ return null;
+ }
+
+ /**
+ * return the X9ECParameters object for the named curve represented by
+ * the passed in object identifier. Null if the curve isn't present.
+ *
+ * @param oid an object identifier representing a named curve, if present.
+ */
+ public static X9ECParameters getByOID(
+ ASN1ObjectIdentifier oid)
+ {
+ X9ECParametersHolder holder = (X9ECParametersHolder)curves.get(oid);
+
+ if (holder != null)
+ {
+ return holder.getParameters();
+ }
+
+ return null;
+ }
+
+ /**
+ * return the object identifier signified by the passed in name. Null
+ * if there is no object identifier associated with name.
+ *
+ * @return the object identifier associated with name, if present.
+ */
+ public static ASN1ObjectIdentifier getOID(
+ String name)
+ {
+ return (ASN1ObjectIdentifier)objIds.get(Strings.toLowerCase(name));
+ }
+
+ /**
+ * return the named curve name represented by the given object identifier.
+ */
+ public static String getName(
+ ASN1ObjectIdentifier oid)
+ {
+ return (String)names.get(oid);
+ }
+
+ /**
+ * returns an enumeration containing the name strings for curves
+ * contained in this structure.
+ */
+ public static Enumeration getNames()
+ {
+ return objIds.keys();
+ }
+
+ public static ASN1ObjectIdentifier getOID(short curvesize, boolean twisted)
+ {
+ return getOID("brainpoolP" + curvesize + (twisted ? "t" : "r") + "1");
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java
new file mode 100644
index 00000000..df9a0ffd
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java
@@ -0,0 +1,42 @@
+package org.bouncycastle.asn1.teletrust;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface TeleTrusTObjectIdentifiers
+{
+ static final ASN1ObjectIdentifier teleTrusTAlgorithm = new ASN1ObjectIdentifier("1.3.36.3");
+
+ static final ASN1ObjectIdentifier ripemd160 = teleTrusTAlgorithm.branch("2.1");
+ static final ASN1ObjectIdentifier ripemd128 = teleTrusTAlgorithm.branch("2.2");
+ static final ASN1ObjectIdentifier ripemd256 = teleTrusTAlgorithm.branch("2.3");
+
+ static final ASN1ObjectIdentifier teleTrusTRSAsignatureAlgorithm = teleTrusTAlgorithm.branch("3.1");
+
+ static final ASN1ObjectIdentifier rsaSignatureWithripemd160 = teleTrusTRSAsignatureAlgorithm.branch("2");
+ static final ASN1ObjectIdentifier rsaSignatureWithripemd128 = teleTrusTRSAsignatureAlgorithm.branch("3");
+ static final ASN1ObjectIdentifier rsaSignatureWithripemd256 = teleTrusTRSAsignatureAlgorithm.branch("4");
+
+ static final ASN1ObjectIdentifier ecSign = teleTrusTAlgorithm.branch("3.2");
+
+ static final ASN1ObjectIdentifier ecSignWithSha1 = ecSign.branch("1");
+ static final ASN1ObjectIdentifier ecSignWithRipemd160 = ecSign.branch("2");
+
+ static final ASN1ObjectIdentifier ecc_brainpool = teleTrusTAlgorithm.branch("3.2.8");
+ static final ASN1ObjectIdentifier ellipticCurve = ecc_brainpool.branch("1");
+ static final ASN1ObjectIdentifier versionOne = ellipticCurve.branch("1");
+
+ static final ASN1ObjectIdentifier brainpoolP160r1 = versionOne.branch("1");
+ static final ASN1ObjectIdentifier brainpoolP160t1 = versionOne.branch("2");
+ static final ASN1ObjectIdentifier brainpoolP192r1 = versionOne.branch("3");
+ static final ASN1ObjectIdentifier brainpoolP192t1 = versionOne.branch("4");
+ static final ASN1ObjectIdentifier brainpoolP224r1 = versionOne.branch("5");
+ static final ASN1ObjectIdentifier brainpoolP224t1 = versionOne.branch("6");
+ static final ASN1ObjectIdentifier brainpoolP256r1 = versionOne.branch("7");
+ static final ASN1ObjectIdentifier brainpoolP256t1 = versionOne.branch("8");
+ static final ASN1ObjectIdentifier brainpoolP320r1 = versionOne.branch("9");
+ static final ASN1ObjectIdentifier brainpoolP320t1 = versionOne.branch("10");
+ static final ASN1ObjectIdentifier brainpoolP384r1 = versionOne.branch("11");
+ static final ASN1ObjectIdentifier brainpoolP384t1 = versionOne.branch("12");
+ static final ASN1ObjectIdentifier brainpoolP512r1 = versionOne.branch("13");
+ static final ASN1ObjectIdentifier brainpoolP512t1 = versionOne.branch("14");
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/tsp/Accuracy.java b/core/src/main/java/org/bouncycastle/asn1/tsp/Accuracy.java
new file mode 100644
index 00000000..fb7763d1
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/tsp/Accuracy.java
@@ -0,0 +1,173 @@
+package org.bouncycastle.asn1.tsp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+
+public class Accuracy
+ extends ASN1Object
+{
+ ASN1Integer seconds;
+
+ ASN1Integer millis;
+
+ ASN1Integer micros;
+
+ // constantes
+ protected static final int MIN_MILLIS = 1;
+
+ protected static final int MAX_MILLIS = 999;
+
+ protected static final int MIN_MICROS = 1;
+
+ protected static final int MAX_MICROS = 999;
+
+ protected Accuracy()
+ {
+ }
+
+ public Accuracy(
+ ASN1Integer seconds,
+ ASN1Integer millis,
+ ASN1Integer micros)
+ {
+ this.seconds = seconds;
+
+ //Verifications
+ if (millis != null
+ && (millis.getValue().intValue() < MIN_MILLIS || millis
+ .getValue().intValue() > MAX_MILLIS))
+ {
+ throw new IllegalArgumentException(
+ "Invalid millis field : not in (1..999)");
+ }
+ else
+ {
+ this.millis = millis;
+ }
+
+ if (micros != null
+ && (micros.getValue().intValue() < MIN_MICROS || micros
+ .getValue().intValue() > MAX_MICROS))
+ {
+ throw new IllegalArgumentException(
+ "Invalid micros field : not in (1..999)");
+ }
+ else
+ {
+ this.micros = micros;
+ }
+
+ }
+
+ private Accuracy(ASN1Sequence seq)
+ {
+ seconds = null;
+ millis = null;
+ micros = null;
+
+ for (int i = 0; i < seq.size(); i++)
+ {
+ // seconds
+ if (seq.getObjectAt(i) instanceof ASN1Integer)
+ {
+ seconds = (ASN1Integer) seq.getObjectAt(i);
+ }
+ else if (seq.getObjectAt(i) instanceof DERTaggedObject)
+ {
+ DERTaggedObject extra = (DERTaggedObject) seq.getObjectAt(i);
+
+ switch (extra.getTagNo())
+ {
+ case 0:
+ millis = ASN1Integer.getInstance(extra, false);
+ if (millis.getValue().intValue() < MIN_MILLIS
+ || millis.getValue().intValue() > MAX_MILLIS)
+ {
+ throw new IllegalArgumentException(
+ "Invalid millis field : not in (1..999).");
+ }
+ break;
+ case 1:
+ micros = ASN1Integer.getInstance(extra, false);
+ if (micros.getValue().intValue() < MIN_MICROS
+ || micros.getValue().intValue() > MAX_MICROS)
+ {
+ throw new IllegalArgumentException(
+ "Invalid micros field : not in (1..999).");
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Invalig tag number");
+ }
+ }
+ }
+ }
+
+ public static Accuracy getInstance(Object o)
+ {
+ if (o instanceof Accuracy)
+ {
+ return (Accuracy) o;
+ }
+
+ if (o != null)
+ {
+ return new Accuracy(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public ASN1Integer getSeconds()
+ {
+ return seconds;
+ }
+
+ public ASN1Integer getMillis()
+ {
+ return millis;
+ }
+
+ public ASN1Integer getMicros()
+ {
+ return micros;
+ }
+
+ /**
+ * <pre>
+ * Accuracy ::= SEQUENCE {
+ * seconds INTEGER OPTIONAL,
+ * millis [0] INTEGER (1..999) OPTIONAL,
+ * micros [1] INTEGER (1..999) OPTIONAL
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (seconds != null)
+ {
+ v.add(seconds);
+ }
+
+ if (millis != null)
+ {
+ v.add(new DERTaggedObject(false, 0, millis));
+ }
+
+ if (micros != null)
+ {
+ v.add(new DERTaggedObject(false, 1, micros));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/tsp/MessageImprint.java b/core/src/main/java/org/bouncycastle/asn1/tsp/MessageImprint.java
new file mode 100644
index 00000000..b551fcfb
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/tsp/MessageImprint.java
@@ -0,0 +1,78 @@
+package org.bouncycastle.asn1.tsp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class MessageImprint
+ extends ASN1Object
+{
+ AlgorithmIdentifier hashAlgorithm;
+ byte[] hashedMessage;
+
+ /**
+ * @param o
+ * @return a MessageImprint object.
+ */
+ public static MessageImprint getInstance(Object o)
+ {
+ if (o instanceof MessageImprint)
+ {
+ return (MessageImprint)o;
+ }
+
+ if (o != null)
+ {
+ return new MessageImprint(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ private MessageImprint(
+ ASN1Sequence seq)
+ {
+ this.hashAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(0));
+ this.hashedMessage = ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets();
+ }
+
+ public MessageImprint(
+ AlgorithmIdentifier hashAlgorithm,
+ byte[] hashedMessage)
+ {
+ this.hashAlgorithm = hashAlgorithm;
+ this.hashedMessage = hashedMessage;
+ }
+
+ public AlgorithmIdentifier getHashAlgorithm()
+ {
+ return hashAlgorithm;
+ }
+
+ public byte[] getHashedMessage()
+ {
+ return hashedMessage;
+ }
+
+ /**
+ * <pre>
+ * MessageImprint ::= SEQUENCE {
+ * hashAlgorithm AlgorithmIdentifier,
+ * hashedMessage OCTET STRING }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(hashAlgorithm);
+ v.add(new DEROctetString(hashedMessage));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/tsp/TSTInfo.java b/core/src/main/java/org/bouncycastle/asn1/tsp/TSTInfo.java
new file mode 100644
index 00000000..312224e5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/tsp/TSTInfo.java
@@ -0,0 +1,233 @@
+package org.bouncycastle.asn1.tsp;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Boolean;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.GeneralName;
+
+public class TSTInfo
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private ASN1ObjectIdentifier tsaPolicyId;
+ private MessageImprint messageImprint;
+ private ASN1Integer serialNumber;
+ private ASN1GeneralizedTime genTime;
+ private Accuracy accuracy;
+ private ASN1Boolean ordering;
+ private ASN1Integer nonce;
+ private GeneralName tsa;
+ private Extensions extensions;
+
+ public static TSTInfo getInstance(Object o)
+ {
+ if (o instanceof TSTInfo)
+ {
+ return (TSTInfo)o;
+ }
+ else if (o != null)
+ {
+ return new TSTInfo(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ private TSTInfo(ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ // version
+ version = ASN1Integer.getInstance(e.nextElement());
+
+ // tsaPolicy
+ tsaPolicyId = ASN1ObjectIdentifier.getInstance(e.nextElement());
+
+ // messageImprint
+ messageImprint = MessageImprint.getInstance(e.nextElement());
+
+ // serialNumber
+ serialNumber = ASN1Integer.getInstance(e.nextElement());
+
+ // genTime
+ genTime = ASN1GeneralizedTime.getInstance(e.nextElement());
+
+ // default for ordering
+ ordering = ASN1Boolean.getInstance(false);
+
+ while (e.hasMoreElements())
+ {
+ ASN1Object o = (ASN1Object) e.nextElement();
+
+ if (o instanceof ASN1TaggedObject)
+ {
+ DERTaggedObject tagged = (DERTaggedObject) o;
+
+ switch (tagged.getTagNo())
+ {
+ case 0:
+ tsa = GeneralName.getInstance(tagged, true);
+ break;
+ case 1:
+ extensions = Extensions.getInstance(tagged, false);
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown tag value " + tagged.getTagNo());
+ }
+ }
+ else if (o instanceof ASN1Sequence || o instanceof Accuracy)
+ {
+ accuracy = Accuracy.getInstance(o);
+ }
+ else if (o instanceof ASN1Boolean)
+ {
+ ordering = ASN1Boolean.getInstance(o);
+ }
+ else if (o instanceof ASN1Integer)
+ {
+ nonce = ASN1Integer.getInstance(o);
+ }
+
+ }
+ }
+
+ public TSTInfo(ASN1ObjectIdentifier tsaPolicyId, MessageImprint messageImprint,
+ ASN1Integer serialNumber, ASN1GeneralizedTime genTime,
+ Accuracy accuracy, ASN1Boolean ordering, ASN1Integer nonce,
+ GeneralName tsa, Extensions extensions)
+ {
+ version = new ASN1Integer(1);
+ this.tsaPolicyId = tsaPolicyId;
+ this.messageImprint = messageImprint;
+ this.serialNumber = serialNumber;
+ this.genTime = genTime;
+
+ this.accuracy = accuracy;
+ this.ordering = ordering;
+ this.nonce = nonce;
+ this.tsa = tsa;
+ this.extensions = extensions;
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public MessageImprint getMessageImprint()
+ {
+ return messageImprint;
+ }
+
+ public ASN1ObjectIdentifier getPolicy()
+ {
+ return tsaPolicyId;
+ }
+
+ public ASN1Integer getSerialNumber()
+ {
+ return serialNumber;
+ }
+
+ public Accuracy getAccuracy()
+ {
+ return accuracy;
+ }
+
+ public ASN1GeneralizedTime getGenTime()
+ {
+ return genTime;
+ }
+
+ public ASN1Boolean getOrdering()
+ {
+ return ordering;
+ }
+
+ public ASN1Integer getNonce()
+ {
+ return nonce;
+ }
+
+ public GeneralName getTsa()
+ {
+ return tsa;
+ }
+
+ public Extensions getExtensions()
+ {
+ return extensions;
+ }
+
+ /**
+ * <pre>
+ *
+ * TSTInfo ::= SEQUENCE {
+ * version INTEGER { v1(1) },
+ * policy TSAPolicyId,
+ * messageImprint MessageImprint,
+ * -- MUST have the same value as the similar field in
+ * -- TimeStampReq
+ * serialNumber INTEGER,
+ * -- Time-Stamping users MUST be ready to accommodate integers
+ * -- up to 160 bits.
+ * genTime GeneralizedTime,
+ * accuracy Accuracy OPTIONAL,
+ * ordering BOOLEAN DEFAULT FALSE,
+ * nonce INTEGER OPTIONAL,
+ * -- MUST be present if the similar field was present
+ * -- in TimeStampReq. In that case it MUST have the same value.
+ * tsa [0] GeneralName OPTIONAL,
+ * extensions [1] IMPLICIT Extensions OPTIONAL }
+ *
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector seq = new ASN1EncodableVector();
+ seq.add(version);
+
+ seq.add(tsaPolicyId);
+ seq.add(messageImprint);
+ seq.add(serialNumber);
+ seq.add(genTime);
+
+ if (accuracy != null)
+ {
+ seq.add(accuracy);
+ }
+
+ if (ordering != null && ordering.isTrue())
+ {
+ seq.add(ordering);
+ }
+
+ if (nonce != null)
+ {
+ seq.add(nonce);
+ }
+
+ if (tsa != null)
+ {
+ seq.add(new DERTaggedObject(true, 0, tsa));
+ }
+
+ if (extensions != null)
+ {
+ seq.add(new DERTaggedObject(false, 1, extensions));
+ }
+
+ return new DERSequence(seq);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/tsp/TimeStampReq.java b/core/src/main/java/org/bouncycastle/asn1/tsp/TimeStampReq.java
new file mode 100644
index 00000000..44490f02
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/tsp/TimeStampReq.java
@@ -0,0 +1,179 @@
+package org.bouncycastle.asn1.tsp;
+
+import org.bouncycastle.asn1.ASN1Boolean;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.Extensions;
+
+public class TimeStampReq
+ extends ASN1Object
+{
+ ASN1Integer version;
+
+ MessageImprint messageImprint;
+
+ ASN1ObjectIdentifier tsaPolicy;
+
+ ASN1Integer nonce;
+
+ ASN1Boolean certReq;
+
+ Extensions extensions;
+
+ public static TimeStampReq getInstance(Object o)
+ {
+ if (o instanceof TimeStampReq)
+ {
+ return (TimeStampReq) o;
+ }
+ else if (o != null)
+ {
+ return new TimeStampReq(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ private TimeStampReq(ASN1Sequence seq)
+ {
+ int nbObjects = seq.size();
+
+ int seqStart = 0;
+
+ // version
+ version = ASN1Integer.getInstance(seq.getObjectAt(seqStart));
+
+ seqStart++;
+
+ // messageImprint
+ messageImprint = MessageImprint.getInstance(seq.getObjectAt(seqStart));
+
+ seqStart++;
+
+ for (int opt = seqStart; opt < nbObjects; opt++)
+ {
+ // tsaPolicy
+ if (seq.getObjectAt(opt) instanceof ASN1ObjectIdentifier)
+ {
+ tsaPolicy = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(opt));
+ }
+ // nonce
+ else if (seq.getObjectAt(opt) instanceof ASN1Integer)
+ {
+ nonce = ASN1Integer.getInstance(seq.getObjectAt(opt));
+ }
+ // certReq
+ else if (seq.getObjectAt(opt) instanceof ASN1Boolean)
+ {
+ certReq = ASN1Boolean.getInstance(seq.getObjectAt(opt));
+ }
+ // extensions
+ else if (seq.getObjectAt(opt) instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject tagged = (ASN1TaggedObject)seq.getObjectAt(opt);
+ if (tagged.getTagNo() == 0)
+ {
+ extensions = Extensions.getInstance(tagged, false);
+ }
+ }
+ }
+ }
+
+ public TimeStampReq(
+ MessageImprint messageImprint,
+ ASN1ObjectIdentifier tsaPolicy,
+ ASN1Integer nonce,
+ ASN1Boolean certReq,
+ Extensions extensions)
+ {
+ // default
+ version = new ASN1Integer(1);
+
+ this.messageImprint = messageImprint;
+ this.tsaPolicy = tsaPolicy;
+ this.nonce = nonce;
+ this.certReq = certReq;
+ this.extensions = extensions;
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public MessageImprint getMessageImprint()
+ {
+ return messageImprint;
+ }
+
+ public ASN1ObjectIdentifier getReqPolicy()
+ {
+ return tsaPolicy;
+ }
+
+ public ASN1Integer getNonce()
+ {
+ return nonce;
+ }
+
+ public ASN1Boolean getCertReq()
+ {
+ return certReq;
+ }
+
+ public Extensions getExtensions()
+ {
+ return extensions;
+ }
+
+ /**
+ * <pre>
+ * TimeStampReq ::= SEQUENCE {
+ * version INTEGER { v1(1) },
+ * messageImprint MessageImprint,
+ * --a hash algorithm OID and the hash value of the data to be
+ * --time-stamped
+ * reqPolicy TSAPolicyId OPTIONAL,
+ * nonce INTEGER OPTIONAL,
+ * certReq BOOLEAN DEFAULT FALSE,
+ * extensions [0] IMPLICIT Extensions OPTIONAL
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+ v.add(messageImprint);
+
+ if (tsaPolicy != null)
+ {
+ v.add(tsaPolicy);
+ }
+
+ if (nonce != null)
+ {
+ v.add(nonce);
+ }
+
+ if (certReq != null && certReq.isTrue())
+ {
+ v.add(certReq);
+ }
+
+ if (extensions != null)
+ {
+ v.add(new DERTaggedObject(false, 0, extensions));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/tsp/TimeStampResp.java b/core/src/main/java/org/bouncycastle/asn1/tsp/TimeStampResp.java
new file mode 100644
index 00000000..96d08a12
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/tsp/TimeStampResp.java
@@ -0,0 +1,84 @@
+package org.bouncycastle.asn1.tsp;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.cmp.PKIStatusInfo;
+import org.bouncycastle.asn1.cms.ContentInfo;
+
+
+public class TimeStampResp
+ extends ASN1Object
+{
+ PKIStatusInfo pkiStatusInfo;
+
+ ContentInfo timeStampToken;
+
+ public static TimeStampResp getInstance(Object o)
+ {
+ if (o instanceof TimeStampResp)
+ {
+ return (TimeStampResp) o;
+ }
+ else if (o != null)
+ {
+ return new TimeStampResp(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ private TimeStampResp(ASN1Sequence seq)
+ {
+
+ Enumeration e = seq.getObjects();
+
+ // status
+ pkiStatusInfo = PKIStatusInfo.getInstance(e.nextElement());
+
+ if (e.hasMoreElements())
+ {
+ timeStampToken = ContentInfo.getInstance(e.nextElement());
+ }
+ }
+
+ public TimeStampResp(PKIStatusInfo pkiStatusInfo, ContentInfo timeStampToken)
+ {
+ this.pkiStatusInfo = pkiStatusInfo;
+ this.timeStampToken = timeStampToken;
+ }
+
+ public PKIStatusInfo getStatus()
+ {
+ return pkiStatusInfo;
+ }
+
+ public ContentInfo getTimeStampToken()
+ {
+ return timeStampToken;
+ }
+
+ /**
+ * <pre>
+ * TimeStampResp ::= SEQUENCE {
+ * status PKIStatusInfo,
+ * timeStampToken TimeStampToken OPTIONAL }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(pkiStatusInfo);
+ if (timeStampToken != null)
+ {
+ v.add(timeStampToken);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145BinaryField.java b/core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145BinaryField.java
new file mode 100644
index 00000000..a0cca6b4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145BinaryField.java
@@ -0,0 +1,119 @@
+package org.bouncycastle.asn1.ua;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class DSTU4145BinaryField
+ extends ASN1Object
+{
+
+ private int m, k, j, l;
+
+ private DSTU4145BinaryField(ASN1Sequence seq)
+ {
+ m = ASN1Integer.getInstance(seq.getObjectAt(0)).getPositiveValue().intValue();
+
+ if (seq.getObjectAt(1) instanceof ASN1Integer)
+ {
+ k = ((ASN1Integer)seq.getObjectAt(1)).getPositiveValue().intValue();
+ }
+ else if (seq.getObjectAt(1) instanceof ASN1Sequence)
+ {
+ ASN1Sequence coefs = ASN1Sequence.getInstance(seq.getObjectAt(1));
+
+ k = ASN1Integer.getInstance(coefs.getObjectAt(0)).getPositiveValue().intValue();
+ j = ASN1Integer.getInstance(coefs.getObjectAt(1)).getPositiveValue().intValue();
+ l = ASN1Integer.getInstance(coefs.getObjectAt(2)).getPositiveValue().intValue();
+ }
+ else
+ {
+ throw new IllegalArgumentException("object parse error");
+ }
+ }
+
+ public static DSTU4145BinaryField getInstance(Object obj)
+ {
+ if (obj instanceof DSTU4145BinaryField)
+ {
+ return (DSTU4145BinaryField)obj;
+ }
+
+ if (obj != null)
+ {
+ return new DSTU4145BinaryField(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public DSTU4145BinaryField(int m, int k1, int k2, int k3)
+ {
+ this.m = m;
+ this.k = k1;
+ this.j = k2;
+ this.l = k3;
+ }
+
+ public int getM()
+ {
+ return m;
+ }
+
+ public int getK1()
+ {
+ return k;
+ }
+
+ public int getK2()
+ {
+ return j;
+ }
+
+ public int getK3()
+ {
+ return l;
+ }
+
+ public DSTU4145BinaryField(int m, int k)
+ {
+ this(m, k, 0, 0);
+ }
+
+ /**
+ * BinaryField ::= SEQUENCE {
+ * M INTEGER,
+ * CHOICE {Trinomial, Pentanomial}
+ * Trinomial::= INTEGER
+ * Pentanomial::= SEQUENCE {
+ * k INTEGER,
+ * j INTEGER,
+ * l INTEGER}
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new ASN1Integer(m));
+ if (j == 0) //Trinomial
+ {
+ v.add(new ASN1Integer(k));
+ }
+ else
+ {
+ ASN1EncodableVector coefs = new ASN1EncodableVector();
+ coefs.add(new ASN1Integer(k));
+ coefs.add(new ASN1Integer(j));
+ coefs.add(new ASN1Integer(l));
+
+ v.add(new DERSequence(coefs));
+ }
+
+ return new DERSequence(v);
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145ECBinary.java b/core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145ECBinary.java
new file mode 100644
index 00000000..11c2af48
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145ECBinary.java
@@ -0,0 +1,144 @@
+package org.bouncycastle.asn1.ua;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x9.X9IntegerConverter;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.util.Arrays;
+
+public class DSTU4145ECBinary
+ extends ASN1Object
+{
+
+ BigInteger version = BigInteger.valueOf(0);
+
+ DSTU4145BinaryField f;
+ ASN1Integer a;
+ ASN1OctetString b;
+ ASN1Integer n;
+ ASN1OctetString bp;
+
+ public DSTU4145ECBinary(ECDomainParameters params)
+ {
+ if (!(params.getCurve() instanceof ECCurve.F2m))
+ {
+ throw new IllegalArgumentException("only binary domain is possible");
+ }
+
+ // We always use big-endian in parameter encoding
+ ECCurve.F2m curve = (ECCurve.F2m)params.getCurve();
+ f = new DSTU4145BinaryField(curve.getM(), curve.getK1(), curve.getK2(), curve.getK3());
+ a = new ASN1Integer(curve.getA().toBigInteger());
+ X9IntegerConverter converter = new X9IntegerConverter();
+ b = new DEROctetString(converter.integerToBytes(curve.getB().toBigInteger(), converter.getByteLength(curve)));
+ n = new ASN1Integer(params.getN());
+ bp = new DEROctetString(DSTU4145PointEncoder.encodePoint(params.getG()));
+ }
+
+ private DSTU4145ECBinary(ASN1Sequence seq)
+ {
+ int index = 0;
+
+ if (seq.getObjectAt(index) instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject taggedVersion = (ASN1TaggedObject)seq.getObjectAt(index);
+ if (taggedVersion.isExplicit() && 0 == taggedVersion.getTagNo())
+ {
+ version = ASN1Integer.getInstance(taggedVersion.getLoadedObject()).getValue();
+ index++;
+ }
+ else
+ {
+ throw new IllegalArgumentException("object parse error");
+ }
+ }
+ f = DSTU4145BinaryField.getInstance(seq.getObjectAt(index));
+ index++;
+ a = ASN1Integer.getInstance(seq.getObjectAt(index));
+ index++;
+ b = ASN1OctetString.getInstance(seq.getObjectAt(index));
+ index++;
+ n = ASN1Integer.getInstance(seq.getObjectAt(index));
+ index++;
+ bp = ASN1OctetString.getInstance(seq.getObjectAt(index));
+ }
+
+ public static DSTU4145ECBinary getInstance(Object obj)
+ {
+ if (obj instanceof DSTU4145ECBinary)
+ {
+ return (DSTU4145ECBinary)obj;
+ }
+
+ if (obj != null)
+ {
+ return new DSTU4145ECBinary(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public DSTU4145BinaryField getField()
+ {
+ return f;
+ }
+
+ public BigInteger getA()
+ {
+ return a.getValue();
+ }
+
+ public byte[] getB()
+ {
+ return Arrays.clone(b.getOctets());
+ }
+
+ public BigInteger getN()
+ {
+ return n.getValue();
+ }
+
+ public byte[] getG()
+ {
+ return Arrays.clone(bp.getOctets());
+ }
+
+ /**
+ * ECBinary ::= SEQUENCE {
+ * version [0] EXPLICIT INTEGER DEFAULT 0,
+ * f BinaryField,
+ * a INTEGER (0..1),
+ * b OCTET STRING,
+ * n INTEGER,
+ * bp OCTET STRING}
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (0 != version.compareTo(BigInteger.valueOf(0)))
+ {
+ v.add(new DERTaggedObject(true, 0, new ASN1Integer(version)));
+ }
+ v.add(f);
+ v.add(a);
+ v.add(b);
+ v.add(n);
+ v.add(bp);
+
+ return new DERSequence(v);
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145NamedCurves.java b/core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145NamedCurves.java
new file mode 100644
index 00000000..353c196d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145NamedCurves.java
@@ -0,0 +1,94 @@
+package org.bouncycastle.asn1.ua;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECPoint;
+
+public class DSTU4145NamedCurves
+{
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ public static final ECDomainParameters[] params = new ECDomainParameters[10];
+ static final ASN1ObjectIdentifier[] oids = new ASN1ObjectIdentifier[10];
+
+ //All named curves have the following oid format: 1.2.804.2.1.1.1.1.3.1.1.2.X
+ //where X is the curve number 0-9
+ static final String oidBase = UAObjectIdentifiers.dstu4145le.getId() + ".2.";
+
+ static
+ {
+ ECCurve.F2m[] curves = new ECCurve.F2m[10];
+ curves[0] = new ECCurve.F2m(163, 3, 6, 7, ONE, new BigInteger("5FF6108462A2DC8210AB403925E638A19C1455D21", 16));
+ curves[1] = new ECCurve.F2m(167, 6, ONE, new BigInteger("6EE3CEEB230811759F20518A0930F1A4315A827DAC", 16));
+ curves[2] = new ECCurve.F2m(173, 1, 2, 10, ZERO, new BigInteger("108576C80499DB2FC16EDDF6853BBB278F6B6FB437D9", 16));
+ curves[3] = new ECCurve.F2m(179, 1, 2, 4, ONE, new BigInteger("4A6E0856526436F2F88DD07A341E32D04184572BEB710", 16));
+ curves[4] = new ECCurve.F2m(191, 9, ONE, new BigInteger("7BC86E2102902EC4D5890E8B6B4981ff27E0482750FEFC03", 16));
+ curves[5] = new ECCurve.F2m(233, 1, 4, 9, ONE, new BigInteger("06973B15095675534C7CF7E64A21BD54EF5DD3B8A0326AA936ECE454D2C", 16));
+ curves[6] = new ECCurve.F2m(257, 12, ZERO, new BigInteger("1CEF494720115657E18F938D7A7942394FF9425C1458C57861F9EEA6ADBE3BE10", 16));
+ curves[7] = new ECCurve.F2m(307, 2, 4, 8, ONE, new BigInteger("393C7F7D53666B5054B5E6C6D3DE94F4296C0C599E2E2E241050DF18B6090BDC90186904968BB", 16));
+ curves[8] = new ECCurve.F2m(367, 21, ONE, new BigInteger("43FC8AD242B0B7A6F3D1627AD5654447556B47BF6AA4A64B0C2AFE42CADAB8F93D92394C79A79755437B56995136", 16));
+ curves[9] = new ECCurve.F2m(431, 1, 3, 5, ONE, new BigInteger("03CE10490F6A708FC26DFE8C3D27C4F94E690134D5BFF988D8D28AAEAEDE975936C66BAC536B18AE2DC312CA493117DAA469C640CAF3", 16));
+
+ ECPoint[] points = new ECPoint[10];
+ points[0] = curves[0].createPoint(new BigInteger("2E2F85F5DD74CE983A5C4237229DAF8A3F35823BE", 16), new BigInteger("3826F008A8C51D7B95284D9D03FF0E00CE2CD723A", 16), false);
+ points[1] = curves[1].createPoint(new BigInteger("7A1F6653786A68192803910A3D30B2A2018B21CD54", 16), new BigInteger("5F49EB26781C0EC6B8909156D98ED435E45FD59918", 16), false);
+ points[2] = curves[2].createPoint(new BigInteger("4D41A619BCC6EADF0448FA22FAD567A9181D37389CA", 16), new BigInteger("10B51CC12849B234C75E6DD2028BF7FF5C1CE0D991A1", 16), false);
+ points[3] = curves[3].createPoint(new BigInteger("6BA06FE51464B2BD26DC57F48819BA9954667022C7D03", 16), new BigInteger("25FBC363582DCEC065080CA8287AAFF09788A66DC3A9E", 16), false);
+ points[4] = curves[4].createPoint(new BigInteger("714114B762F2FF4A7912A6D2AC58B9B5C2FCFE76DAEB7129", 16), new BigInteger("29C41E568B77C617EFE5902F11DB96FA9613CD8D03DB08DA", 16), false);
+ points[5] = curves[5].createPoint(new BigInteger("3FCDA526B6CDF83BA1118DF35B3C31761D3545F32728D003EEB25EFE96", 16), new BigInteger("9CA8B57A934C54DEEDA9E54A7BBAD95E3B2E91C54D32BE0B9DF96D8D35", 16), false);
+ points[6] = curves[6].createPoint(new BigInteger("02A29EF207D0E9B6C55CD260B306C7E007AC491CA1B10C62334A9E8DCD8D20FB7", 16), new BigInteger("10686D41FF744D4449FCCF6D8EEA03102E6812C93A9D60B978B702CF156D814EF", 16), false);
+ points[7] = curves[7].createPoint(new BigInteger("216EE8B189D291A0224984C1E92F1D16BF75CCD825A087A239B276D3167743C52C02D6E7232AA", 16), new BigInteger("5D9306BACD22B7FAEB09D2E049C6E2866C5D1677762A8F2F2DC9A11C7F7BE8340AB2237C7F2A0", 16), false);
+ points[8] = curves[8].createPoint(new BigInteger("324A6EDDD512F08C49A99AE0D3F961197A76413E7BE81A400CA681E09639B5FE12E59A109F78BF4A373541B3B9A1", 16), new BigInteger("1AB597A5B4477F59E39539007C7F977D1A567B92B043A49C6B61984C3FE3481AAF454CD41BA1F051626442B3C10", 16), false);
+ points[9] = curves[9].createPoint(new BigInteger("1A62BA79D98133A16BBAE7ED9A8E03C32E0824D57AEF72F88986874E5AAE49C27BED49A2A95058068426C2171E99FD3B43C5947C857D", 16), new BigInteger("70B5E1E14031C1F70BBEFE96BDDE66F451754B4CA5F48DA241F331AA396B8D1839A855C1769B1EA14BA53308B5E2723724E090E02DB9", 16), false);
+
+ BigInteger[] n_s = new BigInteger[10];
+ n_s[0] = new BigInteger("400000000000000000002BEC12BE2262D39BCF14D", 16);
+ n_s[1] = new BigInteger("3FFFFFFFFFFFFFFFFFFFFFB12EBCC7D7F29FF7701F", 16);
+ n_s[2] = new BigInteger("800000000000000000000189B4E67606E3825BB2831", 16);
+ n_s[3] = new BigInteger("3FFFFFFFFFFFFFFFFFFFFFFB981960435FE5AB64236EF", 16);
+ n_s[4] = new BigInteger("40000000000000000000000069A779CAC1DABC6788F7474F", 16);
+ n_s[5] = new BigInteger("1000000000000000000000000000013E974E72F8A6922031D2603CFE0D7", 16);
+ n_s[6] = new BigInteger("800000000000000000000000000000006759213AF182E987D3E17714907D470D", 16);
+ n_s[7] = new BigInteger("3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC079C2F3825DA70D390FBBA588D4604022B7B7", 16);
+ n_s[8] = new BigInteger("40000000000000000000000000000000000000000000009C300B75A3FA824F22428FD28CE8812245EF44049B2D49", 16);
+ n_s[9] = new BigInteger("3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBA3175458009A8C0A724F02F81AA8A1FCBAF80D90C7A95110504CF", 16);
+
+ for (int i = 0; i < params.length; i++)
+ {
+ params[i] = new ECDomainParameters(curves[i], points[i], n_s[i]);
+ }
+
+ for (int i = 0; i < oids.length; i++)
+ {
+ oids[i] = new ASN1ObjectIdentifier(oidBase + i);
+ }
+ }
+
+ /**
+ * All named curves have the following oid format: 1.2.804.2.1.1.1.1.3.1.1.2.X
+ * where X is the curve number 0-9
+ */
+ public static ASN1ObjectIdentifier[] getOIDs()
+ {
+ return oids;
+ }
+
+ /**
+ * All named curves have the following oid format: 1.2.804.2.1.1.1.1.3.1.1.2.X
+ * where X is the curve number 0-9
+ */
+ public static ECDomainParameters getByOID(ASN1ObjectIdentifier oid)
+ {
+ String oidStr = oid.getId();
+ if (oidStr.startsWith(oidBase))
+ {
+ int index = Integer.parseInt(oidStr.substring(oidStr.length() - 1));
+ return params[index];
+ }
+ return null;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145Params.java b/core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145Params.java
new file mode 100644
index 00000000..c425d73b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145Params.java
@@ -0,0 +1,121 @@
+package org.bouncycastle.asn1.ua;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+
+public class DSTU4145Params
+ extends ASN1Object
+{
+ private static final byte DEFAULT_DKE[] = {
+ (byte)0xa9, (byte)0xd6, (byte)0xeb, 0x45, (byte)0xf1, 0x3c, 0x70, (byte)0x82,
+ (byte)0x80, (byte)0xc4, (byte)0x96, 0x7b, 0x23, 0x1f, 0x5e, (byte)0xad,
+ (byte)0xf6, 0x58, (byte)0xeb, (byte)0xa4, (byte)0xc0, 0x37, 0x29, 0x1d,
+ 0x38, (byte)0xd9, 0x6b, (byte)0xf0, 0x25, (byte)0xca, 0x4e, 0x17,
+ (byte)0xf8, (byte)0xe9, 0x72, 0x0d, (byte)0xc6, 0x15, (byte)0xb4, 0x3a,
+ 0x28, (byte)0x97, 0x5f, 0x0b, (byte)0xc1, (byte)0xde, (byte)0xa3, 0x64,
+ 0x38, (byte)0xb5, 0x64, (byte)0xea, 0x2c, 0x17, (byte)0x9f, (byte)0xd0,
+ 0x12, 0x3e, 0x6d, (byte)0xb8, (byte)0xfa, (byte)0xc5, 0x79, 0x04};
+
+
+ private ASN1ObjectIdentifier namedCurve;
+ private DSTU4145ECBinary ecbinary;
+ private byte[] dke = DEFAULT_DKE;
+
+ public DSTU4145Params(ASN1ObjectIdentifier namedCurve)
+ {
+ this.namedCurve = namedCurve;
+ }
+
+ public DSTU4145Params(DSTU4145ECBinary ecbinary)
+ {
+ this.ecbinary = ecbinary;
+ }
+
+ public boolean isNamedCurve()
+ {
+ return namedCurve != null;
+ }
+
+ public DSTU4145ECBinary getECBinary()
+ {
+ return ecbinary;
+ }
+
+ public byte[] getDKE()
+ {
+ return dke;
+ }
+
+ public static byte[] getDefaultDKE()
+ {
+ return DEFAULT_DKE;
+ }
+
+ public ASN1ObjectIdentifier getNamedCurve()
+ {
+ return namedCurve;
+ }
+
+ public static DSTU4145Params getInstance(Object obj)
+ {
+ if (obj instanceof DSTU4145Params)
+ {
+ return (DSTU4145Params)obj;
+ }
+
+ if (obj != null)
+ {
+ ASN1Sequence seq = ASN1Sequence.getInstance(obj);
+ DSTU4145Params params;
+
+ if (seq.getObjectAt(0) instanceof ASN1ObjectIdentifier)
+ {
+ params = new DSTU4145Params(ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0)));
+ }
+ else
+ {
+ params = new DSTU4145Params(DSTU4145ECBinary.getInstance(seq.getObjectAt(0)));
+ }
+
+ if (seq.size() == 2)
+ {
+ params.dke = ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets();
+ if (params.dke.length != DSTU4145Params.DEFAULT_DKE.length)
+ {
+ throw new IllegalArgumentException("object parse error");
+ }
+ }
+
+ return params;
+ }
+
+ throw new IllegalArgumentException("object parse error");
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (namedCurve != null)
+ {
+ v.add(namedCurve);
+ }
+ else
+ {
+ v.add(ecbinary);
+ }
+
+ if (!org.bouncycastle.util.Arrays.areEqual(dke, DEFAULT_DKE))
+ {
+ v.add(new DEROctetString(dke));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145PointEncoder.java b/core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145PointEncoder.java
new file mode 100644
index 00000000..0227d2ad
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145PointEncoder.java
@@ -0,0 +1,162 @@
+package org.bouncycastle.asn1.ua;
+
+import java.math.BigInteger;
+import java.util.Random;
+
+import org.bouncycastle.asn1.x9.X9IntegerConverter;
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * DSTU4145 encodes points somewhat differently than X9.62
+ * It compresses the point to the size of the field element
+ */
+
+public abstract class DSTU4145PointEncoder
+{
+
+ private static X9IntegerConverter converter = new X9IntegerConverter();
+
+ private static BigInteger trace(ECFieldElement fe)
+ {
+ ECFieldElement t = fe;
+ for (int i = 0; i < fe.getFieldSize() - 1; i++)
+ {
+ t = t.square().add(fe);
+ }
+ return t.toBigInteger();
+ }
+
+ /**
+ * Solves a quadratic equation <code>z<sup>2</sup> + z = beta</code>(X9.62
+ * D.1.6) The other solution is <code>z + 1</code>.
+ *
+ * @param beta The value to solve the qradratic equation for.
+ * @return the solution for <code>z<sup>2</sup> + z = beta</code> or
+ * <code>null</code> if no solution exists.
+ */
+ private static ECFieldElement solveQuadradicEquation(ECFieldElement beta)
+ {
+ ECFieldElement.F2m b = (ECFieldElement.F2m)beta;
+ ECFieldElement zeroElement = new ECFieldElement.F2m(
+ b.getM(), b.getK1(), b.getK2(), b.getK3(), ECConstants.ZERO);
+
+ if (beta.toBigInteger().equals(ECConstants.ZERO))
+ {
+ return zeroElement;
+ }
+
+ ECFieldElement z = null;
+ ECFieldElement gamma = zeroElement;
+
+ Random rand = new Random();
+ int m = b.getM();
+ do
+ {
+ ECFieldElement t = new ECFieldElement.F2m(b.getM(), b.getK1(),
+ b.getK2(), b.getK3(), new BigInteger(m, rand));
+ z = zeroElement;
+ ECFieldElement w = beta;
+ for (int i = 1; i <= m - 1; i++)
+ {
+ ECFieldElement w2 = w.square();
+ z = z.square().add(w2.multiply(t));
+ w = w2.add(beta);
+ }
+ if (!w.toBigInteger().equals(ECConstants.ZERO))
+ {
+ return null;
+ }
+ gamma = z.square().add(z);
+ }
+ while (gamma.toBigInteger().equals(ECConstants.ZERO));
+
+ return z;
+ }
+
+ public static byte[] encodePoint(ECPoint Q)
+ {
+ /*if (!Q.isCompressed())
+ Q=new ECPoint.F2m(Q.getCurve(),Q.getX(),Q.getY(),true);
+
+ byte[] bytes=Q.getEncoded();
+
+ if (bytes[0]==0x02)
+ bytes[bytes.length-1]&=0xFE;
+ else if (bytes[0]==0x02)
+ bytes[bytes.length-1]|=0x01;
+
+ return Arrays.copyOfRange(bytes, 1, bytes.length);*/
+
+ int byteCount = converter.getByteLength(Q.getX());
+ byte[] bytes = converter.integerToBytes(Q.getX().toBigInteger(), byteCount);
+
+ if (!(Q.getX().toBigInteger().equals(ECConstants.ZERO)))
+ {
+ ECFieldElement y = Q.getY().multiply(Q.getX().invert());
+ if (trace(y).equals(ECConstants.ONE))
+ {
+ bytes[bytes.length - 1] |= 0x01;
+ }
+ else
+ {
+ bytes[bytes.length - 1] &= 0xFE;
+ }
+ }
+
+ return bytes;
+ }
+
+ public static ECPoint decodePoint(ECCurve curve, byte[] bytes)
+ {
+ /*byte[] bp_enc=new byte[bytes.length+1];
+ if (0==(bytes[bytes.length-1]&0x1))
+ bp_enc[0]=0x02;
+ else
+ bp_enc[0]=0x03;
+ System.arraycopy(bytes, 0, bp_enc, 1, bytes.length);
+ if (!trace(curve.fromBigInteger(new BigInteger(1, bytes))).equals(curve.getA().toBigInteger()))
+ bp_enc[bp_enc.length-1]^=0x01;
+
+ return curve.decodePoint(bp_enc);*/
+
+ BigInteger k = BigInteger.valueOf(bytes[bytes.length - 1] & 0x1);
+ if (!trace(curve.fromBigInteger(new BigInteger(1, bytes))).equals(curve.getA().toBigInteger()))
+ {
+ bytes = Arrays.clone(bytes);
+ bytes[bytes.length - 1] ^= 0x01;
+ }
+ ECCurve.F2m c = (ECCurve.F2m)curve;
+ ECFieldElement xp = curve.fromBigInteger(new BigInteger(1, bytes));
+ ECFieldElement yp = null;
+ if (xp.toBigInteger().equals(ECConstants.ZERO))
+ {
+ yp = (ECFieldElement.F2m)curve.getB();
+ for (int i = 0; i < c.getM() - 1; i++)
+ {
+ yp = yp.square();
+ }
+ }
+ else
+ {
+ ECFieldElement beta = xp.add(curve.getA()).add(
+ curve.getB().multiply(xp.square().invert()));
+ ECFieldElement z = solveQuadradicEquation(beta);
+ if (z == null)
+ {
+ throw new RuntimeException("Invalid point compression");
+ }
+ if (!trace(z).equals(k))
+ {
+ z = z.add(curve.fromBigInteger(ECConstants.ONE));
+ }
+ yp = xp.multiply(z);
+ }
+
+ return new ECPoint.F2m(curve, xp, yp);
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145PublicKey.java b/core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145PublicKey.java
new file mode 100644
index 00000000..769eff66
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ua/DSTU4145PublicKey.java
@@ -0,0 +1,46 @@
+package org.bouncycastle.asn1.ua;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.math.ec.ECPoint;
+
+public class DSTU4145PublicKey
+ extends ASN1Object
+{
+
+ private ASN1OctetString pubKey;
+
+ public DSTU4145PublicKey(ECPoint pubKey)
+ {
+ // We always use big-endian in parameter encoding
+ this.pubKey = new DEROctetString(DSTU4145PointEncoder.encodePoint(pubKey));
+ }
+
+ private DSTU4145PublicKey(ASN1OctetString ocStr)
+ {
+ pubKey = ocStr;
+ }
+
+ public static DSTU4145PublicKey getInstance(Object obj)
+ {
+ if (obj instanceof DSTU4145PublicKey)
+ {
+ return (DSTU4145PublicKey)obj;
+ }
+
+ if (obj != null)
+ {
+ return new DSTU4145PublicKey(ASN1OctetString.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return pubKey;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/ua/UAObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/ua/UAObjectIdentifiers.java
new file mode 100644
index 00000000..046bc6f7
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/ua/UAObjectIdentifiers.java
@@ -0,0 +1,16 @@
+package org.bouncycastle.asn1.ua;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface UAObjectIdentifiers
+{
+ // Ukrainian object identifiers
+ // {iso(1) member-body(2) Ukraine(804 ) root(2) security(1) cryptography(1) pki(1)}
+
+ static final ASN1ObjectIdentifier UaOid = new ASN1ObjectIdentifier("1.2.804.2.1.1.1");
+
+ // {pki-alg(1) pki-alg-�sym(3) Dstu4145WithGost34311(1) PB(1)}
+ // DSTU4145 in polynomial basis has 2 oids, one for little-endian representation and one for big-endian
+ static final ASN1ObjectIdentifier dstu4145le = UaOid.branch("1.3.1.1");
+ static final ASN1ObjectIdentifier dstu4145be = UaOid.branch("1.3.1.1.1.1");
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java b/core/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java
new file mode 100644
index 00000000..5302552b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java
@@ -0,0 +1,404 @@
+package org.bouncycastle.asn1.util;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.BERApplicationSpecific;
+import org.bouncycastle.asn1.BERConstructedOctetString;
+import org.bouncycastle.asn1.BEROctetString;
+import org.bouncycastle.asn1.BERSequence;
+import org.bouncycastle.asn1.BERSet;
+import org.bouncycastle.asn1.BERTaggedObject;
+import org.bouncycastle.asn1.BERTags;
+import org.bouncycastle.asn1.DERApplicationSpecific;
+import org.bouncycastle.asn1.DERBMPString;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERBoolean;
+import org.bouncycastle.asn1.DEREnumerated;
+import org.bouncycastle.asn1.DERExternal;
+import org.bouncycastle.asn1.DERGeneralizedTime;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DERPrintableString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERT61String;
+import org.bouncycastle.asn1.DERUTCTime;
+import org.bouncycastle.asn1.DERUTF8String;
+import org.bouncycastle.asn1.DERVisibleString;
+import org.bouncycastle.util.encoders.Hex;
+
+public class ASN1Dump
+{
+ private static final String TAB = " ";
+ private static final int SAMPLE_SIZE = 32;
+
+ /**
+ * dump a DER object as a formatted string with indentation
+ *
+ * @param obj the ASN1Primitive to be dumped out.
+ */
+ static void _dumpAsString(
+ String indent,
+ boolean verbose,
+ ASN1Primitive obj,
+ StringBuffer buf)
+ {
+ String nl = System.getProperty("line.separator");
+ if (obj instanceof ASN1Sequence)
+ {
+ Enumeration e = ((ASN1Sequence)obj).getObjects();
+ String tab = indent + TAB;
+
+ buf.append(indent);
+ if (obj instanceof BERSequence)
+ {
+ buf.append("BER Sequence");
+ }
+ else if (obj instanceof DERSequence)
+ {
+ buf.append("DER Sequence");
+ }
+ else
+ {
+ buf.append("Sequence");
+ }
+
+ buf.append(nl);
+
+ while (e.hasMoreElements())
+ {
+ Object o = e.nextElement();
+
+ if (o == null || o.equals(DERNull.INSTANCE))
+ {
+ buf.append(tab);
+ buf.append("NULL");
+ buf.append(nl);
+ }
+ else if (o instanceof ASN1Primitive)
+ {
+ _dumpAsString(tab, verbose, (ASN1Primitive)o, buf);
+ }
+ else
+ {
+ _dumpAsString(tab, verbose, ((ASN1Encodable)o).toASN1Primitive(), buf);
+ }
+ }
+ }
+ else if (obj instanceof ASN1TaggedObject)
+ {
+ String tab = indent + TAB;
+
+ buf.append(indent);
+ if (obj instanceof BERTaggedObject)
+ {
+ buf.append("BER Tagged [");
+ }
+ else
+ {
+ buf.append("Tagged [");
+ }
+
+ ASN1TaggedObject o = (ASN1TaggedObject)obj;
+
+ buf.append(Integer.toString(o.getTagNo()));
+ buf.append(']');
+
+ if (!o.isExplicit())
+ {
+ buf.append(" IMPLICIT ");
+ }
+
+ buf.append(nl);
+
+ if (o.isEmpty())
+ {
+ buf.append(tab);
+ buf.append("EMPTY");
+ buf.append(nl);
+ }
+ else
+ {
+ _dumpAsString(tab, verbose, o.getObject(), buf);
+ }
+ }
+ else if (obj instanceof ASN1Set)
+ {
+ Enumeration e = ((ASN1Set)obj).getObjects();
+ String tab = indent + TAB;
+
+ buf.append(indent);
+
+ if (obj instanceof BERSet)
+ {
+ buf.append("BER Set");
+ }
+ else
+ {
+ buf.append("DER Set");
+ }
+
+ buf.append(nl);
+
+ while (e.hasMoreElements())
+ {
+ Object o = e.nextElement();
+
+ if (o == null)
+ {
+ buf.append(tab);
+ buf.append("NULL");
+ buf.append(nl);
+ }
+ else if (o instanceof ASN1Primitive)
+ {
+ _dumpAsString(tab, verbose, (ASN1Primitive)o, buf);
+ }
+ else
+ {
+ _dumpAsString(tab, verbose, ((ASN1Encodable)o).toASN1Primitive(), buf);
+ }
+ }
+ }
+ else if (obj instanceof ASN1OctetString)
+ {
+ ASN1OctetString oct = (ASN1OctetString)obj;
+
+ if (obj instanceof BEROctetString || obj instanceof BERConstructedOctetString)
+ {
+ buf.append(indent + "BER Constructed Octet String" + "[" + oct.getOctets().length + "] ");
+ }
+ else
+ {
+ buf.append(indent + "DER Octet String" + "[" + oct.getOctets().length + "] ");
+ }
+ if (verbose)
+ {
+ buf.append(dumpBinaryDataAsString(indent, oct.getOctets()));
+ }
+ else
+ {
+ buf.append(nl);
+ }
+ }
+ else if (obj instanceof ASN1ObjectIdentifier)
+ {
+ buf.append(indent + "ObjectIdentifier(" + ((ASN1ObjectIdentifier)obj).getId() + ")" + nl);
+ }
+ else if (obj instanceof DERBoolean)
+ {
+ buf.append(indent + "Boolean(" + ((DERBoolean)obj).isTrue() + ")" + nl);
+ }
+ else if (obj instanceof ASN1Integer)
+ {
+ buf.append(indent + "Integer(" + ((ASN1Integer)obj).getValue() + ")" + nl);
+ }
+ else if (obj instanceof DERBitString)
+ {
+ DERBitString bt = (DERBitString)obj;
+ buf.append(indent + "DER Bit String" + "[" + bt.getBytes().length + ", " + bt.getPadBits() + "] ");
+ if (verbose)
+ {
+ buf.append(dumpBinaryDataAsString(indent, bt.getBytes()));
+ }
+ else
+ {
+ buf.append(nl);
+ }
+ }
+ else if (obj instanceof DERIA5String)
+ {
+ buf.append(indent + "IA5String(" + ((DERIA5String)obj).getString() + ") " + nl);
+ }
+ else if (obj instanceof DERUTF8String)
+ {
+ buf.append(indent + "UTF8String(" + ((DERUTF8String)obj).getString() + ") " + nl);
+ }
+ else if (obj instanceof DERPrintableString)
+ {
+ buf.append(indent + "PrintableString(" + ((DERPrintableString)obj).getString() + ") " + nl);
+ }
+ else if (obj instanceof DERVisibleString)
+ {
+ buf.append(indent + "VisibleString(" + ((DERVisibleString)obj).getString() + ") " + nl);
+ }
+ else if (obj instanceof DERBMPString)
+ {
+ buf.append(indent + "BMPString(" + ((DERBMPString)obj).getString() + ") " + nl);
+ }
+ else if (obj instanceof DERT61String)
+ {
+ buf.append(indent + "T61String(" + ((DERT61String)obj).getString() + ") " + nl);
+ }
+ else if (obj instanceof DERUTCTime)
+ {
+ buf.append(indent + "UTCTime(" + ((DERUTCTime)obj).getTime() + ") " + nl);
+ }
+ else if (obj instanceof DERGeneralizedTime)
+ {
+ buf.append(indent + "GeneralizedTime(" + ((DERGeneralizedTime)obj).getTime() + ") " + nl);
+ }
+ else if (obj instanceof BERApplicationSpecific)
+ {
+ buf.append(outputApplicationSpecific("BER", indent, verbose, obj, nl));
+ }
+ else if (obj instanceof DERApplicationSpecific)
+ {
+ buf.append(outputApplicationSpecific("DER", indent, verbose, obj, nl));
+ }
+ else if (obj instanceof DEREnumerated)
+ {
+ DEREnumerated en = (DEREnumerated) obj;
+ buf.append(indent + "DER Enumerated(" + en.getValue() + ")" + nl);
+ }
+ else if (obj instanceof DERExternal)
+ {
+ DERExternal ext = (DERExternal) obj;
+ buf.append(indent + "External " + nl);
+ String tab = indent + TAB;
+ if (ext.getDirectReference() != null)
+ {
+ buf.append(tab + "Direct Reference: " + ext.getDirectReference().getId() + nl);
+ }
+ if (ext.getIndirectReference() != null)
+ {
+ buf.append(tab + "Indirect Reference: " + ext.getIndirectReference().toString() + nl);
+ }
+ if (ext.getDataValueDescriptor() != null)
+ {
+ _dumpAsString(tab, verbose, ext.getDataValueDescriptor(), buf);
+ }
+ buf.append(tab + "Encoding: " + ext.getEncoding() + nl);
+ _dumpAsString(tab, verbose, ext.getExternalContent(), buf);
+ }
+ else
+ {
+ buf.append(indent + obj.toString() + nl);
+ }
+ }
+
+ private static String outputApplicationSpecific(String type, String indent, boolean verbose, ASN1Primitive obj, String nl)
+ {
+ DERApplicationSpecific app = (DERApplicationSpecific)obj;
+ StringBuffer buf = new StringBuffer();
+
+ if (app.isConstructed())
+ {
+ try
+ {
+ ASN1Sequence s = ASN1Sequence.getInstance(app.getObject(BERTags.SEQUENCE));
+ buf.append(indent + type + " ApplicationSpecific[" + app.getApplicationTag() + "]" + nl);
+ for (Enumeration e = s.getObjects(); e.hasMoreElements();)
+ {
+ _dumpAsString(indent + TAB, verbose, (ASN1Primitive)e.nextElement(), buf);
+ }
+ }
+ catch (IOException e)
+ {
+ buf.append(e);
+ }
+ return buf.toString();
+ }
+
+ return indent + type + " ApplicationSpecific[" + app.getApplicationTag() + "] (" + new String(Hex.encode(app.getContents())) + ")" + nl;
+ }
+
+ /**
+ * dump out a DER object as a formatted string, in non-verbose mode.
+ *
+ * @param obj the ASN1Primitive to be dumped out.
+ * @return the resulting string.
+ */
+ public static String dumpAsString(
+ Object obj)
+ {
+ return dumpAsString(obj, false);
+ }
+
+ /**
+ * Dump out the object as a string.
+ *
+ * @param obj the object to be dumped
+ * @param verbose if true, dump out the contents of octet and bit strings.
+ * @return the resulting string.
+ */
+ public static String dumpAsString(
+ Object obj,
+ boolean verbose)
+ {
+ StringBuffer buf = new StringBuffer();
+
+ if (obj instanceof ASN1Primitive)
+ {
+ _dumpAsString("", verbose, (ASN1Primitive)obj, buf);
+ }
+ else if (obj instanceof ASN1Encodable)
+ {
+ _dumpAsString("", verbose, ((ASN1Encodable)obj).toASN1Primitive(), buf);
+ }
+ else
+ {
+ return "unknown object type " + obj.toString();
+ }
+
+ return buf.toString();
+ }
+
+ private static String dumpBinaryDataAsString(String indent, byte[] bytes)
+ {
+ String nl = System.getProperty("line.separator");
+ StringBuffer buf = new StringBuffer();
+
+ indent += TAB;
+
+ buf.append(nl);
+ for (int i = 0; i < bytes.length; i += SAMPLE_SIZE)
+ {
+ if (bytes.length - i > SAMPLE_SIZE)
+ {
+ buf.append(indent);
+ buf.append(new String(Hex.encode(bytes, i, SAMPLE_SIZE)));
+ buf.append(TAB);
+ buf.append(calculateAscString(bytes, i, SAMPLE_SIZE));
+ buf.append(nl);
+ }
+ else
+ {
+ buf.append(indent);
+ buf.append(new String(Hex.encode(bytes, i, bytes.length - i)));
+ for (int j = bytes.length - i; j != SAMPLE_SIZE; j++)
+ {
+ buf.append(" ");
+ }
+ buf.append(TAB);
+ buf.append(calculateAscString(bytes, i, bytes.length - i));
+ buf.append(nl);
+ }
+ }
+
+ return buf.toString();
+ }
+
+ private static String calculateAscString(byte[] bytes, int off, int len)
+ {
+ StringBuffer buf = new StringBuffer();
+
+ for (int i = off; i != off + len; i++)
+ {
+ if (bytes[i] >= ' ' && bytes[i] <= '~')
+ {
+ buf.append((char)bytes[i]);
+ }
+ }
+
+ return buf.toString();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/util/DERDump.java b/core/src/main/java/org/bouncycastle/asn1/util/DERDump.java
new file mode 100644
index 00000000..78875ff2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/util/DERDump.java
@@ -0,0 +1,41 @@
+package org.bouncycastle.asn1.util;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Primitive;
+
+/**
+ * @deprecated use ASN1Dump.
+ */
+public class DERDump
+ extends ASN1Dump
+{
+ /**
+ * dump out a DER object as a formatted string
+ *
+ * @param obj the ASN1Primitive to be dumped out.
+ */
+ public static String dumpAsString(
+ ASN1Primitive obj)
+ {
+ StringBuffer buf = new StringBuffer();
+
+ _dumpAsString("", false, obj, buf);
+
+ return buf.toString();
+ }
+
+ /**
+ * dump out a DER object as a formatted string
+ *
+ * @param obj the ASN1Primitive to be dumped out.
+ */
+ public static String dumpAsString(
+ ASN1Encodable obj)
+ {
+ StringBuffer buf = new StringBuffer();
+
+ _dumpAsString("", false, obj.toASN1Primitive(), buf);
+
+ return buf.toString();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/util/Dump.java b/core/src/main/java/org/bouncycastle/asn1/util/Dump.java
new file mode 100644
index 00000000..27a37f34
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/util/Dump.java
@@ -0,0 +1,22 @@
+package org.bouncycastle.asn1.util;
+
+import java.io.FileInputStream;
+
+import org.bouncycastle.asn1.ASN1InputStream;
+
+public class Dump
+{
+ public static void main(
+ String args[])
+ throws Exception
+ {
+ FileInputStream fIn = new FileInputStream(args[0]);
+ ASN1InputStream bIn = new ASN1InputStream(fIn);
+ Object obj = null;
+
+ while ((obj = bIn.readObject()) != null)
+ {
+ System.out.println(ASN1Dump.dumpAsString(obj));
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x500/AttributeTypeAndValue.java b/core/src/main/java/org/bouncycastle/asn1/x500/AttributeTypeAndValue.java
new file mode 100644
index 00000000..7f283f97
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x500/AttributeTypeAndValue.java
@@ -0,0 +1,72 @@
+package org.bouncycastle.asn1.x500;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class AttributeTypeAndValue
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier type;
+ private ASN1Encodable value;
+
+ private AttributeTypeAndValue(ASN1Sequence seq)
+ {
+ type = (ASN1ObjectIdentifier)seq.getObjectAt(0);
+ value = (ASN1Encodable)seq.getObjectAt(1);
+ }
+
+ public static AttributeTypeAndValue getInstance(Object o)
+ {
+ if (o instanceof AttributeTypeAndValue)
+ {
+ return (AttributeTypeAndValue)o;
+ }
+ else if (o != null)
+ {
+ return new AttributeTypeAndValue(ASN1Sequence.getInstance(o));
+ }
+
+ throw new IllegalArgumentException("null value in getInstance()");
+ }
+
+ public AttributeTypeAndValue(
+ ASN1ObjectIdentifier type,
+ ASN1Encodable value)
+ {
+ this.type = type;
+ this.value = value;
+ }
+
+ public ASN1ObjectIdentifier getType()
+ {
+ return type;
+ }
+
+ public ASN1Encodable getValue()
+ {
+ return value;
+ }
+
+ /**
+ * <pre>
+ * AttributeTypeAndValue ::= SEQUENCE {
+ * type OBJECT IDENTIFIER,
+ * value ANY DEFINED BY type }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(type);
+ v.add(value);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x500/DirectoryString.java b/core/src/main/java/org/bouncycastle/asn1/x500/DirectoryString.java
new file mode 100644
index 00000000..cf7563e2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x500/DirectoryString.java
@@ -0,0 +1,125 @@
+package org.bouncycastle.asn1.x500;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1String;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBMPString;
+import org.bouncycastle.asn1.DERPrintableString;
+import org.bouncycastle.asn1.DERT61String;
+import org.bouncycastle.asn1.DERUTF8String;
+import org.bouncycastle.asn1.DERUniversalString;
+
+public class DirectoryString
+ extends ASN1Object
+ implements ASN1Choice, ASN1String
+{
+ private ASN1String string;
+
+ public static DirectoryString getInstance(Object o)
+ {
+ if (o == null || o instanceof DirectoryString)
+ {
+ return (DirectoryString)o;
+ }
+
+ if (o instanceof DERT61String)
+ {
+ return new DirectoryString((DERT61String)o);
+ }
+
+ if (o instanceof DERPrintableString)
+ {
+ return new DirectoryString((DERPrintableString)o);
+ }
+
+ if (o instanceof DERUniversalString)
+ {
+ return new DirectoryString((DERUniversalString)o);
+ }
+
+ if (o instanceof DERUTF8String)
+ {
+ return new DirectoryString((DERUTF8String)o);
+ }
+
+ if (o instanceof DERBMPString)
+ {
+ return new DirectoryString((DERBMPString)o);
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + o.getClass().getName());
+ }
+
+ public static DirectoryString getInstance(ASN1TaggedObject o, boolean explicit)
+ {
+ if (!explicit)
+ {
+ throw new IllegalArgumentException("choice item must be explicitly tagged");
+ }
+
+ return getInstance(o.getObject());
+ }
+
+ private DirectoryString(
+ DERT61String string)
+ {
+ this.string = string;
+ }
+
+ private DirectoryString(
+ DERPrintableString string)
+ {
+ this.string = string;
+ }
+
+ private DirectoryString(
+ DERUniversalString string)
+ {
+ this.string = string;
+ }
+
+ private DirectoryString(
+ DERUTF8String string)
+ {
+ this.string = string;
+ }
+
+ private DirectoryString(
+ DERBMPString string)
+ {
+ this.string = string;
+ }
+
+ public DirectoryString(String string)
+ {
+ this.string = new DERUTF8String(string);
+ }
+
+ public String getString()
+ {
+ return string.getString();
+ }
+
+ public String toString()
+ {
+ return string.getString();
+ }
+
+ /**
+ * <pre>
+ * DirectoryString ::= CHOICE {
+ * teletexString TeletexString (SIZE (1..MAX)),
+ * printableString PrintableString (SIZE (1..MAX)),
+ * universalString UniversalString (SIZE (1..MAX)),
+ * utf8String UTF8String (SIZE (1..MAX)),
+ * bmpString BMPString (SIZE (1..MAX)) }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return ((ASN1Encodable)string).toASN1Primitive();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x500/RDN.java b/core/src/main/java/org/bouncycastle/asn1/x500/RDN.java
new file mode 100644
index 00000000..f51c2619
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x500/RDN.java
@@ -0,0 +1,119 @@
+package org.bouncycastle.asn1.x500;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERSet;
+
+public class RDN
+ extends ASN1Object
+{
+ private ASN1Set values;
+
+ private RDN(ASN1Set values)
+ {
+ this.values = values;
+ }
+
+ public static RDN getInstance(Object obj)
+ {
+ if (obj instanceof RDN)
+ {
+ return (RDN)obj;
+ }
+ else if (obj != null)
+ {
+ return new RDN(ASN1Set.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Create a single valued RDN.
+ *
+ * @param oid RDN type.
+ * @param value RDN value.
+ */
+ public RDN(ASN1ObjectIdentifier oid, ASN1Encodable value)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(oid);
+ v.add(value);
+
+ this.values = new DERSet(new DERSequence(v));
+ }
+
+ public RDN(AttributeTypeAndValue attrTAndV)
+ {
+ this.values = new DERSet(attrTAndV);
+ }
+
+ /**
+ * Create a multi-valued RDN.
+ *
+ * @param aAndVs attribute type/value pairs making up the RDN
+ */
+ public RDN(AttributeTypeAndValue[] aAndVs)
+ {
+ this.values = new DERSet(aAndVs);
+ }
+
+ public boolean isMultiValued()
+ {
+ return this.values.size() > 1;
+ }
+
+ /**
+ * Return the number of AttributeTypeAndValue objects in this RDN,
+ *
+ * @return size of RDN, greater than 1 if multi-valued.
+ */
+ public int size()
+ {
+ return this.values.size();
+ }
+
+ public AttributeTypeAndValue getFirst()
+ {
+ if (this.values.size() == 0)
+ {
+ return null;
+ }
+
+ return AttributeTypeAndValue.getInstance(this.values.getObjectAt(0));
+ }
+
+ public AttributeTypeAndValue[] getTypesAndValues()
+ {
+ AttributeTypeAndValue[] tmp = new AttributeTypeAndValue[values.size()];
+
+ for (int i = 0; i != tmp.length; i++)
+ {
+ tmp[i] = AttributeTypeAndValue.getInstance(values.getObjectAt(i));
+ }
+
+ return tmp;
+ }
+
+ /**
+ * <pre>
+ * RelativeDistinguishedName ::=
+ * SET OF AttributeTypeAndValue
+
+ * AttributeTypeAndValue ::= SEQUENCE {
+ * type AttributeType,
+ * value AttributeValue }
+ * </pre>
+ * @return this object as an ASN1Primitive type
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return values;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x500/X500Name.java b/core/src/main/java/org/bouncycastle/asn1/x500/X500Name.java
new file mode 100644
index 00000000..50e57c51
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x500/X500Name.java
@@ -0,0 +1,326 @@
+package org.bouncycastle.asn1.x500;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x500.style.BCStyle;
+
+/**
+ * <pre>
+ * Name ::= CHOICE {
+ * RDNSequence }
+ *
+ * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+ *
+ * RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue
+ *
+ * AttributeTypeAndValue ::= SEQUENCE {
+ * type OBJECT IDENTIFIER,
+ * value ANY }
+ * </pre>
+ */
+public class X500Name
+ extends ASN1Object
+ implements ASN1Choice
+{
+ private static X500NameStyle defaultStyle = BCStyle.INSTANCE;
+
+ private boolean isHashCodeCalculated;
+ private int hashCodeValue;
+
+ private X500NameStyle style;
+ private RDN[] rdns;
+
+ public X500Name(X500NameStyle style, X500Name name)
+ {
+ this.rdns = name.rdns;
+ this.style = style;
+ }
+
+ /**
+ * Return a X500Name based on the passed in tagged object.
+ *
+ * @param obj tag object holding name.
+ * @param explicit true if explicitly tagged false otherwise.
+ * @return the X500Name
+ */
+ public static X500Name getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ // must be true as choice item
+ return getInstance(ASN1Sequence.getInstance(obj, true));
+ }
+
+ public static X500Name getInstance(
+ Object obj)
+ {
+ if (obj instanceof X500Name)
+ {
+ return (X500Name)obj;
+ }
+ else if (obj != null)
+ {
+ return new X500Name(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static X500Name getInstance(
+ X500NameStyle style,
+ Object obj)
+ {
+ if (obj instanceof X500Name)
+ {
+ return getInstance(style, ((X500Name)obj).toASN1Primitive());
+ }
+ else if (obj != null)
+ {
+ return new X500Name(style, ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Constructor from ASN1Sequence
+ *
+ * the principal will be a list of constructed sets, each containing an (OID, String) pair.
+ */
+ private X500Name(
+ ASN1Sequence seq)
+ {
+ this(defaultStyle, seq);
+ }
+
+ private X500Name(
+ X500NameStyle style,
+ ASN1Sequence seq)
+ {
+ this.style = style;
+ this.rdns = new RDN[seq.size()];
+
+ int index = 0;
+
+ for (Enumeration e = seq.getObjects(); e.hasMoreElements();)
+ {
+ rdns[index++] = RDN.getInstance(e.nextElement());
+ }
+ }
+
+ public X500Name(
+ RDN[] rDNs)
+ {
+ this(defaultStyle, rDNs);
+ }
+
+ public X500Name(
+ X500NameStyle style,
+ RDN[] rDNs)
+ {
+ this.rdns = rDNs;
+ this.style = style;
+ }
+
+ public X500Name(
+ String dirName)
+ {
+ this(defaultStyle, dirName);
+ }
+
+ public X500Name(
+ X500NameStyle style,
+ String dirName)
+ {
+ this(style.fromString(dirName));
+
+ this.style = style;
+ }
+
+ /**
+ * return an array of RDNs in structure order.
+ *
+ * @return an array of RDN objects.
+ */
+ public RDN[] getRDNs()
+ {
+ RDN[] tmp = new RDN[this.rdns.length];
+
+ System.arraycopy(rdns, 0, tmp, 0, tmp.length);
+
+ return tmp;
+ }
+
+ /**
+ * return an array of OIDs contained in the attribute type of each RDN in structure order.
+ *
+ * @return an array, possibly zero length, of ASN1ObjectIdentifiers objects.
+ */
+ public ASN1ObjectIdentifier[] getAttributeTypes()
+ {
+ int count = 0;
+
+ for (int i = 0; i != rdns.length; i++)
+ {
+ RDN rdn = rdns[i];
+
+ count += rdn.size();
+ }
+
+ ASN1ObjectIdentifier[] res = new ASN1ObjectIdentifier[count];
+
+ count = 0;
+
+ for (int i = 0; i != rdns.length; i++)
+ {
+ RDN rdn = rdns[i];
+
+ if (rdn.isMultiValued())
+ {
+ AttributeTypeAndValue[] attr = rdn.getTypesAndValues();
+ for (int j = 0; j != attr.length; j++)
+ {
+ res[count++] = attr[j].getType();
+ }
+ }
+ else if (rdn.size() != 0)
+ {
+ res[count++] = rdn.getFirst().getType();
+ }
+ }
+
+ return res;
+ }
+
+ /**
+ * return an array of RDNs containing the attribute type given by OID in structure order.
+ *
+ * @param attributeType the type OID we are looking for.
+ * @return an array, possibly zero length, of RDN objects.
+ */
+ public RDN[] getRDNs(ASN1ObjectIdentifier attributeType)
+ {
+ RDN[] res = new RDN[rdns.length];
+ int count = 0;
+
+ for (int i = 0; i != rdns.length; i++)
+ {
+ RDN rdn = rdns[i];
+
+ if (rdn.isMultiValued())
+ {
+ AttributeTypeAndValue[] attr = rdn.getTypesAndValues();
+ for (int j = 0; j != attr.length; j++)
+ {
+ if (attr[j].getType().equals(attributeType))
+ {
+ res[count++] = rdn;
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (rdn.getFirst().getType().equals(attributeType))
+ {
+ res[count++] = rdn;
+ }
+ }
+ }
+
+ RDN[] tmp = new RDN[count];
+
+ System.arraycopy(res, 0, tmp, 0, tmp.length);
+
+ return tmp;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return new DERSequence(rdns);
+ }
+
+ public int hashCode()
+ {
+ if (isHashCodeCalculated)
+ {
+ return hashCodeValue;
+ }
+
+ isHashCodeCalculated = true;
+
+ hashCodeValue = style.calculateHashCode(this);
+
+ return hashCodeValue;
+ }
+
+ /**
+ * test for equality - note: case is ignored.
+ */
+ public boolean equals(Object obj)
+ {
+ if (obj == this)
+ {
+ return true;
+ }
+
+ if (!(obj instanceof X500Name || obj instanceof ASN1Sequence))
+ {
+ return false;
+ }
+
+ ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive();
+
+ if (this.toASN1Primitive().equals(derO))
+ {
+ return true;
+ }
+
+ try
+ {
+ return style.areEqual(this, new X500Name(ASN1Sequence.getInstance(((ASN1Encodable)obj).toASN1Primitive())));
+ }
+ catch (Exception e)
+ {
+ return false;
+ }
+ }
+
+ public String toString()
+ {
+ return style.toString(this);
+ }
+
+ /**
+ * Set the default style for X500Name construction.
+ *
+ * @param style an X500NameStyle
+ */
+ public static void setDefaultStyle(X500NameStyle style)
+ {
+ if (style == null)
+ {
+ throw new NullPointerException("cannot set style to null");
+ }
+
+ defaultStyle = style;
+ }
+
+ /**
+ * Return the current default style.
+ *
+ * @return default style for X500Name construction.
+ */
+ public static X500NameStyle getDefaultStyle()
+ {
+ return defaultStyle;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x500/X500NameBuilder.java b/core/src/main/java/org/bouncycastle/asn1/x500/X500NameBuilder.java
new file mode 100644
index 00000000..7c9506a9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x500/X500NameBuilder.java
@@ -0,0 +1,87 @@
+package org.bouncycastle.asn1.x500;
+
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.x500.style.BCStyle;
+
+public class X500NameBuilder
+{
+ private X500NameStyle template;
+ private Vector rdns = new Vector();
+
+ public X500NameBuilder()
+ {
+ this(BCStyle.INSTANCE);
+ }
+
+ public X500NameBuilder(X500NameStyle template)
+ {
+ this.template = template;
+ }
+
+ public X500NameBuilder addRDN(ASN1ObjectIdentifier oid, String value)
+ {
+ this.addRDN(oid, template.stringToValue(oid, value));
+
+ return this;
+ }
+
+ public X500NameBuilder addRDN(ASN1ObjectIdentifier oid, ASN1Encodable value)
+ {
+ rdns.addElement(new RDN(oid, value));
+
+ return this;
+ }
+
+ public X500NameBuilder addRDN(AttributeTypeAndValue attrTAndV)
+ {
+ rdns.addElement(new RDN(attrTAndV));
+
+ return this;
+ }
+
+ public X500NameBuilder addMultiValuedRDN(ASN1ObjectIdentifier[] oids, String[] values)
+ {
+ ASN1Encodable[] vals = new ASN1Encodable[values.length];
+
+ for (int i = 0; i != vals.length; i++)
+ {
+ vals[i] = template.stringToValue(oids[i], values[i]);
+ }
+
+ return addMultiValuedRDN(oids, vals);
+ }
+
+ public X500NameBuilder addMultiValuedRDN(ASN1ObjectIdentifier[] oids, ASN1Encodable[] values)
+ {
+ AttributeTypeAndValue[] avs = new AttributeTypeAndValue[oids.length];
+
+ for (int i = 0; i != oids.length; i++)
+ {
+ avs[i] = new AttributeTypeAndValue(oids[i], values[i]);
+ }
+
+ return addMultiValuedRDN(avs);
+ }
+
+ public X500NameBuilder addMultiValuedRDN(AttributeTypeAndValue[] attrTAndVs)
+ {
+ rdns.addElement(new RDN(attrTAndVs));
+
+ return this;
+ }
+
+ public X500Name build()
+ {
+ RDN[] vals = new RDN[rdns.size()];
+
+ for (int i = 0; i != vals.length; i++)
+ {
+ vals[i] = (RDN)rdns.elementAt(i);
+ }
+
+ return new X500Name(template, vals);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x500/X500NameStyle.java b/core/src/main/java/org/bouncycastle/asn1/x500/X500NameStyle.java
new file mode 100644
index 00000000..704ea72e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x500/X500NameStyle.java
@@ -0,0 +1,79 @@
+package org.bouncycastle.asn1.x500;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+/**
+ * It turns out that the number of standard ways the fields in a DN should be
+ * encoded into their ASN.1 counterparts is rapidly approaching the
+ * number of machines on the internet. By default the X500Name class
+ * will produce UTF8Strings in line with the current recommendations (RFC 3280).
+ * <p>
+ */
+public interface X500NameStyle
+{
+ /**
+ * Convert the passed in String value into the appropriate ASN.1
+ * encoded object.
+ *
+ * @param oid the OID associated with the value in the DN.
+ * @param value the value of the particular DN component.
+ * @return the ASN.1 equivalent for the value.
+ */
+ ASN1Encodable stringToValue(ASN1ObjectIdentifier oid, String value);
+
+ /**
+ * Return the OID associated with the passed in name.
+ *
+ * @param attrName the string to match.
+ * @return an OID
+ */
+ ASN1ObjectIdentifier attrNameToOID(String attrName);
+
+ /**
+ * Return an array of RDN generated from the passed in String.
+ * @param dirName the String representation.
+ * @return an array of corresponding RDNs.
+ */
+ RDN[] fromString(String dirName);
+
+ /**
+ * Return true if the two names are equal.
+ *
+ * @param name1 first name for comparison.
+ * @param name2 second name for comparison.
+ * @return true if name1 = name 2, false otherwise.
+ */
+ boolean areEqual(X500Name name1, X500Name name2);
+
+ /**
+ * Calculate a hashCode for the passed in name.
+ *
+ * @param name the name the hashCode is required for.
+ * @return the calculated hashCode.
+ */
+ int calculateHashCode(X500Name name);
+
+ /**
+ * Convert the passed in X500Name to a String.
+ * @param name the name to convert.
+ * @return a String representation.
+ */
+ String toString(X500Name name);
+
+ /**
+ * Return the display name for toString() associated with the OID.
+ *
+ * @param oid the OID of interest.
+ * @return the name displayed in toString(), null if no mapping provided.
+ */
+ String oidToDisplayName(ASN1ObjectIdentifier oid);
+
+ /**
+ * Return the acceptable names in a String DN that map to OID.
+ *
+ * @param oid the OID of interest.
+ * @return an array of String aliases for the OID, zero length if there are none.
+ */
+ String[] oidToAttrNames(ASN1ObjectIdentifier oid);
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x500/style/BCStrictStyle.java b/core/src/main/java/org/bouncycastle/asn1/x500/style/BCStrictStyle.java
new file mode 100644
index 00000000..eb627c0e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x500/style/BCStrictStyle.java
@@ -0,0 +1,36 @@
+package org.bouncycastle.asn1.x500.style;
+
+import org.bouncycastle.asn1.x500.RDN;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x500.X500NameStyle;
+
+/**
+ * Variation of BCStyle that insists on strict ordering for equality
+ * and hashCode comparisons
+ */
+public class BCStrictStyle
+ extends BCStyle
+{
+ public static final X500NameStyle INSTANCE = new BCStrictStyle();
+
+ public boolean areEqual(X500Name name1, X500Name name2)
+ {
+ RDN[] rdns1 = name1.getRDNs();
+ RDN[] rdns2 = name2.getRDNs();
+
+ if (rdns1.length != rdns2.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != rdns1.length; i++)
+ {
+ if (!rdnAreEqual(rdns1[i], rdns2[i]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java b/core/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java
new file mode 100644
index 00000000..714a32c0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java
@@ -0,0 +1,459 @@
+package org.bouncycastle.asn1.x500.style;
+
+import java.io.IOException;
+import java.util.Hashtable;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.DERPrintableString;
+import org.bouncycastle.asn1.DERUTF8String;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
+import org.bouncycastle.asn1.x500.RDN;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x500.X500NameStyle;
+import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
+
+public class BCStyle
+ implements X500NameStyle
+{
+ public static final X500NameStyle INSTANCE = new BCStyle();
+
+ /**
+ * country code - StringType(SIZE(2))
+ */
+ public static final ASN1ObjectIdentifier C = new ASN1ObjectIdentifier("2.5.4.6");
+
+ /**
+ * organization - StringType(SIZE(1..64))
+ */
+ public static final ASN1ObjectIdentifier O = new ASN1ObjectIdentifier("2.5.4.10");
+
+ /**
+ * organizational unit name - StringType(SIZE(1..64))
+ */
+ public static final ASN1ObjectIdentifier OU = new ASN1ObjectIdentifier("2.5.4.11");
+
+ /**
+ * Title
+ */
+ public static final ASN1ObjectIdentifier T = new ASN1ObjectIdentifier("2.5.4.12");
+
+ /**
+ * common name - StringType(SIZE(1..64))
+ */
+ public static final ASN1ObjectIdentifier CN = new ASN1ObjectIdentifier("2.5.4.3");
+
+ /**
+ * device serial number name - StringType(SIZE(1..64))
+ */
+ public static final ASN1ObjectIdentifier SN = new ASN1ObjectIdentifier("2.5.4.5");
+
+ /**
+ * street - StringType(SIZE(1..64))
+ */
+ public static final ASN1ObjectIdentifier STREET = new ASN1ObjectIdentifier("2.5.4.9");
+
+ /**
+ * device serial number name - StringType(SIZE(1..64))
+ */
+ public static final ASN1ObjectIdentifier SERIALNUMBER = SN;
+
+ /**
+ * locality name - StringType(SIZE(1..64))
+ */
+ public static final ASN1ObjectIdentifier L = new ASN1ObjectIdentifier("2.5.4.7");
+
+ /**
+ * state, or province name - StringType(SIZE(1..64))
+ */
+ public static final ASN1ObjectIdentifier ST = new ASN1ObjectIdentifier("2.5.4.8");
+
+ /**
+ * Naming attributes of type X520name
+ */
+ public static final ASN1ObjectIdentifier SURNAME = new ASN1ObjectIdentifier("2.5.4.4");
+ public static final ASN1ObjectIdentifier GIVENNAME = new ASN1ObjectIdentifier("2.5.4.42");
+ public static final ASN1ObjectIdentifier INITIALS = new ASN1ObjectIdentifier("2.5.4.43");
+ public static final ASN1ObjectIdentifier GENERATION = new ASN1ObjectIdentifier("2.5.4.44");
+ public static final ASN1ObjectIdentifier UNIQUE_IDENTIFIER = new ASN1ObjectIdentifier("2.5.4.45");
+
+ /**
+ * businessCategory - DirectoryString(SIZE(1..128)
+ */
+ public static final ASN1ObjectIdentifier BUSINESS_CATEGORY = new ASN1ObjectIdentifier(
+ "2.5.4.15");
+
+ /**
+ * postalCode - DirectoryString(SIZE(1..40)
+ */
+ public static final ASN1ObjectIdentifier POSTAL_CODE = new ASN1ObjectIdentifier(
+ "2.5.4.17");
+
+ /**
+ * dnQualifier - DirectoryString(SIZE(1..64)
+ */
+ public static final ASN1ObjectIdentifier DN_QUALIFIER = new ASN1ObjectIdentifier(
+ "2.5.4.46");
+
+ /**
+ * RFC 3039 Pseudonym - DirectoryString(SIZE(1..64)
+ */
+ public static final ASN1ObjectIdentifier PSEUDONYM = new ASN1ObjectIdentifier(
+ "2.5.4.65");
+
+
+ /**
+ * RFC 3039 DateOfBirth - GeneralizedTime - YYYYMMDD000000Z
+ */
+ public static final ASN1ObjectIdentifier DATE_OF_BIRTH = new ASN1ObjectIdentifier(
+ "1.3.6.1.5.5.7.9.1");
+
+ /**
+ * RFC 3039 PlaceOfBirth - DirectoryString(SIZE(1..128)
+ */
+ public static final ASN1ObjectIdentifier PLACE_OF_BIRTH = new ASN1ObjectIdentifier(
+ "1.3.6.1.5.5.7.9.2");
+
+ /**
+ * RFC 3039 Gender - PrintableString (SIZE(1)) -- "M", "F", "m" or "f"
+ */
+ public static final ASN1ObjectIdentifier GENDER = new ASN1ObjectIdentifier(
+ "1.3.6.1.5.5.7.9.3");
+
+ /**
+ * RFC 3039 CountryOfCitizenship - PrintableString (SIZE (2)) -- ISO 3166
+ * codes only
+ */
+ public static final ASN1ObjectIdentifier COUNTRY_OF_CITIZENSHIP = new ASN1ObjectIdentifier(
+ "1.3.6.1.5.5.7.9.4");
+
+ /**
+ * RFC 3039 CountryOfResidence - PrintableString (SIZE (2)) -- ISO 3166
+ * codes only
+ */
+ public static final ASN1ObjectIdentifier COUNTRY_OF_RESIDENCE = new ASN1ObjectIdentifier(
+ "1.3.6.1.5.5.7.9.5");
+
+
+ /**
+ * ISIS-MTT NameAtBirth - DirectoryString(SIZE(1..64)
+ */
+ public static final ASN1ObjectIdentifier NAME_AT_BIRTH = new ASN1ObjectIdentifier("1.3.36.8.3.14");
+
+ /**
+ * RFC 3039 PostalAddress - SEQUENCE SIZE (1..6) OF
+ * DirectoryString(SIZE(1..30))
+ */
+ public static final ASN1ObjectIdentifier POSTAL_ADDRESS = new ASN1ObjectIdentifier("2.5.4.16");
+
+ /**
+ * RFC 2256 dmdName
+ */
+ public static final ASN1ObjectIdentifier DMD_NAME = new ASN1ObjectIdentifier("2.5.4.54");
+
+ /**
+ * id-at-telephoneNumber
+ */
+ public static final ASN1ObjectIdentifier TELEPHONE_NUMBER = X509ObjectIdentifiers.id_at_telephoneNumber;
+
+ /**
+ * id-at-name
+ */
+ public static final ASN1ObjectIdentifier NAME = X509ObjectIdentifiers.id_at_name;
+
+ /**
+ * Email address (RSA PKCS#9 extension) - IA5String.
+ * <p>Note: if you're trying to be ultra orthodox, don't use this! It shouldn't be in here.
+ */
+ public static final ASN1ObjectIdentifier EmailAddress = PKCSObjectIdentifiers.pkcs_9_at_emailAddress;
+
+ /**
+ * more from PKCS#9
+ */
+ public static final ASN1ObjectIdentifier UnstructuredName = PKCSObjectIdentifiers.pkcs_9_at_unstructuredName;
+ public static final ASN1ObjectIdentifier UnstructuredAddress = PKCSObjectIdentifiers.pkcs_9_at_unstructuredAddress;
+
+ /**
+ * email address in Verisign certificates
+ */
+ public static final ASN1ObjectIdentifier E = EmailAddress;
+
+ /*
+ * others...
+ */
+ public static final ASN1ObjectIdentifier DC = new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.25");
+
+ /**
+ * LDAP User id.
+ */
+ public static final ASN1ObjectIdentifier UID = new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.1");
+
+ /**
+ * default look up table translating OID values into their common symbols following
+ * the convention in RFC 2253 with a few extras
+ */
+ private static final Hashtable DefaultSymbols = new Hashtable();
+
+ /**
+ * look up table translating common symbols into their OIDS.
+ */
+ private static final Hashtable DefaultLookUp = new Hashtable();
+
+ static
+ {
+ DefaultSymbols.put(C, "C");
+ DefaultSymbols.put(O, "O");
+ DefaultSymbols.put(T, "T");
+ DefaultSymbols.put(OU, "OU");
+ DefaultSymbols.put(CN, "CN");
+ DefaultSymbols.put(L, "L");
+ DefaultSymbols.put(ST, "ST");
+ DefaultSymbols.put(SN, "SERIALNUMBER");
+ DefaultSymbols.put(EmailAddress, "E");
+ DefaultSymbols.put(DC, "DC");
+ DefaultSymbols.put(UID, "UID");
+ DefaultSymbols.put(STREET, "STREET");
+ DefaultSymbols.put(SURNAME, "SURNAME");
+ DefaultSymbols.put(GIVENNAME, "GIVENNAME");
+ DefaultSymbols.put(INITIALS, "INITIALS");
+ DefaultSymbols.put(GENERATION, "GENERATION");
+ DefaultSymbols.put(UnstructuredAddress, "unstructuredAddress");
+ DefaultSymbols.put(UnstructuredName, "unstructuredName");
+ DefaultSymbols.put(UNIQUE_IDENTIFIER, "UniqueIdentifier");
+ DefaultSymbols.put(DN_QUALIFIER, "DN");
+ DefaultSymbols.put(PSEUDONYM, "Pseudonym");
+ DefaultSymbols.put(POSTAL_ADDRESS, "PostalAddress");
+ DefaultSymbols.put(NAME_AT_BIRTH, "NameAtBirth");
+ DefaultSymbols.put(COUNTRY_OF_CITIZENSHIP, "CountryOfCitizenship");
+ DefaultSymbols.put(COUNTRY_OF_RESIDENCE, "CountryOfResidence");
+ DefaultSymbols.put(GENDER, "Gender");
+ DefaultSymbols.put(PLACE_OF_BIRTH, "PlaceOfBirth");
+ DefaultSymbols.put(DATE_OF_BIRTH, "DateOfBirth");
+ DefaultSymbols.put(POSTAL_CODE, "PostalCode");
+ DefaultSymbols.put(BUSINESS_CATEGORY, "BusinessCategory");
+ DefaultSymbols.put(TELEPHONE_NUMBER, "TelephoneNumber");
+ DefaultSymbols.put(NAME, "Name");
+
+ DefaultLookUp.put("c", C);
+ DefaultLookUp.put("o", O);
+ DefaultLookUp.put("t", T);
+ DefaultLookUp.put("ou", OU);
+ DefaultLookUp.put("cn", CN);
+ DefaultLookUp.put("l", L);
+ DefaultLookUp.put("st", ST);
+ DefaultLookUp.put("sn", SN);
+ DefaultLookUp.put("serialnumber", SN);
+ DefaultLookUp.put("street", STREET);
+ DefaultLookUp.put("emailaddress", E);
+ DefaultLookUp.put("dc", DC);
+ DefaultLookUp.put("e", E);
+ DefaultLookUp.put("uid", UID);
+ DefaultLookUp.put("surname", SURNAME);
+ DefaultLookUp.put("givenname", GIVENNAME);
+ DefaultLookUp.put("initials", INITIALS);
+ DefaultLookUp.put("generation", GENERATION);
+ DefaultLookUp.put("unstructuredaddress", UnstructuredAddress);
+ DefaultLookUp.put("unstructuredname", UnstructuredName);
+ DefaultLookUp.put("uniqueidentifier", UNIQUE_IDENTIFIER);
+ DefaultLookUp.put("dn", DN_QUALIFIER);
+ DefaultLookUp.put("pseudonym", PSEUDONYM);
+ DefaultLookUp.put("postaladdress", POSTAL_ADDRESS);
+ DefaultLookUp.put("nameofbirth", NAME_AT_BIRTH);
+ DefaultLookUp.put("countryofcitizenship", COUNTRY_OF_CITIZENSHIP);
+ DefaultLookUp.put("countryofresidence", COUNTRY_OF_RESIDENCE);
+ DefaultLookUp.put("gender", GENDER);
+ DefaultLookUp.put("placeofbirth", PLACE_OF_BIRTH);
+ DefaultLookUp.put("dateofbirth", DATE_OF_BIRTH);
+ DefaultLookUp.put("postalcode", POSTAL_CODE);
+ DefaultLookUp.put("businesscategory", BUSINESS_CATEGORY);
+ DefaultLookUp.put("telephonenumber", TELEPHONE_NUMBER);
+ DefaultLookUp.put("name", NAME);
+ }
+
+ protected BCStyle()
+ {
+
+ }
+
+ public ASN1Encodable stringToValue(ASN1ObjectIdentifier oid, String value)
+ {
+ if (value.length() != 0 && value.charAt(0) == '#')
+ {
+ try
+ {
+ return IETFUtils.valueFromHexString(value, 1);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("can't recode value for oid " + oid.getId());
+ }
+ }
+ else
+ {
+ if (value.length() != 0 && value.charAt(0) == '\\')
+ {
+ value = value.substring(1);
+ }
+ if (oid.equals(EmailAddress) || oid.equals(DC))
+ {
+ return new DERIA5String(value);
+ }
+ else if (oid.equals(DATE_OF_BIRTH)) // accept time string as well as # (for compatibility)
+ {
+ return new ASN1GeneralizedTime(value);
+ }
+ else if (oid.equals(C) || oid.equals(SN) || oid.equals(DN_QUALIFIER)
+ || oid.equals(TELEPHONE_NUMBER))
+ {
+ return new DERPrintableString(value);
+ }
+ }
+
+ return new DERUTF8String(value);
+ }
+
+ public String oidToDisplayName(ASN1ObjectIdentifier oid)
+ {
+ return (String)DefaultSymbols.get(oid);
+ }
+
+ public String[] oidToAttrNames(ASN1ObjectIdentifier oid)
+ {
+ return IETFUtils.findAttrNamesForOID(oid, DefaultLookUp);
+ }
+
+ public ASN1ObjectIdentifier attrNameToOID(String attrName)
+ {
+ return IETFUtils.decodeAttrName(attrName, DefaultLookUp);
+ }
+
+ public boolean areEqual(X500Name name1, X500Name name2)
+ {
+ RDN[] rdns1 = name1.getRDNs();
+ RDN[] rdns2 = name2.getRDNs();
+
+ if (rdns1.length != rdns2.length)
+ {
+ return false;
+ }
+
+ boolean reverse = false;
+
+ if (rdns1[0].getFirst() != null && rdns2[0].getFirst() != null)
+ {
+ reverse = !rdns1[0].getFirst().getType().equals(rdns2[0].getFirst().getType()); // guess forward
+ }
+
+ for (int i = 0; i != rdns1.length; i++)
+ {
+ if (!foundMatch(reverse, rdns1[i], rdns2))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean foundMatch(boolean reverse, RDN rdn, RDN[] possRDNs)
+ {
+ if (reverse)
+ {
+ for (int i = possRDNs.length - 1; i >= 0; i--)
+ {
+ if (possRDNs[i] != null && rdnAreEqual(rdn, possRDNs[i]))
+ {
+ possRDNs[i] = null;
+ return true;
+ }
+ }
+ }
+ else
+ {
+ for (int i = 0; i != possRDNs.length; i++)
+ {
+ if (possRDNs[i] != null && rdnAreEqual(rdn, possRDNs[i]))
+ {
+ possRDNs[i] = null;
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ protected boolean rdnAreEqual(RDN rdn1, RDN rdn2)
+ {
+ return IETFUtils.rDNAreEqual(rdn1, rdn2);
+ }
+
+ public RDN[] fromString(String dirName)
+ {
+ return IETFUtils.rDNsFromString(dirName, this);
+ }
+
+ public int calculateHashCode(X500Name name)
+ {
+ int hashCodeValue = 0;
+ RDN[] rdns = name.getRDNs();
+
+ // this needs to be order independent, like equals
+ for (int i = 0; i != rdns.length; i++)
+ {
+ if (rdns[i].isMultiValued())
+ {
+ AttributeTypeAndValue[] atv = rdns[i].getTypesAndValues();
+
+ for (int j = 0; j != atv.length; j++)
+ {
+ hashCodeValue ^= atv[j].getType().hashCode();
+ hashCodeValue ^= calcHashCode(atv[j].getValue());
+ }
+ }
+ else
+ {
+ hashCodeValue ^= rdns[i].getFirst().getType().hashCode();
+ hashCodeValue ^= calcHashCode(rdns[i].getFirst().getValue());
+ }
+ }
+
+ return hashCodeValue;
+ }
+
+ private int calcHashCode(ASN1Encodable enc)
+ {
+ String value = IETFUtils.valueToString(enc);
+
+ value = IETFUtils.canonicalize(value);
+
+ return value.hashCode();
+ }
+
+ public String toString(X500Name name)
+ {
+ StringBuffer buf = new StringBuffer();
+ boolean first = true;
+
+ RDN[] rdns = name.getRDNs();
+
+ for (int i = 0; i < rdns.length; i++)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ buf.append(',');
+ }
+
+ IETFUtils.appendRDN(buf, rdns[i], DefaultSymbols);
+ }
+
+ return buf.toString();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x500/style/IETFUtils.java b/core/src/main/java/org/bouncycastle/asn1/x500/style/IETFUtils.java
new file mode 100644
index 00000000..c73107eb
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x500/style/IETFUtils.java
@@ -0,0 +1,572 @@
+package org.bouncycastle.asn1.x500.style;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1String;
+import org.bouncycastle.asn1.DERUniversalString;
+import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
+import org.bouncycastle.asn1.x500.RDN;
+import org.bouncycastle.asn1.x500.X500NameBuilder;
+import org.bouncycastle.asn1.x500.X500NameStyle;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Hex;
+
+public class IETFUtils
+{
+ private static String unescape(String elt)
+ {
+ if (elt.length() == 0 || (elt.indexOf('\\') < 0 && elt.indexOf('"') < 0))
+ {
+ return elt.trim();
+ }
+
+ char[] elts = elt.toCharArray();
+ boolean escaped = false;
+ boolean quoted = false;
+ StringBuffer buf = new StringBuffer(elt.length());
+ int start = 0;
+
+ // if it's an escaped hash string and not an actual encoding in string form
+ // we need to leave it escaped.
+ if (elts[0] == '\\')
+ {
+ if (elts[1] == '#')
+ {
+ start = 2;
+ buf.append("\\#");
+ }
+ }
+
+ boolean nonWhiteSpaceEncountered = false;
+ int lastEscaped = 0;
+ char hex1 = 0;
+
+ for (int i = start; i != elts.length; i++)
+ {
+ char c = elts[i];
+
+ if (c != ' ')
+ {
+ nonWhiteSpaceEncountered = true;
+ }
+
+ if (c == '"')
+ {
+ if (!escaped)
+ {
+ quoted = !quoted;
+ }
+ else
+ {
+ buf.append(c);
+ }
+ escaped = false;
+ }
+ else if (c == '\\' && !(escaped || quoted))
+ {
+ escaped = true;
+ lastEscaped = buf.length();
+ }
+ else
+ {
+ if (c == ' ' && !escaped && !nonWhiteSpaceEncountered)
+ {
+ continue;
+ }
+ if (escaped && isHexDigit(c))
+ {
+ if (hex1 != 0)
+ {
+ buf.append((char)(convertHex(hex1) * 16 + convertHex(c)));
+ escaped = false;
+ hex1 = 0;
+ continue;
+ }
+ hex1 = c;
+ continue;
+ }
+ buf.append(c);
+ escaped = false;
+ }
+ }
+
+ if (buf.length() > 0)
+ {
+ while (buf.charAt(buf.length() - 1) == ' ' && lastEscaped != (buf.length() - 1))
+ {
+ buf.setLength(buf.length() - 1);
+ }
+ }
+
+ return buf.toString();
+ }
+
+ private static boolean isHexDigit(char c)
+ {
+ return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
+ }
+
+ private static int convertHex(char c)
+ {
+ if ('0' <= c && c <= '9')
+ {
+ return c - '0';
+ }
+ if ('a' <= c && c <= 'f')
+ {
+ return c - 'a' + 10;
+ }
+ return c - 'A' + 10;
+ }
+
+ public static RDN[] rDNsFromString(String name, X500NameStyle x500Style)
+ {
+ X500NameTokenizer nTok = new X500NameTokenizer(name);
+ X500NameBuilder builder = new X500NameBuilder(x500Style);
+
+ while (nTok.hasMoreTokens())
+ {
+ String token = nTok.nextToken();
+
+ if (token.indexOf('+') > 0)
+ {
+ X500NameTokenizer pTok = new X500NameTokenizer(token, '+');
+ X500NameTokenizer vTok = new X500NameTokenizer(pTok.nextToken(), '=');
+
+ String attr = vTok.nextToken();
+
+ if (!vTok.hasMoreTokens())
+ {
+ throw new IllegalArgumentException("badly formatted directory string");
+ }
+
+ String value = vTok.nextToken();
+ ASN1ObjectIdentifier oid = x500Style.attrNameToOID(attr.trim());
+
+ if (pTok.hasMoreTokens())
+ {
+ Vector oids = new Vector();
+ Vector values = new Vector();
+
+ oids.addElement(oid);
+ values.addElement(unescape(value));
+
+ while (pTok.hasMoreTokens())
+ {
+ vTok = new X500NameTokenizer(pTok.nextToken(), '=');
+
+ attr = vTok.nextToken();
+
+ if (!vTok.hasMoreTokens())
+ {
+ throw new IllegalArgumentException("badly formatted directory string");
+ }
+
+ value = vTok.nextToken();
+ oid = x500Style.attrNameToOID(attr.trim());
+
+
+ oids.addElement(oid);
+ values.addElement(unescape(value));
+ }
+
+ builder.addMultiValuedRDN(toOIDArray(oids), toValueArray(values));
+ }
+ else
+ {
+ builder.addRDN(oid, unescape(value));
+ }
+ }
+ else
+ {
+ X500NameTokenizer vTok = new X500NameTokenizer(token, '=');
+
+ String attr = vTok.nextToken();
+
+ if (!vTok.hasMoreTokens())
+ {
+ throw new IllegalArgumentException("badly formatted directory string");
+ }
+
+ String value = vTok.nextToken();
+ ASN1ObjectIdentifier oid = x500Style.attrNameToOID(attr.trim());
+
+ builder.addRDN(oid, unescape(value));
+ }
+ }
+
+ return builder.build().getRDNs();
+ }
+
+ private static String[] toValueArray(Vector values)
+ {
+ String[] tmp = new String[values.size()];
+
+ for (int i = 0; i != tmp.length; i++)
+ {
+ tmp[i] = (String)values.elementAt(i);
+ }
+
+ return tmp;
+ }
+
+ private static ASN1ObjectIdentifier[] toOIDArray(Vector oids)
+ {
+ ASN1ObjectIdentifier[] tmp = new ASN1ObjectIdentifier[oids.size()];
+
+ for (int i = 0; i != tmp.length; i++)
+ {
+ tmp[i] = (ASN1ObjectIdentifier)oids.elementAt(i);
+ }
+
+ return tmp;
+ }
+
+ public static String[] findAttrNamesForOID(
+ ASN1ObjectIdentifier oid,
+ Hashtable lookup)
+ {
+ int count = 0;
+ for (Enumeration en = lookup.elements(); en.hasMoreElements();)
+ {
+ if (oid.equals(en.nextElement()))
+ {
+ count++;
+ }
+ }
+
+ String[] aliases = new String[count];
+ count = 0;
+
+ for (Enumeration en = lookup.keys(); en.hasMoreElements();)
+ {
+ String key = (String)en.nextElement();
+ if (oid.equals(lookup.get(key)))
+ {
+ aliases[count++] = key;
+ }
+ }
+
+ return aliases;
+ }
+
+ public static ASN1ObjectIdentifier decodeAttrName(
+ String name,
+ Hashtable lookUp)
+ {
+ if (Strings.toUpperCase(name).startsWith("OID."))
+ {
+ return new ASN1ObjectIdentifier(name.substring(4));
+ }
+ else if (name.charAt(0) >= '0' && name.charAt(0) <= '9')
+ {
+ return new ASN1ObjectIdentifier(name);
+ }
+
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)lookUp.get(Strings.toLowerCase(name));
+ if (oid == null)
+ {
+ throw new IllegalArgumentException("Unknown object id - " + name + " - passed to distinguished name");
+ }
+
+ return oid;
+ }
+
+ public static ASN1Encodable valueFromHexString(
+ String str,
+ int off)
+ throws IOException
+ {
+ byte[] data = new byte[(str.length() - off) / 2];
+ for (int index = 0; index != data.length; index++)
+ {
+ char left = str.charAt((index * 2) + off);
+ char right = str.charAt((index * 2) + off + 1);
+
+ data[index] = (byte)((convertHex(left) << 4) | convertHex(right));
+ }
+
+ return ASN1Primitive.fromByteArray(data);
+ }
+
+ public static void appendRDN(
+ StringBuffer buf,
+ RDN rdn,
+ Hashtable oidSymbols)
+ {
+ if (rdn.isMultiValued())
+ {
+ AttributeTypeAndValue[] atv = rdn.getTypesAndValues();
+ boolean firstAtv = true;
+
+ for (int j = 0; j != atv.length; j++)
+ {
+ if (firstAtv)
+ {
+ firstAtv = false;
+ }
+ else
+ {
+ buf.append('+');
+ }
+
+ IETFUtils.appendTypeAndValue(buf, atv[j], oidSymbols);
+ }
+ }
+ else
+ {
+ IETFUtils.appendTypeAndValue(buf, rdn.getFirst(), oidSymbols);
+ }
+ }
+
+ public static void appendTypeAndValue(
+ StringBuffer buf,
+ AttributeTypeAndValue typeAndValue,
+ Hashtable oidSymbols)
+ {
+ String sym = (String)oidSymbols.get(typeAndValue.getType());
+
+ if (sym != null)
+ {
+ buf.append(sym);
+ }
+ else
+ {
+ buf.append(typeAndValue.getType().getId());
+ }
+
+ buf.append('=');
+
+ buf.append(valueToString(typeAndValue.getValue()));
+ }
+
+ public static String valueToString(ASN1Encodable value)
+ {
+ StringBuffer vBuf = new StringBuffer();
+
+ if (value instanceof ASN1String && !(value instanceof DERUniversalString))
+ {
+ String v = ((ASN1String)value).getString();
+ if (v.length() > 0 && v.charAt(0) == '#')
+ {
+ vBuf.append("\\" + v);
+ }
+ else
+ {
+ vBuf.append(v);
+ }
+ }
+ else
+ {
+ try
+ {
+ vBuf.append("#" + bytesToString(Hex.encode(value.toASN1Primitive().getEncoded(ASN1Encoding.DER))));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("Other value has no encoded form");
+ }
+ }
+
+ int end = vBuf.length();
+ int index = 0;
+
+ if (vBuf.length() >= 2 && vBuf.charAt(0) == '\\' && vBuf.charAt(1) == '#')
+ {
+ index += 2;
+ }
+
+ while (index != end)
+ {
+ if ((vBuf.charAt(index) == ',')
+ || (vBuf.charAt(index) == '"')
+ || (vBuf.charAt(index) == '\\')
+ || (vBuf.charAt(index) == '+')
+ || (vBuf.charAt(index) == '=')
+ || (vBuf.charAt(index) == '<')
+ || (vBuf.charAt(index) == '>')
+ || (vBuf.charAt(index) == ';'))
+ {
+ vBuf.insert(index, "\\");
+ index++;
+ end++;
+ }
+
+ index++;
+ }
+
+ int start = 0;
+ if (vBuf.length() > 0)
+ {
+ while (vBuf.charAt(start) == ' ')
+ {
+ vBuf.insert(start, "\\");
+ start += 2;
+ }
+ }
+
+ int endBuf = vBuf.length() - 1;
+
+ while (endBuf >= 0 && vBuf.charAt(endBuf) == ' ')
+ {
+ vBuf.insert(endBuf, '\\');
+ endBuf--;
+ }
+
+ return vBuf.toString();
+ }
+
+ private static String bytesToString(
+ byte[] data)
+ {
+ char[] cs = new char[data.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ cs[i] = (char)(data[i] & 0xff);
+ }
+
+ return new String(cs);
+ }
+
+ public static String canonicalize(String s)
+ {
+ String value = Strings.toLowerCase(s.trim());
+
+ if (value.length() > 0 && value.charAt(0) == '#')
+ {
+ ASN1Primitive obj = decodeObject(value);
+
+ if (obj instanceof ASN1String)
+ {
+ value = Strings.toLowerCase(((ASN1String)obj).getString().trim());
+ }
+ }
+
+ value = stripInternalSpaces(value);
+
+ return value;
+ }
+
+ private static ASN1Primitive decodeObject(String oValue)
+ {
+ try
+ {
+ return ASN1Primitive.fromByteArray(Hex.decode(oValue.substring(1)));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("unknown encoding in name: " + e);
+ }
+ }
+
+ public static String stripInternalSpaces(
+ String str)
+ {
+ StringBuffer res = new StringBuffer();
+
+ if (str.length() != 0)
+ {
+ char c1 = str.charAt(0);
+
+ res.append(c1);
+
+ for (int k = 1; k < str.length(); k++)
+ {
+ char c2 = str.charAt(k);
+ if (!(c1 == ' ' && c2 == ' '))
+ {
+ res.append(c2);
+ }
+ c1 = c2;
+ }
+ }
+
+ return res.toString();
+ }
+
+ public static boolean rDNAreEqual(RDN rdn1, RDN rdn2)
+ {
+ if (rdn1.isMultiValued())
+ {
+ if (rdn2.isMultiValued())
+ {
+ AttributeTypeAndValue[] atvs1 = rdn1.getTypesAndValues();
+ AttributeTypeAndValue[] atvs2 = rdn2.getTypesAndValues();
+
+ if (atvs1.length != atvs2.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != atvs1.length; i++)
+ {
+ if (!atvAreEqual(atvs1[i], atvs2[i]))
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (!rdn2.isMultiValued())
+ {
+ return atvAreEqual(rdn1.getFirst(), rdn2.getFirst());
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static boolean atvAreEqual(AttributeTypeAndValue atv1, AttributeTypeAndValue atv2)
+ {
+ if (atv1 == atv2)
+ {
+ return true;
+ }
+
+ if (atv1 == null)
+ {
+ return false;
+ }
+
+ if (atv2 == null)
+ {
+ return false;
+ }
+
+ ASN1ObjectIdentifier o1 = atv1.getType();
+ ASN1ObjectIdentifier o2 = atv2.getType();
+
+ if (!o1.equals(o2))
+ {
+ return false;
+ }
+
+ String v1 = IETFUtils.canonicalize(IETFUtils.valueToString(atv1.getValue()));
+ String v2 = IETFUtils.canonicalize(IETFUtils.valueToString(atv2.getValue()));
+
+ if (!v1.equals(v2))
+ {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x500/style/RFC4519Style.java b/core/src/main/java/org/bouncycastle/asn1/x500/style/RFC4519Style.java
new file mode 100644
index 00000000..84869895
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x500/style/RFC4519Style.java
@@ -0,0 +1,358 @@
+package org.bouncycastle.asn1.x500.style;
+
+import java.io.IOException;
+import java.util.Hashtable;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.DERPrintableString;
+import org.bouncycastle.asn1.DERUTF8String;
+import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
+import org.bouncycastle.asn1.x500.RDN;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x500.X500NameStyle;
+
+public class RFC4519Style
+ implements X500NameStyle
+{
+ public static final X500NameStyle INSTANCE = new RFC4519Style();
+
+ public static final ASN1ObjectIdentifier businessCategory = new ASN1ObjectIdentifier("2.5.4.15");
+ public static final ASN1ObjectIdentifier c = new ASN1ObjectIdentifier("2.5.4.6");
+ public static final ASN1ObjectIdentifier cn = new ASN1ObjectIdentifier("2.5.4.3");
+ public static final ASN1ObjectIdentifier dc = new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.25");
+ public static final ASN1ObjectIdentifier description = new ASN1ObjectIdentifier("2.5.4.13");
+ public static final ASN1ObjectIdentifier destinationIndicator = new ASN1ObjectIdentifier("2.5.4.27");
+ public static final ASN1ObjectIdentifier distinguishedName = new ASN1ObjectIdentifier("2.5.4.49");
+ public static final ASN1ObjectIdentifier dnQualifier = new ASN1ObjectIdentifier("2.5.4.46");
+ public static final ASN1ObjectIdentifier enhancedSearchGuide = new ASN1ObjectIdentifier("2.5.4.47");
+ public static final ASN1ObjectIdentifier facsimileTelephoneNumber = new ASN1ObjectIdentifier("2.5.4.23");
+ public static final ASN1ObjectIdentifier generationQualifier = new ASN1ObjectIdentifier("2.5.4.44");
+ public static final ASN1ObjectIdentifier givenName = new ASN1ObjectIdentifier("2.5.4.42");
+ public static final ASN1ObjectIdentifier houseIdentifier = new ASN1ObjectIdentifier("2.5.4.51");
+ public static final ASN1ObjectIdentifier initials = new ASN1ObjectIdentifier("2.5.4.43");
+ public static final ASN1ObjectIdentifier internationalISDNNumber = new ASN1ObjectIdentifier("2.5.4.25");
+ public static final ASN1ObjectIdentifier l = new ASN1ObjectIdentifier("2.5.4.7");
+ public static final ASN1ObjectIdentifier member = new ASN1ObjectIdentifier("2.5.4.31");
+ public static final ASN1ObjectIdentifier name = new ASN1ObjectIdentifier("2.5.4.41");
+ public static final ASN1ObjectIdentifier o = new ASN1ObjectIdentifier("2.5.4.10");
+ public static final ASN1ObjectIdentifier ou = new ASN1ObjectIdentifier("2.5.4.11");
+ public static final ASN1ObjectIdentifier owner = new ASN1ObjectIdentifier("2.5.4.32");
+ public static final ASN1ObjectIdentifier physicalDeliveryOfficeName = new ASN1ObjectIdentifier("2.5.4.19");
+ public static final ASN1ObjectIdentifier postalAddress = new ASN1ObjectIdentifier("2.5.4.16");
+ public static final ASN1ObjectIdentifier postalCode = new ASN1ObjectIdentifier("2.5.4.17");
+ public static final ASN1ObjectIdentifier postOfficeBox = new ASN1ObjectIdentifier("2.5.4.18");
+ public static final ASN1ObjectIdentifier preferredDeliveryMethod = new ASN1ObjectIdentifier("2.5.4.28");
+ public static final ASN1ObjectIdentifier registeredAddress = new ASN1ObjectIdentifier("2.5.4.26");
+ public static final ASN1ObjectIdentifier roleOccupant = new ASN1ObjectIdentifier("2.5.4.33");
+ public static final ASN1ObjectIdentifier searchGuide = new ASN1ObjectIdentifier("2.5.4.14");
+ public static final ASN1ObjectIdentifier seeAlso = new ASN1ObjectIdentifier("2.5.4.34");
+ public static final ASN1ObjectIdentifier serialNumber = new ASN1ObjectIdentifier("2.5.4.5");
+ public static final ASN1ObjectIdentifier sn = new ASN1ObjectIdentifier("2.5.4.4");
+ public static final ASN1ObjectIdentifier st = new ASN1ObjectIdentifier("2.5.4.8");
+ public static final ASN1ObjectIdentifier street = new ASN1ObjectIdentifier("2.5.4.9");
+ public static final ASN1ObjectIdentifier telephoneNumber = new ASN1ObjectIdentifier("2.5.4.20");
+ public static final ASN1ObjectIdentifier teletexTerminalIdentifier = new ASN1ObjectIdentifier("2.5.4.22");
+ public static final ASN1ObjectIdentifier telexNumber = new ASN1ObjectIdentifier("2.5.4.21");
+ public static final ASN1ObjectIdentifier title = new ASN1ObjectIdentifier("2.5.4.12");
+ public static final ASN1ObjectIdentifier uid = new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.1");
+ public static final ASN1ObjectIdentifier uniqueMember = new ASN1ObjectIdentifier("2.5.4.50");
+ public static final ASN1ObjectIdentifier userPassword = new ASN1ObjectIdentifier("2.5.4.35");
+ public static final ASN1ObjectIdentifier x121Address = new ASN1ObjectIdentifier("2.5.4.24");
+ public static final ASN1ObjectIdentifier x500UniqueIdentifier = new ASN1ObjectIdentifier("2.5.4.45");
+
+ /**
+ * default look up table translating OID values into their common symbols following
+ * the convention in RFC 2253 with a few extras
+ */
+ private static final Hashtable DefaultSymbols = new Hashtable();
+
+ /**
+ * look up table translating common symbols into their OIDS.
+ */
+ private static final Hashtable DefaultLookUp = new Hashtable();
+
+ static
+ {
+ DefaultSymbols.put(businessCategory, "businessCategory");
+ DefaultSymbols.put(c, "c");
+ DefaultSymbols.put(cn, "cn");
+ DefaultSymbols.put(dc, "dc");
+ DefaultSymbols.put(description, "description");
+ DefaultSymbols.put(destinationIndicator, "destinationIndicator");
+ DefaultSymbols.put(distinguishedName, "distinguishedName");
+ DefaultSymbols.put(dnQualifier, "dnQualifier");
+ DefaultSymbols.put(enhancedSearchGuide, "enhancedSearchGuide");
+ DefaultSymbols.put(facsimileTelephoneNumber, "facsimileTelephoneNumber");
+ DefaultSymbols.put(generationQualifier, "generationQualifier");
+ DefaultSymbols.put(givenName, "givenName");
+ DefaultSymbols.put(houseIdentifier, "houseIdentifier");
+ DefaultSymbols.put(initials, "initials");
+ DefaultSymbols.put(internationalISDNNumber, "internationalISDNNumber");
+ DefaultSymbols.put(l, "l");
+ DefaultSymbols.put(member, "member");
+ DefaultSymbols.put(name, "name");
+ DefaultSymbols.put(o, "o");
+ DefaultSymbols.put(ou, "ou");
+ DefaultSymbols.put(owner, "owner");
+ DefaultSymbols.put(physicalDeliveryOfficeName, "physicalDeliveryOfficeName");
+ DefaultSymbols.put(postalAddress, "postalAddress");
+ DefaultSymbols.put(postalCode, "postalCode");
+ DefaultSymbols.put(postOfficeBox, "postOfficeBox");
+ DefaultSymbols.put(preferredDeliveryMethod, "preferredDeliveryMethod");
+ DefaultSymbols.put(registeredAddress, "registeredAddress");
+ DefaultSymbols.put(roleOccupant, "roleOccupant");
+ DefaultSymbols.put(searchGuide, "searchGuide");
+ DefaultSymbols.put(seeAlso, "seeAlso");
+ DefaultSymbols.put(serialNumber, "serialNumber");
+ DefaultSymbols.put(sn, "sn");
+ DefaultSymbols.put(st, "st");
+ DefaultSymbols.put(street, "street");
+ DefaultSymbols.put(telephoneNumber, "telephoneNumber");
+ DefaultSymbols.put(teletexTerminalIdentifier, "teletexTerminalIdentifier");
+ DefaultSymbols.put(telexNumber, "telexNumber");
+ DefaultSymbols.put(title, "title");
+ DefaultSymbols.put(uid, "uid");
+ DefaultSymbols.put(uniqueMember, "uniqueMember");
+ DefaultSymbols.put(userPassword, "userPassword");
+ DefaultSymbols.put(x121Address, "x121Address");
+ DefaultSymbols.put(x500UniqueIdentifier, "x500UniqueIdentifier");
+
+ DefaultLookUp.put("businesscategory", businessCategory);
+ DefaultLookUp.put("c", c);
+ DefaultLookUp.put("cn", cn);
+ DefaultLookUp.put("dc", dc);
+ DefaultLookUp.put("description", description);
+ DefaultLookUp.put("destinationindicator", destinationIndicator);
+ DefaultLookUp.put("distinguishedname", distinguishedName);
+ DefaultLookUp.put("dnqualifier", dnQualifier);
+ DefaultLookUp.put("enhancedsearchguide", enhancedSearchGuide);
+ DefaultLookUp.put("facsimiletelephonenumber", facsimileTelephoneNumber);
+ DefaultLookUp.put("generationqualifier", generationQualifier);
+ DefaultLookUp.put("givenname", givenName);
+ DefaultLookUp.put("houseidentifier", houseIdentifier);
+ DefaultLookUp.put("initials", initials);
+ DefaultLookUp.put("internationalisdnnumber", internationalISDNNumber);
+ DefaultLookUp.put("l", l);
+ DefaultLookUp.put("member", member);
+ DefaultLookUp.put("name", name);
+ DefaultLookUp.put("o", o);
+ DefaultLookUp.put("ou", ou);
+ DefaultLookUp.put("owner", owner);
+ DefaultLookUp.put("physicaldeliveryofficename", physicalDeliveryOfficeName);
+ DefaultLookUp.put("postaladdress", postalAddress);
+ DefaultLookUp.put("postalcode", postalCode);
+ DefaultLookUp.put("postofficebox", postOfficeBox);
+ DefaultLookUp.put("preferreddeliverymethod", preferredDeliveryMethod);
+ DefaultLookUp.put("registeredaddress", registeredAddress);
+ DefaultLookUp.put("roleoccupant", roleOccupant);
+ DefaultLookUp.put("searchguide", searchGuide);
+ DefaultLookUp.put("seealso", seeAlso);
+ DefaultLookUp.put("serialnumber", serialNumber);
+ DefaultLookUp.put("sn", sn);
+ DefaultLookUp.put("st", st);
+ DefaultLookUp.put("street", street);
+ DefaultLookUp.put("telephonenumber", telephoneNumber);
+ DefaultLookUp.put("teletexterminalidentifier", teletexTerminalIdentifier);
+ DefaultLookUp.put("telexnumber", telexNumber);
+ DefaultLookUp.put("title", title);
+ DefaultLookUp.put("uid", uid);
+ DefaultLookUp.put("uniquemember", uniqueMember);
+ DefaultLookUp.put("userpassword", userPassword);
+ DefaultLookUp.put("x121address", x121Address);
+ DefaultLookUp.put("x500uniqueidentifier", x500UniqueIdentifier);
+
+ // TODO: need to add correct matching for equality comparisons.
+ }
+
+ protected RFC4519Style()
+ {
+
+ }
+
+ public ASN1Encodable stringToValue(ASN1ObjectIdentifier oid, String value)
+ {
+ if (value.length() != 0 && value.charAt(0) == '#')
+ {
+ try
+ {
+ return IETFUtils.valueFromHexString(value, 1);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("can't recode value for oid " + oid.getId());
+ }
+ }
+ else
+ {
+ if (value.length() != 0 && value.charAt(0) == '\\')
+ {
+ value = value.substring(1);
+ }
+ if (oid.equals(dc))
+ {
+ return new DERIA5String(value);
+ }
+ else if (oid.equals(c) || oid.equals(serialNumber) || oid.equals(dnQualifier)
+ || oid.equals(telephoneNumber))
+ {
+ return new DERPrintableString(value);
+ }
+ }
+
+ return new DERUTF8String(value);
+ }
+
+ public String oidToDisplayName(ASN1ObjectIdentifier oid)
+ {
+ return (String)DefaultSymbols.get(oid);
+ }
+
+ public String[] oidToAttrNames(ASN1ObjectIdentifier oid)
+ {
+ return IETFUtils.findAttrNamesForOID(oid, DefaultLookUp);
+ }
+
+ public ASN1ObjectIdentifier attrNameToOID(String attrName)
+ {
+ return IETFUtils.decodeAttrName(attrName, DefaultLookUp);
+ }
+
+ public boolean areEqual(X500Name name1, X500Name name2)
+ {
+ RDN[] rdns1 = name1.getRDNs();
+ RDN[] rdns2 = name2.getRDNs();
+
+ if (rdns1.length != rdns2.length)
+ {
+ return false;
+ }
+
+ boolean reverse = false;
+
+ if (rdns1[0].getFirst() != null && rdns2[0].getFirst() != null)
+ {
+ reverse = !rdns1[0].getFirst().getType().equals(rdns2[0].getFirst().getType()); // guess forward
+ }
+
+ for (int i = 0; i != rdns1.length; i++)
+ {
+ if (!foundMatch(reverse, rdns1[i], rdns2))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean foundMatch(boolean reverse, RDN rdn, RDN[] possRDNs)
+ {
+ if (reverse)
+ {
+ for (int i = possRDNs.length - 1; i >= 0; i--)
+ {
+ if (possRDNs[i] != null && rdnAreEqual(rdn, possRDNs[i]))
+ {
+ possRDNs[i] = null;
+ return true;
+ }
+ }
+ }
+ else
+ {
+ for (int i = 0; i != possRDNs.length; i++)
+ {
+ if (possRDNs[i] != null && rdnAreEqual(rdn, possRDNs[i]))
+ {
+ possRDNs[i] = null;
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ protected boolean rdnAreEqual(RDN rdn1, RDN rdn2)
+ {
+ return IETFUtils.rDNAreEqual(rdn1, rdn2);
+ }
+
+ // parse backwards
+ public RDN[] fromString(String dirName)
+ {
+ RDN[] tmp = IETFUtils.rDNsFromString(dirName, this);
+ RDN[] res = new RDN[tmp.length];
+
+ for (int i = 0; i != tmp.length; i++)
+ {
+ res[res.length - i - 1] = tmp[i];
+ }
+
+ return res;
+ }
+
+ public int calculateHashCode(X500Name name)
+ {
+ int hashCodeValue = 0;
+ RDN[] rdns = name.getRDNs();
+
+ // this needs to be order independent, like equals
+ for (int i = 0; i != rdns.length; i++)
+ {
+ if (rdns[i].isMultiValued())
+ {
+ AttributeTypeAndValue[] atv = rdns[i].getTypesAndValues();
+
+ for (int j = 0; j != atv.length; j++)
+ {
+ hashCodeValue ^= atv[j].getType().hashCode();
+ hashCodeValue ^= calcHashCode(atv[j].getValue());
+ }
+ }
+ else
+ {
+ hashCodeValue ^= rdns[i].getFirst().getType().hashCode();
+ hashCodeValue ^= calcHashCode(rdns[i].getFirst().getValue());
+ }
+ }
+
+ return hashCodeValue;
+ }
+
+ private int calcHashCode(ASN1Encodable enc)
+ {
+ String value = IETFUtils.valueToString(enc);
+
+ value = IETFUtils.canonicalize(value);
+
+ return value.hashCode();
+ }
+
+ // convert in reverse
+ public String toString(X500Name name)
+ {
+ StringBuffer buf = new StringBuffer();
+ boolean first = true;
+
+ RDN[] rdns = name.getRDNs();
+
+ for (int i = rdns.length - 1; i >= 0; i--)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ buf.append(',');
+ }
+
+ IETFUtils.appendRDN(buf, rdns[i], DefaultSymbols);
+ }
+
+ return buf.toString();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x500/style/X500NameTokenizer.java b/core/src/main/java/org/bouncycastle/asn1/x500/style/X500NameTokenizer.java
new file mode 100644
index 00000000..2c8e3fcf
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x500/style/X500NameTokenizer.java
@@ -0,0 +1,90 @@
+package org.bouncycastle.asn1.x500.style;
+
+/**
+ * class for breaking up an X500 Name into it's component tokens, ala
+ * java.util.StringTokenizer. We need this class as some of the
+ * lightweight Java environment don't support classes like
+ * StringTokenizer.
+ */
+class X500NameTokenizer
+{
+ private String value;
+ private int index;
+ private char separator;
+ private StringBuffer buf = new StringBuffer();
+
+ public X500NameTokenizer(
+ String oid)
+ {
+ this(oid, ',');
+ }
+
+ public X500NameTokenizer(
+ String oid,
+ char separator)
+ {
+ this.value = oid;
+ this.index = -1;
+ this.separator = separator;
+ }
+
+ public boolean hasMoreTokens()
+ {
+ return (index != value.length());
+ }
+
+ public String nextToken()
+ {
+ if (index == value.length())
+ {
+ return null;
+ }
+
+ int end = index + 1;
+ boolean quoted = false;
+ boolean escaped = false;
+
+ buf.setLength(0);
+
+ while (end != value.length())
+ {
+ char c = value.charAt(end);
+
+ if (c == '"')
+ {
+ if (!escaped)
+ {
+ quoted = !quoted;
+ }
+ buf.append(c);
+ escaped = false;
+ }
+ else
+ {
+ if (escaped || quoted)
+ {
+ buf.append(c);
+ escaped = false;
+ }
+ else if (c == '\\')
+ {
+ buf.append(c);
+ escaped = true;
+ }
+ else if (c == separator)
+ {
+ break;
+ }
+ else
+ {
+ buf.append(c);
+ }
+ }
+ end++;
+ }
+
+ index = end;
+
+ return buf.toString();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/AccessDescription.java b/core/src/main/java/org/bouncycastle/asn1/x509/AccessDescription.java
new file mode 100644
index 00000000..a1aaca4e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/AccessDescription.java
@@ -0,0 +1,98 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * The AccessDescription object.
+ * <pre>
+ * AccessDescription ::= SEQUENCE {
+ * accessMethod OBJECT IDENTIFIER,
+ * accessLocation GeneralName }
+ * </pre>
+ */
+public class AccessDescription
+ extends ASN1Object
+{
+ public final static ASN1ObjectIdentifier id_ad_caIssuers = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.2");
+
+ public final static ASN1ObjectIdentifier id_ad_ocsp = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1");
+
+ ASN1ObjectIdentifier accessMethod = null;
+ GeneralName accessLocation = null;
+
+ public static AccessDescription getInstance(
+ Object obj)
+ {
+ if (obj instanceof AccessDescription)
+ {
+ return (AccessDescription)obj;
+ }
+ else if (obj != null)
+ {
+ return new AccessDescription(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private AccessDescription(
+ ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("wrong number of elements in sequence");
+ }
+
+ accessMethod = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+ accessLocation = GeneralName.getInstance(seq.getObjectAt(1));
+ }
+
+ /**
+ * create an AccessDescription with the oid and location provided.
+ */
+ public AccessDescription(
+ ASN1ObjectIdentifier oid,
+ GeneralName location)
+ {
+ accessMethod = oid;
+ accessLocation = location;
+ }
+
+ /**
+ *
+ * @return the access method.
+ */
+ public ASN1ObjectIdentifier getAccessMethod()
+ {
+ return accessMethod;
+ }
+
+ /**
+ *
+ * @return the access location
+ */
+ public GeneralName getAccessLocation()
+ {
+ return accessLocation;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector accessDescription = new ASN1EncodableVector();
+
+ accessDescription.add(accessMethod);
+ accessDescription.add(accessLocation);
+
+ return new DERSequence(accessDescription);
+ }
+
+ public String toString()
+ {
+ return ("AccessDescription: Oid(" + this.accessMethod.getId() + ")");
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/AlgorithmIdentifier.java b/core/src/main/java/org/bouncycastle/asn1/x509/AlgorithmIdentifier.java
new file mode 100644
index 00000000..d250bf1e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/AlgorithmIdentifier.java
@@ -0,0 +1,173 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERSequence;
+
+public class AlgorithmIdentifier
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier objectId;
+ private ASN1Encodable parameters;
+ private boolean parametersDefined = false;
+
+ public static AlgorithmIdentifier getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static AlgorithmIdentifier getInstance(
+ Object obj)
+ {
+ if (obj== null || obj instanceof AlgorithmIdentifier)
+ {
+ return (AlgorithmIdentifier)obj;
+ }
+
+ // TODO: delete
+ if (obj instanceof ASN1ObjectIdentifier)
+ {
+ return new AlgorithmIdentifier((ASN1ObjectIdentifier)obj);
+ }
+
+ // TODO: delete
+ if (obj instanceof String)
+ {
+ return new AlgorithmIdentifier((String)obj);
+ }
+
+ return new AlgorithmIdentifier(ASN1Sequence.getInstance(obj));
+ }
+
+ public AlgorithmIdentifier(
+ ASN1ObjectIdentifier objectId)
+ {
+ this.objectId = objectId;
+ }
+
+ /**
+ * @deprecated use ASN1ObjectIdentifier
+ * @param objectId
+ */
+ public AlgorithmIdentifier(
+ String objectId)
+ {
+ this.objectId = new ASN1ObjectIdentifier(objectId);
+ }
+
+ /**
+ * @deprecated use ASN1ObjectIdentifier
+ * @param objectId
+ */
+ public AlgorithmIdentifier(
+ DERObjectIdentifier objectId)
+ {
+ this.objectId = new ASN1ObjectIdentifier(objectId.getId());
+ }
+
+ /**
+ * @deprecated use ASN1ObjectIdentifier
+ * @param objectId
+ * @param parameters
+ */
+ public AlgorithmIdentifier(
+ DERObjectIdentifier objectId,
+ ASN1Encodable parameters)
+ {
+ parametersDefined = true;
+ this.objectId = new ASN1ObjectIdentifier(objectId.getId());
+ this.parameters = parameters;
+ }
+
+ public AlgorithmIdentifier(
+ ASN1ObjectIdentifier objectId,
+ ASN1Encodable parameters)
+ {
+ parametersDefined = true;
+ this.objectId = objectId;
+ this.parameters = parameters;
+ }
+
+ /**
+ * @deprecated use AlgorithmIdentifier.getInstance()
+ * @param seq
+ */
+ public AlgorithmIdentifier(
+ ASN1Sequence seq)
+ {
+ if (seq.size() < 1 || seq.size() > 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ objectId = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+
+ if (seq.size() == 2)
+ {
+ parametersDefined = true;
+ parameters = seq.getObjectAt(1);
+ }
+ else
+ {
+ parameters = null;
+ }
+ }
+
+ public ASN1ObjectIdentifier getAlgorithm()
+ {
+ return new ASN1ObjectIdentifier(objectId.getId());
+ }
+
+ /**
+ * @deprecated use getAlgorithm
+ * @return
+ */
+ public ASN1ObjectIdentifier getObjectId()
+ {
+ return objectId;
+ }
+
+ public ASN1Encodable getParameters()
+ {
+ return parameters;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * AlgorithmIdentifier ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY algorithm OPTIONAL }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(objectId);
+
+ if (parametersDefined)
+ {
+ if (parameters != null)
+ {
+ v.add(parameters);
+ }
+ else
+ {
+ v.add(DERNull.INSTANCE);
+ }
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/AttCertIssuer.java b/core/src/main/java/org/bouncycastle/asn1/x509/AttCertIssuer.java
new file mode 100644
index 00000000..21907c68
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/AttCertIssuer.java
@@ -0,0 +1,91 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class AttCertIssuer
+ extends ASN1Object
+ implements ASN1Choice
+{
+ ASN1Encodable obj;
+ ASN1Primitive choiceObj;
+
+ public static AttCertIssuer getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof AttCertIssuer)
+ {
+ return (AttCertIssuer)obj;
+ }
+ else if (obj instanceof V2Form)
+ {
+ return new AttCertIssuer(V2Form.getInstance(obj));
+ }
+ else if (obj instanceof GeneralNames)
+ {
+ return new AttCertIssuer((GeneralNames)obj);
+ }
+ else if (obj instanceof ASN1TaggedObject)
+ {
+ return new AttCertIssuer(V2Form.getInstance((ASN1TaggedObject)obj, false));
+ }
+ else if (obj instanceof ASN1Sequence)
+ {
+ return new AttCertIssuer(GeneralNames.getInstance(obj));
+ }
+
+ throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
+ }
+
+ public static AttCertIssuer getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject()); // must be explicitly tagged
+ }
+
+ /**
+ * Don't use this one if you are trying to be RFC 3281 compliant.
+ * Use it for v1 attribute certificates only.
+ *
+ * @param names our GeneralNames structure
+ */
+ public AttCertIssuer(
+ GeneralNames names)
+ {
+ obj = names;
+ choiceObj = obj.toASN1Primitive();
+ }
+
+ public AttCertIssuer(
+ V2Form v2Form)
+ {
+ obj = v2Form;
+ choiceObj = new DERTaggedObject(false, 0, obj);
+ }
+
+ public ASN1Encodable getIssuer()
+ {
+ return obj;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * AttCertIssuer ::= CHOICE {
+ * v1Form GeneralNames, -- MUST NOT be used in this
+ * -- profile
+ * v2Form [0] V2Form -- v2 only
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return choiceObj;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/AttCertValidityPeriod.java b/core/src/main/java/org/bouncycastle/asn1/x509/AttCertValidityPeriod.java
new file mode 100644
index 00000000..2f781564
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/AttCertValidityPeriod.java
@@ -0,0 +1,84 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class AttCertValidityPeriod
+ extends ASN1Object
+{
+ ASN1GeneralizedTime notBeforeTime;
+ ASN1GeneralizedTime notAfterTime;
+
+ public static AttCertValidityPeriod getInstance(
+ Object obj)
+ {
+ if (obj instanceof AttCertValidityPeriod)
+ {
+ return (AttCertValidityPeriod)obj;
+ }
+ else if (obj != null)
+ {
+ return new AttCertValidityPeriod(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private AttCertValidityPeriod(
+ ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ notBeforeTime = ASN1GeneralizedTime.getInstance(seq.getObjectAt(0));
+ notAfterTime = ASN1GeneralizedTime.getInstance(seq.getObjectAt(1));
+ }
+
+ /**
+ * @param notBeforeTime
+ * @param notAfterTime
+ */
+ public AttCertValidityPeriod(
+ ASN1GeneralizedTime notBeforeTime,
+ ASN1GeneralizedTime notAfterTime)
+ {
+ this.notBeforeTime = notBeforeTime;
+ this.notAfterTime = notAfterTime;
+ }
+
+ public ASN1GeneralizedTime getNotBeforeTime()
+ {
+ return notBeforeTime;
+ }
+
+ public ASN1GeneralizedTime getNotAfterTime()
+ {
+ return notAfterTime;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * AttCertValidityPeriod ::= SEQUENCE {
+ * notBeforeTime GeneralizedTime,
+ * notAfterTime GeneralizedTime
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(notBeforeTime);
+ v.add(notAfterTime);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/Attribute.java b/core/src/main/java/org/bouncycastle/asn1/x509/Attribute.java
new file mode 100644
index 00000000..b8d4bde7
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/Attribute.java
@@ -0,0 +1,93 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.DERSequence;
+
+public class Attribute
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier attrType;
+ private ASN1Set attrValues;
+
+ /**
+ * return an Attribute object from the given object.
+ *
+ * @param o the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static Attribute getInstance(
+ Object o)
+ {
+ if (o instanceof Attribute)
+ {
+ return (Attribute)o;
+ }
+
+ if (o != null)
+ {
+ return new Attribute(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ private Attribute(
+ ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ attrType = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+ attrValues = ASN1Set.getInstance(seq.getObjectAt(1));
+ }
+
+ public Attribute(
+ ASN1ObjectIdentifier attrType,
+ ASN1Set attrValues)
+ {
+ this.attrType = attrType;
+ this.attrValues = attrValues;
+ }
+
+ public ASN1ObjectIdentifier getAttrType()
+ {
+ return new ASN1ObjectIdentifier(attrType.getId());
+ }
+
+ public ASN1Encodable[] getAttributeValues()
+ {
+ return attrValues.toArray();
+ }
+
+ public ASN1Set getAttrValues()
+ {
+ return attrValues;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * Attribute ::= SEQUENCE {
+ * attrType OBJECT IDENTIFIER,
+ * attrValues SET OF AttributeValue
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(attrType);
+ v.add(attrValues);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java b/core/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java
new file mode 100644
index 00000000..92aa0f7b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java
@@ -0,0 +1,94 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+
+public class AttributeCertificate
+ extends ASN1Object
+{
+ AttributeCertificateInfo acinfo;
+ AlgorithmIdentifier signatureAlgorithm;
+ DERBitString signatureValue;
+
+ /**
+ * @param obj
+ * @return an AttributeCertificate object
+ */
+ public static AttributeCertificate getInstance(Object obj)
+ {
+ if (obj instanceof AttributeCertificate)
+ {
+ return (AttributeCertificate)obj;
+ }
+ else if (obj != null)
+ {
+ return new AttributeCertificate(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public AttributeCertificate(
+ AttributeCertificateInfo acinfo,
+ AlgorithmIdentifier signatureAlgorithm,
+ DERBitString signatureValue)
+ {
+ this.acinfo = acinfo;
+ this.signatureAlgorithm = signatureAlgorithm;
+ this.signatureValue = signatureValue;
+ }
+
+ public AttributeCertificate(
+ ASN1Sequence seq)
+ {
+ if (seq.size() != 3)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ this.acinfo = AttributeCertificateInfo.getInstance(seq.getObjectAt(0));
+ this.signatureAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
+ this.signatureValue = DERBitString.getInstance(seq.getObjectAt(2));
+ }
+
+ public AttributeCertificateInfo getAcinfo()
+ {
+ return acinfo;
+ }
+
+ public AlgorithmIdentifier getSignatureAlgorithm()
+ {
+ return signatureAlgorithm;
+ }
+
+ public DERBitString getSignatureValue()
+ {
+ return signatureValue;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * AttributeCertificate ::= SEQUENCE {
+ * acinfo AttributeCertificateInfo,
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signatureValue BIT STRING
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(acinfo);
+ v.add(signatureAlgorithm);
+ v.add(signatureValue);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java b/core/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java
new file mode 100644
index 00000000..7b9d4503
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java
@@ -0,0 +1,166 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+
+public class AttributeCertificateInfo
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private Holder holder;
+ private AttCertIssuer issuer;
+ private AlgorithmIdentifier signature;
+ private ASN1Integer serialNumber;
+ private AttCertValidityPeriod attrCertValidityPeriod;
+ private ASN1Sequence attributes;
+ private DERBitString issuerUniqueID;
+ private Extensions extensions;
+
+ public static AttributeCertificateInfo getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static AttributeCertificateInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof AttributeCertificateInfo)
+ {
+ return (AttributeCertificateInfo)obj;
+ }
+ else if (obj != null)
+ {
+ return new AttributeCertificateInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private AttributeCertificateInfo(
+ ASN1Sequence seq)
+ {
+ if (seq.size() < 7 || seq.size() > 9)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ this.version = ASN1Integer.getInstance(seq.getObjectAt(0));
+ this.holder = Holder.getInstance(seq.getObjectAt(1));
+ this.issuer = AttCertIssuer.getInstance(seq.getObjectAt(2));
+ this.signature = AlgorithmIdentifier.getInstance(seq.getObjectAt(3));
+ this.serialNumber = ASN1Integer.getInstance(seq.getObjectAt(4));
+ this.attrCertValidityPeriod = AttCertValidityPeriod.getInstance(seq.getObjectAt(5));
+ this.attributes = ASN1Sequence.getInstance(seq.getObjectAt(6));
+
+ for (int i = 7; i < seq.size(); i++)
+ {
+ ASN1Encodable obj = (ASN1Encodable)seq.getObjectAt(i);
+
+ if (obj instanceof DERBitString)
+ {
+ this.issuerUniqueID = DERBitString.getInstance(seq.getObjectAt(i));
+ }
+ else if (obj instanceof ASN1Sequence || obj instanceof Extensions)
+ {
+ this.extensions = Extensions.getInstance(seq.getObjectAt(i));
+ }
+ }
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public Holder getHolder()
+ {
+ return holder;
+ }
+
+ public AttCertIssuer getIssuer()
+ {
+ return issuer;
+ }
+
+ public AlgorithmIdentifier getSignature()
+ {
+ return signature;
+ }
+
+ public ASN1Integer getSerialNumber()
+ {
+ return serialNumber;
+ }
+
+ public AttCertValidityPeriod getAttrCertValidityPeriod()
+ {
+ return attrCertValidityPeriod;
+ }
+
+ public ASN1Sequence getAttributes()
+ {
+ return attributes;
+ }
+
+ public DERBitString getIssuerUniqueID()
+ {
+ return issuerUniqueID;
+ }
+
+ public Extensions getExtensions()
+ {
+ return extensions;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * AttributeCertificateInfo ::= SEQUENCE {
+ * version AttCertVersion -- version is v2,
+ * holder Holder,
+ * issuer AttCertIssuer,
+ * signature AlgorithmIdentifier,
+ * serialNumber CertificateSerialNumber,
+ * attrCertValidityPeriod AttCertValidityPeriod,
+ * attributes SEQUENCE OF Attribute,
+ * issuerUniqueID UniqueIdentifier OPTIONAL,
+ * extensions Extensions OPTIONAL
+ * }
+ *
+ * AttCertVersion ::= INTEGER { v2(1) }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+ v.add(holder);
+ v.add(issuer);
+ v.add(signature);
+ v.add(serialNumber);
+ v.add(attrCertValidityPeriod);
+ v.add(attributes);
+
+ if (issuerUniqueID != null)
+ {
+ v.add(issuerUniqueID);
+ }
+
+ if (extensions != null)
+ {
+ v.add(extensions);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/AuthorityInformationAccess.java b/core/src/main/java/org/bouncycastle/asn1/x509/AuthorityInformationAccess.java
new file mode 100644
index 00000000..3a239ab0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/AuthorityInformationAccess.java
@@ -0,0 +1,101 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * The AuthorityInformationAccess object.
+ * <pre>
+ * id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
+ *
+ * AuthorityInfoAccessSyntax ::=
+ * SEQUENCE SIZE (1..MAX) OF AccessDescription
+ * AccessDescription ::= SEQUENCE {
+ * accessMethod OBJECT IDENTIFIER,
+ * accessLocation GeneralName }
+ *
+ * id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
+ * id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
+ * id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
+ * </pre>
+ */
+public class AuthorityInformationAccess
+ extends ASN1Object
+{
+ private AccessDescription[] descriptions;
+
+ public static AuthorityInformationAccess getInstance(
+ Object obj)
+ {
+ if (obj instanceof AuthorityInformationAccess)
+ {
+ return (AuthorityInformationAccess)obj;
+ }
+
+ if (obj != null)
+ {
+ return new AuthorityInformationAccess(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private AuthorityInformationAccess(
+ ASN1Sequence seq)
+ {
+ if (seq.size() < 1)
+ {
+ throw new IllegalArgumentException("sequence may not be empty");
+ }
+
+ descriptions = new AccessDescription[seq.size()];
+
+ for (int i = 0; i != seq.size(); i++)
+ {
+ descriptions[i] = AccessDescription.getInstance(seq.getObjectAt(i));
+ }
+ }
+
+ /**
+ * create an AuthorityInformationAccess with the oid and location provided.
+ */
+ public AuthorityInformationAccess(
+ ASN1ObjectIdentifier oid,
+ GeneralName location)
+ {
+ descriptions = new AccessDescription[1];
+
+ descriptions[0] = new AccessDescription(oid, location);
+ }
+
+
+ /**
+ *
+ * @return the access descriptions contained in this object.
+ */
+ public AccessDescription[] getAccessDescriptions()
+ {
+ return descriptions;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+
+ for (int i = 0; i != descriptions.length; i++)
+ {
+ vec.add(descriptions[i]);
+ }
+
+ return new DERSequence(vec);
+ }
+
+ public String toString()
+ {
+ return ("AuthorityInformationAccess: Oid(" + this.descriptions[0].getAccessMethod().getId() + ")");
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java b/core/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
new file mode 100644
index 00000000..c91fdc6c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
@@ -0,0 +1,232 @@
+package org.bouncycastle.asn1.x509;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+
+/**
+ * The AuthorityKeyIdentifier object.
+ * <pre>
+ * id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 }
+ *
+ * AuthorityKeyIdentifier ::= SEQUENCE {
+ * keyIdentifier [0] IMPLICIT KeyIdentifier OPTIONAL,
+ * authorityCertIssuer [1] IMPLICIT GeneralNames OPTIONAL,
+ * authorityCertSerialNumber [2] IMPLICIT CertificateSerialNumber OPTIONAL }
+ *
+ * KeyIdentifier ::= OCTET STRING
+ * </pre>
+ *
+ */
+public class AuthorityKeyIdentifier
+ extends ASN1Object
+{
+ ASN1OctetString keyidentifier=null;
+ GeneralNames certissuer=null;
+ ASN1Integer certserno=null;
+
+ public static AuthorityKeyIdentifier getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static AuthorityKeyIdentifier getInstance(
+ Object obj)
+ {
+ if (obj instanceof AuthorityKeyIdentifier)
+ {
+ return (AuthorityKeyIdentifier)obj;
+ }
+ if (obj != null)
+ {
+ return new AuthorityKeyIdentifier(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static AuthorityKeyIdentifier fromExtensions(Extensions extensions)
+ {
+ return AuthorityKeyIdentifier.getInstance(extensions.getExtensionParsedValue(Extension.authorityKeyIdentifier));
+ }
+
+ protected AuthorityKeyIdentifier(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ ASN1TaggedObject o = DERTaggedObject.getInstance(e.nextElement());
+
+ switch (o.getTagNo())
+ {
+ case 0:
+ this.keyidentifier = ASN1OctetString.getInstance(o, false);
+ break;
+ case 1:
+ this.certissuer = GeneralNames.getInstance(o, false);
+ break;
+ case 2:
+ this.certserno = ASN1Integer.getInstance(o, false);
+ break;
+ default:
+ throw new IllegalArgumentException("illegal tag");
+ }
+ }
+ }
+
+ /**
+ *
+ * Calulates the keyidentifier using a SHA1 hash over the BIT STRING
+ * from SubjectPublicKeyInfo as defined in RFC2459.
+ *
+ * Example of making a AuthorityKeyIdentifier:
+ * <pre>
+ * SubjectPublicKeyInfo apki = new SubjectPublicKeyInfo((ASN1Sequence)new ASN1InputStream(
+ * publicKey.getEncoded()).readObject());
+ * AuthorityKeyIdentifier aki = new AuthorityKeyIdentifier(apki);
+ * </pre>
+ *
+ **/
+ public AuthorityKeyIdentifier(
+ SubjectPublicKeyInfo spki)
+ {
+ Digest digest = new SHA1Digest();
+ byte[] resBuf = new byte[digest.getDigestSize()];
+
+ byte[] bytes = spki.getPublicKeyData().getBytes();
+ digest.update(bytes, 0, bytes.length);
+ digest.doFinal(resBuf, 0);
+ this.keyidentifier = new DEROctetString(resBuf);
+ }
+
+ /**
+ * create an AuthorityKeyIdentifier with the GeneralNames tag and
+ * the serial number provided as well.
+ */
+ public AuthorityKeyIdentifier(
+ SubjectPublicKeyInfo spki,
+ GeneralNames name,
+ BigInteger serialNumber)
+ {
+ Digest digest = new SHA1Digest();
+ byte[] resBuf = new byte[digest.getDigestSize()];
+
+ byte[] bytes = spki.getPublicKeyData().getBytes();
+ digest.update(bytes, 0, bytes.length);
+ digest.doFinal(resBuf, 0);
+
+ this.keyidentifier = new DEROctetString(resBuf);
+ this.certissuer = GeneralNames.getInstance(name.toASN1Primitive());
+ this.certserno = new ASN1Integer(serialNumber);
+ }
+
+ /**
+ * create an AuthorityKeyIdentifier with the GeneralNames tag and
+ * the serial number provided.
+ */
+ public AuthorityKeyIdentifier(
+ GeneralNames name,
+ BigInteger serialNumber)
+ {
+ this.keyidentifier = null;
+ this.certissuer = GeneralNames.getInstance(name.toASN1Primitive());
+ this.certserno = new ASN1Integer(serialNumber);
+ }
+
+ /**
+ * create an AuthorityKeyIdentifier with a precomputed key identifier
+ */
+ public AuthorityKeyIdentifier(
+ byte[] keyIdentifier)
+ {
+ this.keyidentifier = new DEROctetString(keyIdentifier);
+ this.certissuer = null;
+ this.certserno = null;
+ }
+
+ /**
+ * create an AuthorityKeyIdentifier with a precomputed key identifier
+ * and the GeneralNames tag and the serial number provided as well.
+ */
+ public AuthorityKeyIdentifier(
+ byte[] keyIdentifier,
+ GeneralNames name,
+ BigInteger serialNumber)
+ {
+ this.keyidentifier = new DEROctetString(keyIdentifier);
+ this.certissuer = GeneralNames.getInstance(name.toASN1Primitive());
+ this.certserno = new ASN1Integer(serialNumber);
+ }
+
+ public byte[] getKeyIdentifier()
+ {
+ if (keyidentifier != null)
+ {
+ return keyidentifier.getOctets();
+ }
+
+ return null;
+ }
+
+ public GeneralNames getAuthorityCertIssuer()
+ {
+ return certissuer;
+ }
+
+ public BigInteger getAuthorityCertSerialNumber()
+ {
+ if (certserno != null)
+ {
+ return certserno.getValue();
+ }
+
+ return null;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (keyidentifier != null)
+ {
+ v.add(new DERTaggedObject(false, 0, keyidentifier));
+ }
+
+ if (certissuer != null)
+ {
+ v.add(new DERTaggedObject(false, 1, certissuer));
+ }
+
+ if (certserno != null)
+ {
+ v.add(new DERTaggedObject(false, 2, certserno));
+ }
+
+
+ return new DERSequence(v);
+ }
+
+ public String toString()
+ {
+ return ("AuthorityKeyIdentifier: KeyID(" + this.keyidentifier.getOctets() + ")");
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/BasicConstraints.java b/core/src/main/java/org/bouncycastle/asn1/x509/BasicConstraints.java
new file mode 100644
index 00000000..4a16bd4b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/BasicConstraints.java
@@ -0,0 +1,164 @@
+package org.bouncycastle.asn1.x509;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1Boolean;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBoolean;
+import org.bouncycastle.asn1.DERSequence;
+
+public class BasicConstraints
+ extends ASN1Object
+{
+ ASN1Boolean cA = ASN1Boolean.getInstance(false);
+ ASN1Integer pathLenConstraint = null;
+
+ public static BasicConstraints getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static BasicConstraints getInstance(
+ Object obj)
+ {
+ if (obj instanceof BasicConstraints)
+ {
+ return (BasicConstraints)obj;
+ }
+ if (obj instanceof X509Extension)
+ {
+ return getInstance(X509Extension.convertValueToObject((X509Extension)obj));
+ }
+ if (obj != null)
+ {
+ return new BasicConstraints(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static BasicConstraints fromExtensions(Extensions extensions)
+ {
+ return BasicConstraints.getInstance(extensions.getExtensionParsedValue(Extension.basicConstraints));
+ }
+
+ private BasicConstraints(
+ ASN1Sequence seq)
+ {
+ if (seq.size() == 0)
+ {
+ this.cA = null;
+ this.pathLenConstraint = null;
+ }
+ else
+ {
+ if (seq.getObjectAt(0) instanceof DERBoolean)
+ {
+ this.cA = DERBoolean.getInstance(seq.getObjectAt(0));
+ }
+ else
+ {
+ this.cA = null;
+ this.pathLenConstraint = ASN1Integer.getInstance(seq.getObjectAt(0));
+ }
+ if (seq.size() > 1)
+ {
+ if (this.cA != null)
+ {
+ this.pathLenConstraint = ASN1Integer.getInstance(seq.getObjectAt(1));
+ }
+ else
+ {
+ throw new IllegalArgumentException("wrong sequence in constructor");
+ }
+ }
+ }
+ }
+
+ public BasicConstraints(
+ boolean cA)
+ {
+ if (cA)
+ {
+ this.cA = ASN1Boolean.getInstance(true);
+ }
+ else
+ {
+ this.cA = null;
+ }
+ this.pathLenConstraint = null;
+ }
+
+ /**
+ * create a cA=true object for the given path length constraint.
+ *
+ * @param pathLenConstraint
+ */
+ public BasicConstraints(
+ int pathLenConstraint)
+ {
+ this.cA = ASN1Boolean.getInstance(true);
+ this.pathLenConstraint = new ASN1Integer(pathLenConstraint);
+ }
+
+ public boolean isCA()
+ {
+ return (cA != null) && cA.isTrue();
+ }
+
+ public BigInteger getPathLenConstraint()
+ {
+ if (pathLenConstraint != null)
+ {
+ return pathLenConstraint.getValue();
+ }
+
+ return null;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * BasicConstraints := SEQUENCE {
+ * cA BOOLEAN DEFAULT FALSE,
+ * pathLenConstraint INTEGER (0..MAX) OPTIONAL
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (cA != null)
+ {
+ v.add(cA);
+ }
+
+ if (pathLenConstraint != null) // yes some people actually do this when cA is false...
+ {
+ v.add(pathLenConstraint);
+ }
+
+ return new DERSequence(v);
+ }
+
+ public String toString()
+ {
+ if (pathLenConstraint == null)
+ {
+ if (cA == null)
+ {
+ return "BasicConstraints: isCa(false)";
+ }
+ return "BasicConstraints: isCa(" + this.isCA() + ")";
+ }
+ return "BasicConstraints: isCa(" + this.isCA() + "), pathLenConstraint = " + pathLenConstraint.getValue();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/CRLDistPoint.java b/core/src/main/java/org/bouncycastle/asn1/x509/CRLDistPoint.java
new file mode 100644
index 00000000..1ee6aa5a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/CRLDistPoint.java
@@ -0,0 +1,100 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+public class CRLDistPoint
+ extends ASN1Object
+{
+ ASN1Sequence seq = null;
+
+ public static CRLDistPoint getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static CRLDistPoint getInstance(
+ Object obj)
+ {
+ if (obj instanceof CRLDistPoint)
+ {
+ return (CRLDistPoint)obj;
+ }
+ else if (obj != null)
+ {
+ return new CRLDistPoint(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private CRLDistPoint(
+ ASN1Sequence seq)
+ {
+ this.seq = seq;
+ }
+
+ public CRLDistPoint(
+ DistributionPoint[] points)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ for (int i = 0; i != points.length; i++)
+ {
+ v.add(points[i]);
+ }
+
+ seq = new DERSequence(v);
+ }
+
+ /**
+ * Return the distribution points making up the sequence.
+ *
+ * @return DistributionPoint[]
+ */
+ public DistributionPoint[] getDistributionPoints()
+ {
+ DistributionPoint[] dp = new DistributionPoint[seq.size()];
+
+ for (int i = 0; i != seq.size(); i++)
+ {
+ dp[i] = DistributionPoint.getInstance(seq.getObjectAt(i));
+ }
+
+ return dp;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * CRLDistPoint ::= SEQUENCE SIZE {1..MAX} OF DistributionPoint
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return seq;
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ String sep = System.getProperty("line.separator");
+
+ buf.append("CRLDistPoint:");
+ buf.append(sep);
+ DistributionPoint dp[] = getDistributionPoints();
+ for (int i = 0; i != dp.length; i++)
+ {
+ buf.append(" ");
+ buf.append(dp[i]);
+ buf.append(sep);
+ }
+ return buf.toString();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/CRLNumber.java b/core/src/main/java/org/bouncycastle/asn1/x509/CRLNumber.java
new file mode 100644
index 00000000..95425ba3
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/CRLNumber.java
@@ -0,0 +1,54 @@
+package org.bouncycastle.asn1.x509;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+
+/**
+ * The CRLNumber object.
+ * <pre>
+ * CRLNumber::= INTEGER(0..MAX)
+ * </pre>
+ */
+public class CRLNumber
+ extends ASN1Object
+{
+ private BigInteger number;
+
+ public CRLNumber(
+ BigInteger number)
+ {
+ this.number = number;
+ }
+
+ public BigInteger getCRLNumber()
+ {
+ return number;
+ }
+
+ public String toString()
+ {
+ return "CRLNumber: " + getCRLNumber();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return new ASN1Integer(number);
+ }
+
+ public static CRLNumber getInstance(Object o)
+ {
+ if (o instanceof CRLNumber)
+ {
+ return (CRLNumber)o;
+ }
+ else if (o != null)
+ {
+ return new CRLNumber(ASN1Integer.getInstance(o).getValue());
+ }
+
+ return null;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/CRLReason.java b/core/src/main/java/org/bouncycastle/asn1/x509/CRLReason.java
new file mode 100644
index 00000000..ecc68721
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/CRLReason.java
@@ -0,0 +1,151 @@
+package org.bouncycastle.asn1.x509;
+
+import java.math.BigInteger;
+import java.util.Hashtable;
+
+import org.bouncycastle.asn1.ASN1Enumerated;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.util.Integers;
+
+/**
+ * The CRLReason enumeration.
+ * <pre>
+ * CRLReason ::= ENUMERATED {
+ * unspecified (0),
+ * keyCompromise (1),
+ * cACompromise (2),
+ * affiliationChanged (3),
+ * superseded (4),
+ * cessationOfOperation (5),
+ * certificateHold (6),
+ * removeFromCRL (8),
+ * privilegeWithdrawn (9),
+ * aACompromise (10)
+ * }
+ * </pre>
+ */
+public class CRLReason
+ extends ASN1Object
+{
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int UNSPECIFIED = 0;
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int KEY_COMPROMISE = 1;
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int CA_COMPROMISE = 2;
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int AFFILIATION_CHANGED = 3;
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int SUPERSEDED = 4;
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int CESSATION_OF_OPERATION = 5;
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int CERTIFICATE_HOLD = 6;
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int REMOVE_FROM_CRL = 8;
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int PRIVILEGE_WITHDRAWN = 9;
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int AA_COMPROMISE = 10;
+
+ public static final int unspecified = 0;
+ public static final int keyCompromise = 1;
+ public static final int cACompromise = 2;
+ public static final int affiliationChanged = 3;
+ public static final int superseded = 4;
+ public static final int cessationOfOperation = 5;
+ public static final int certificateHold = 6;
+ // 7 -> unknown
+ public static final int removeFromCRL = 8;
+ public static final int privilegeWithdrawn = 9;
+ public static final int aACompromise = 10;
+
+ private static final String[] reasonString =
+ {
+ "unspecified", "keyCompromise", "cACompromise", "affiliationChanged",
+ "superseded", "cessationOfOperation", "certificateHold", "unknown",
+ "removeFromCRL", "privilegeWithdrawn", "aACompromise"
+ };
+
+ private static final Hashtable table = new Hashtable();
+
+ private ASN1Enumerated value;
+
+ public static CRLReason getInstance(Object o)
+ {
+ if (o instanceof CRLReason)
+ {
+ return (CRLReason)o;
+ }
+ else if (o != null)
+ {
+ return lookup(ASN1Enumerated.getInstance(o).getValue().intValue());
+ }
+
+ return null;
+ }
+
+ private CRLReason(
+ int reason)
+ {
+ value = new ASN1Enumerated(reason);
+ }
+
+ public String toString()
+ {
+ String str;
+ int reason = getValue().intValue();
+ if (reason < 0 || reason > 10)
+ {
+ str = "invalid";
+ }
+ else
+ {
+ str = reasonString[reason];
+ }
+ return "CRLReason: " + str;
+ }
+
+ public BigInteger getValue()
+ {
+ return value.getValue();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return value;
+ }
+
+ public static CRLReason lookup(int value)
+ {
+ Integer idx = Integers.valueOf(value);
+
+ if (!table.containsKey(idx))
+ {
+ table.put(idx, new CRLReason(value));
+ }
+
+ return (CRLReason)table.get(idx);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/CertPolicyId.java b/core/src/main/java/org/bouncycastle/asn1/x509/CertPolicyId.java
new file mode 100644
index 00000000..ab1e5a22
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/CertPolicyId.java
@@ -0,0 +1,57 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+
+
+/**
+ * CertPolicyId, used in the CertificatePolicies and PolicyMappings
+ * X509V3 Extensions.
+ *
+ * <pre>
+ * CertPolicyId ::= OBJECT IDENTIFIER
+ * </pre>
+ */
+/**
+ * CertPolicyId, used in the CertificatePolicies and PolicyMappings
+ * X509V3 Extensions.
+ *
+ * <pre>
+ * CertPolicyId ::= OBJECT IDENTIFIER
+ * </pre>
+ */
+public class CertPolicyId
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier id;
+
+ private CertPolicyId(ASN1ObjectIdentifier id)
+ {
+ this.id = id;
+ }
+
+ public static CertPolicyId getInstance(Object o)
+ {
+ if (o instanceof CertPolicyId)
+ {
+ return (CertPolicyId)o;
+ }
+ else if (o != null)
+ {
+ return new CertPolicyId(ASN1ObjectIdentifier.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public String getId()
+ {
+ return id.getId();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return id;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/Certificate.java b/core/src/main/java/org/bouncycastle/asn1/x509/Certificate.java
new file mode 100644
index 00000000..4ca14d40
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/Certificate.java
@@ -0,0 +1,131 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.x500.X500Name;
+
+/**
+ * an X509Certificate structure.
+ * <pre>
+ * Certificate ::= SEQUENCE {
+ * tbsCertificate TBSCertificate,
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signature BIT STRING
+ * }
+ * </pre>
+ */
+public class Certificate
+ extends ASN1Object
+{
+ ASN1Sequence seq;
+ TBSCertificate tbsCert;
+ AlgorithmIdentifier sigAlgId;
+ DERBitString sig;
+
+ public static Certificate getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static Certificate getInstance(
+ Object obj)
+ {
+ if (obj instanceof Certificate)
+ {
+ return (Certificate)obj;
+ }
+ else if (obj != null)
+ {
+ return new Certificate(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private Certificate(
+ ASN1Sequence seq)
+ {
+ this.seq = seq;
+
+ //
+ // correct x509 certficate
+ //
+ if (seq.size() == 3)
+ {
+ tbsCert = TBSCertificate.getInstance(seq.getObjectAt(0));
+ sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
+
+ sig = DERBitString.getInstance(seq.getObjectAt(2));
+ }
+ else
+ {
+ throw new IllegalArgumentException("sequence wrong size for a certificate");
+ }
+ }
+
+ public TBSCertificate getTBSCertificate()
+ {
+ return tbsCert;
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return tbsCert.getVersion();
+ }
+
+ public int getVersionNumber()
+ {
+ return tbsCert.getVersionNumber();
+ }
+
+ public ASN1Integer getSerialNumber()
+ {
+ return tbsCert.getSerialNumber();
+ }
+
+ public X500Name getIssuer()
+ {
+ return tbsCert.getIssuer();
+ }
+
+ public Time getStartDate()
+ {
+ return tbsCert.getStartDate();
+ }
+
+ public Time getEndDate()
+ {
+ return tbsCert.getEndDate();
+ }
+
+ public X500Name getSubject()
+ {
+ return tbsCert.getSubject();
+ }
+
+ public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
+ {
+ return tbsCert.getSubjectPublicKeyInfo();
+ }
+
+ public AlgorithmIdentifier getSignatureAlgorithm()
+ {
+ return sigAlgId;
+ }
+
+ public DERBitString getSignature()
+ {
+ return sig;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return seq;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java b/core/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java
new file mode 100644
index 00000000..91a37ade
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java
@@ -0,0 +1,127 @@
+
+package org.bouncycastle.asn1.x509;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x500.X500Name;
+
+/**
+ * PKIX RFC-2459
+ *
+ * The X.509 v2 CRL syntax is as follows. For signature calculation,
+ * the data that is to be signed is ASN.1 DER encoded.
+ *
+ * <pre>
+ * CertificateList ::= SEQUENCE {
+ * tbsCertList TBSCertList,
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signatureValue BIT STRING }
+ * </pre>
+ */
+public class CertificateList
+ extends ASN1Object
+{
+ TBSCertList tbsCertList;
+ AlgorithmIdentifier sigAlgId;
+ DERBitString sig;
+
+ public static CertificateList getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static CertificateList getInstance(
+ Object obj)
+ {
+ if (obj instanceof CertificateList)
+ {
+ return (CertificateList)obj;
+ }
+ else if (obj != null)
+ {
+ return new CertificateList(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public CertificateList(
+ ASN1Sequence seq)
+ {
+ if (seq.size() == 3)
+ {
+ tbsCertList = TBSCertList.getInstance(seq.getObjectAt(0));
+ sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
+ sig = DERBitString.getInstance(seq.getObjectAt(2));
+ }
+ else
+ {
+ throw new IllegalArgumentException("sequence wrong size for CertificateList");
+ }
+ }
+
+ public TBSCertList getTBSCertList()
+ {
+ return tbsCertList;
+ }
+
+ public TBSCertList.CRLEntry[] getRevokedCertificates()
+ {
+ return tbsCertList.getRevokedCertificates();
+ }
+
+ public Enumeration getRevokedCertificateEnumeration()
+ {
+ return tbsCertList.getRevokedCertificateEnumeration();
+ }
+
+ public AlgorithmIdentifier getSignatureAlgorithm()
+ {
+ return sigAlgId;
+ }
+
+ public DERBitString getSignature()
+ {
+ return sig;
+ }
+
+ public int getVersionNumber()
+ {
+ return tbsCertList.getVersionNumber();
+ }
+
+ public X500Name getIssuer()
+ {
+ return tbsCertList.getIssuer();
+ }
+
+ public Time getThisUpdate()
+ {
+ return tbsCertList.getThisUpdate();
+ }
+
+ public Time getNextUpdate()
+ {
+ return tbsCertList.getNextUpdate();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(tbsCertList);
+ v.add(sigAlgId);
+ v.add(sig);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/CertificatePair.java b/core/src/main/java/org/bouncycastle/asn1/x509/CertificatePair.java
new file mode 100644
index 00000000..cab44d1b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/CertificatePair.java
@@ -0,0 +1,169 @@
+package org.bouncycastle.asn1.x509;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * This class helps to support crossCerfificatePairs in a LDAP directory
+ * according RFC 2587
+ *
+ * <pre>
+ * crossCertificatePairATTRIBUTE::={
+ * WITH SYNTAX CertificatePair
+ * EQUALITY MATCHING RULE certificatePairExactMatch
+ * ID joint-iso-ccitt(2) ds(5) attributeType(4) crossCertificatePair(40)}
+ * </pre>
+ *
+ * <blockquote> The forward elements of the crossCertificatePair attribute of a
+ * CA's directory entry shall be used to store all, except self-issued
+ * certificates issued to this CA. Optionally, the reverse elements of the
+ * crossCertificatePair attribute, of a CA's directory entry may contain a
+ * subset of certificates issued by this CA to other CAs. When both the forward
+ * and the reverse elements are present in a single attribute value, issuer name
+ * in one certificate shall match the subject name in the other and vice versa,
+ * and the subject public key in one certificate shall be capable of verifying
+ * the digital signature on the other certificate and vice versa.
+ *
+ * When a reverse element is present, the forward element value and the reverse
+ * element value need not be stored in the same attribute value; in other words,
+ * they can be stored in either a single attribute value or two attribute
+ * values. </blockquote>
+ *
+ * <pre>
+ * CertificatePair ::= SEQUENCE {
+ * forward [0] Certificate OPTIONAL,
+ * reverse [1] Certificate OPTIONAL,
+ * -- at least one of the pair shall be present -- }
+ * </pre>
+ */
+public class CertificatePair
+ extends ASN1Object
+{
+ private Certificate forward;
+
+ private Certificate reverse;
+
+ public static CertificatePair getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof CertificatePair)
+ {
+ return (CertificatePair)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new CertificatePair((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: "
+ + obj.getClass().getName());
+ }
+
+ /**
+ * Constructor from ASN1Sequence.
+ * <p/>
+ * The sequence is of type CertificatePair:
+ * <p/>
+ * <pre>
+ * CertificatePair ::= SEQUENCE {
+ * forward [0] Certificate OPTIONAL,
+ * reverse [1] Certificate OPTIONAL,
+ * -- at least one of the pair shall be present -- }
+ * </pre>
+ *
+ * @param seq The ASN.1 sequence.
+ */
+ private CertificatePair(ASN1Sequence seq)
+ {
+ if (seq.size() != 1 && seq.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ ASN1TaggedObject o = ASN1TaggedObject.getInstance(e.nextElement());
+ if (o.getTagNo() == 0)
+ {
+ forward = Certificate.getInstance(o, true);
+ }
+ else if (o.getTagNo() == 1)
+ {
+ reverse = Certificate.getInstance(o, true);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Bad tag number: "
+ + o.getTagNo());
+ }
+ }
+ }
+
+ /**
+ * Constructor from a given details.
+ *
+ * @param forward Certificates issued to this CA.
+ * @param reverse Certificates issued by this CA to other CAs.
+ */
+ public CertificatePair(Certificate forward, Certificate reverse)
+ {
+ this.forward = forward;
+ this.reverse = reverse;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <p/>
+ * Returns:
+ * <p/>
+ * <pre>
+ * CertificatePair ::= SEQUENCE {
+ * forward [0] Certificate OPTIONAL,
+ * reverse [1] Certificate OPTIONAL,
+ * -- at least one of the pair shall be present -- }
+ * </pre>
+ *
+ * @return a ASN1Primitive
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+
+ if (forward != null)
+ {
+ vec.add(new DERTaggedObject(0, forward));
+ }
+ if (reverse != null)
+ {
+ vec.add(new DERTaggedObject(1, reverse));
+ }
+
+ return new DERSequence(vec);
+ }
+
+ /**
+ * @return Returns the forward.
+ */
+ public Certificate getForward()
+ {
+ return forward;
+ }
+
+ /**
+ * @return Returns the reverse.
+ */
+ public Certificate getReverse()
+ {
+ return reverse;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java b/core/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java
new file mode 100644
index 00000000..e42cefa0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java
@@ -0,0 +1,99 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+public class CertificatePolicies
+ extends ASN1Object
+{
+ private final PolicyInformation[] policyInformation;
+
+ public static CertificatePolicies getInstance(
+ Object obj)
+ {
+ if (obj instanceof CertificatePolicies)
+ {
+ return (CertificatePolicies)obj;
+ }
+
+ if (obj != null)
+ {
+ return new CertificatePolicies(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static CertificatePolicies getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * Construct a CertificatePolicies object containing one PolicyInformation.
+ *
+ * @param name the name to be contained.
+ */
+ public CertificatePolicies(
+ PolicyInformation name)
+ {
+ this.policyInformation = new PolicyInformation[] { name };
+ }
+
+ public CertificatePolicies(
+ PolicyInformation[] policyInformation)
+ {
+ this.policyInformation = policyInformation;
+ }
+
+ private CertificatePolicies(
+ ASN1Sequence seq)
+ {
+ this.policyInformation = new PolicyInformation[seq.size()];
+
+ for (int i = 0; i != seq.size(); i++)
+ {
+ policyInformation[i] = PolicyInformation.getInstance(seq.getObjectAt(i));
+ }
+ }
+
+ public PolicyInformation[] getPolicyInformation()
+ {
+ PolicyInformation[] tmp = new PolicyInformation[policyInformation.length];
+
+ System.arraycopy(policyInformation, 0, tmp, 0, policyInformation.length);
+
+ return tmp;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * CertificatePolicies ::= SEQUENCE SIZE {1..MAX} OF PolicyInformation
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return new DERSequence(policyInformation);
+ }
+
+ public String toString()
+ {
+ String p = null;
+ for (int i = 0; i < policyInformation.length; i++)
+ {
+ if (p != null)
+ {
+ p += ", ";
+ }
+ p += policyInformation[i];
+ }
+
+ return "CertificatePolicies: " + p;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/DSAParameter.java b/core/src/main/java/org/bouncycastle/asn1/x509/DSAParameter.java
new file mode 100644
index 00000000..056798ca
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/DSAParameter.java
@@ -0,0 +1,92 @@
+package org.bouncycastle.asn1.x509;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+public class DSAParameter
+ extends ASN1Object
+{
+ ASN1Integer p, q, g;
+
+ public static DSAParameter getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static DSAParameter getInstance(
+ Object obj)
+ {
+ if (obj instanceof DSAParameter)
+ {
+ return (DSAParameter)obj;
+ }
+
+ if(obj != null)
+ {
+ return new DSAParameter(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public DSAParameter(
+ BigInteger p,
+ BigInteger q,
+ BigInteger g)
+ {
+ this.p = new ASN1Integer(p);
+ this.q = new ASN1Integer(q);
+ this.g = new ASN1Integer(g);
+ }
+
+ private DSAParameter(
+ ASN1Sequence seq)
+ {
+ if (seq.size() != 3)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ Enumeration e = seq.getObjects();
+
+ p = ASN1Integer.getInstance(e.nextElement());
+ q = ASN1Integer.getInstance(e.nextElement());
+ g = ASN1Integer.getInstance(e.nextElement());
+ }
+
+ public BigInteger getP()
+ {
+ return p.getPositiveValue();
+ }
+
+ public BigInteger getQ()
+ {
+ return q.getPositiveValue();
+ }
+
+ public BigInteger getG()
+ {
+ return g.getPositiveValue();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(p);
+ v.add(q);
+ v.add(g);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/DigestInfo.java b/core/src/main/java/org/bouncycastle/asn1/x509/DigestInfo.java
new file mode 100644
index 00000000..fd17f1b5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/DigestInfo.java
@@ -0,0 +1,86 @@
+package org.bouncycastle.asn1.x509;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * The DigestInfo object.
+ * <pre>
+ * DigestInfo::=SEQUENCE{
+ * digestAlgorithm AlgorithmIdentifier,
+ * digest OCTET STRING }
+ * </pre>
+ */
+public class DigestInfo
+ extends ASN1Object
+{
+ private byte[] digest;
+ private AlgorithmIdentifier algId;
+
+ public static DigestInfo getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static DigestInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof DigestInfo)
+ {
+ return (DigestInfo)obj;
+ }
+ else if (obj != null)
+ {
+ return new DigestInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public DigestInfo(
+ AlgorithmIdentifier algId,
+ byte[] digest)
+ {
+ this.digest = digest;
+ this.algId = algId;
+ }
+
+ public DigestInfo(
+ ASN1Sequence obj)
+ {
+ Enumeration e = obj.getObjects();
+
+ algId = AlgorithmIdentifier.getInstance(e.nextElement());
+ digest = ASN1OctetString.getInstance(e.nextElement()).getOctets();
+ }
+
+ public AlgorithmIdentifier getAlgorithmId()
+ {
+ return algId;
+ }
+
+ public byte[] getDigest()
+ {
+ return digest;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(algId);
+ v.add(new DEROctetString(digest));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/DisplayText.java b/core/src/main/java/org/bouncycastle/asn1/x509/DisplayText.java
new file mode 100644
index 00000000..acebcbe7
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/DisplayText.java
@@ -0,0 +1,165 @@
+
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1String;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBMPString;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.DERUTF8String;
+import org.bouncycastle.asn1.DERVisibleString;
+
+/**
+ * <code>DisplayText</code> class, used in
+ * <code>CertificatePolicies</code> X509 V3 extensions (in policy qualifiers).
+ *
+ * <p>It stores a string in a chosen encoding.
+ * <pre>
+ * DisplayText ::= CHOICE {
+ * ia5String IA5String (SIZE (1..200)),
+ * visibleString VisibleString (SIZE (1..200)),
+ * bmpString BMPString (SIZE (1..200)),
+ * utf8String UTF8String (SIZE (1..200)) }
+ * </pre>
+ * @see PolicyQualifierInfo
+ * @see PolicyInformation
+ */
+public class DisplayText
+ extends ASN1Object
+ implements ASN1Choice
+{
+ /**
+ * Constant corresponding to ia5String encoding.
+ *
+ */
+ public static final int CONTENT_TYPE_IA5STRING = 0;
+ /**
+ * Constant corresponding to bmpString encoding.
+ *
+ */
+ public static final int CONTENT_TYPE_BMPSTRING = 1;
+ /**
+ * Constant corresponding to utf8String encoding.
+ *
+ */
+ public static final int CONTENT_TYPE_UTF8STRING = 2;
+ /**
+ * Constant corresponding to visibleString encoding.
+ *
+ */
+ public static final int CONTENT_TYPE_VISIBLESTRING = 3;
+
+ /**
+ * Describe constant <code>DISPLAY_TEXT_MAXIMUM_SIZE</code> here.
+ *
+ */
+ public static final int DISPLAY_TEXT_MAXIMUM_SIZE = 200;
+
+ int contentType;
+ ASN1String contents;
+
+ /**
+ * Creates a new <code>DisplayText</code> instance.
+ *
+ * @param type the desired encoding type for the text.
+ * @param text the text to store. Strings longer than 200
+ * characters are truncated.
+ */
+ public DisplayText(int type, String text)
+ {
+ if (text.length() > DISPLAY_TEXT_MAXIMUM_SIZE)
+ {
+ // RFC3280 limits these strings to 200 chars
+ // truncate the string
+ text = text.substring (0, DISPLAY_TEXT_MAXIMUM_SIZE);
+ }
+
+ contentType = type;
+ switch (type)
+ {
+ case CONTENT_TYPE_IA5STRING:
+ contents = new DERIA5String(text);
+ break;
+ case CONTENT_TYPE_UTF8STRING:
+ contents = new DERUTF8String(text);
+ break;
+ case CONTENT_TYPE_VISIBLESTRING:
+ contents = new DERVisibleString(text);
+ break;
+ case CONTENT_TYPE_BMPSTRING:
+ contents = new DERBMPString(text);
+ break;
+ default:
+ contents = new DERUTF8String(text);
+ break;
+ }
+ }
+
+ /**
+ * Creates a new <code>DisplayText</code> instance.
+ *
+ * @param text the text to encapsulate. Strings longer than 200
+ * characters are truncated.
+ */
+ public DisplayText(String text)
+ {
+ // by default use UTF8String
+ if (text.length() > DISPLAY_TEXT_MAXIMUM_SIZE)
+ {
+ text = text.substring(0, DISPLAY_TEXT_MAXIMUM_SIZE);
+ }
+
+ contentType = CONTENT_TYPE_UTF8STRING;
+ contents = new DERUTF8String(text);
+ }
+
+ /**
+ * Creates a new <code>DisplayText</code> instance.
+ * <p>Useful when reading back a <code>DisplayText</code> class
+ * from it's ASN1Encodable/DEREncodable form.
+ *
+ * @param de a <code>DEREncodable</code> instance.
+ */
+ private DisplayText(ASN1String de)
+ {
+ contents = de;
+ }
+
+ public static DisplayText getInstance(Object obj)
+ {
+ if (obj instanceof ASN1String)
+ {
+ return new DisplayText((ASN1String)obj);
+ }
+ else if (obj == null || obj instanceof DisplayText)
+ {
+ return (DisplayText)obj;
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ public static DisplayText getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject()); // must be explicitly tagged
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return (ASN1Primitive)contents;
+ }
+
+ /**
+ * Returns the stored <code>String</code> object.
+ *
+ * @return the stored text as a <code>String</code>.
+ */
+ public String getString()
+ {
+ return contents.getString();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/DistributionPoint.java b/core/src/main/java/org/bouncycastle/asn1/x509/DistributionPoint.java
new file mode 100644
index 00000000..ab73dfbb
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/DistributionPoint.java
@@ -0,0 +1,158 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * The DistributionPoint object.
+ * <pre>
+ * DistributionPoint ::= SEQUENCE {
+ * distributionPoint [0] DistributionPointName OPTIONAL,
+ * reasons [1] ReasonFlags OPTIONAL,
+ * cRLIssuer [2] GeneralNames OPTIONAL
+ * }
+ * </pre>
+ */
+public class DistributionPoint
+ extends ASN1Object
+{
+ DistributionPointName distributionPoint;
+ ReasonFlags reasons;
+ GeneralNames cRLIssuer;
+
+ public static DistributionPoint getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static DistributionPoint getInstance(
+ Object obj)
+ {
+ if(obj == null || obj instanceof DistributionPoint)
+ {
+ return (DistributionPoint)obj;
+ }
+
+ if(obj instanceof ASN1Sequence)
+ {
+ return new DistributionPoint((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid DistributionPoint: " + obj.getClass().getName());
+ }
+
+ public DistributionPoint(
+ ASN1Sequence seq)
+ {
+ for (int i = 0; i != seq.size(); i++)
+ {
+ ASN1TaggedObject t = ASN1TaggedObject.getInstance(seq.getObjectAt(i));
+ switch (t.getTagNo())
+ {
+ case 0:
+ distributionPoint = DistributionPointName.getInstance(t, true);
+ break;
+ case 1:
+ reasons = new ReasonFlags(DERBitString.getInstance(t, false));
+ break;
+ case 2:
+ cRLIssuer = GeneralNames.getInstance(t, false);
+ }
+ }
+ }
+
+ public DistributionPoint(
+ DistributionPointName distributionPoint,
+ ReasonFlags reasons,
+ GeneralNames cRLIssuer)
+ {
+ this.distributionPoint = distributionPoint;
+ this.reasons = reasons;
+ this.cRLIssuer = cRLIssuer;
+ }
+
+ public DistributionPointName getDistributionPoint()
+ {
+ return distributionPoint;
+ }
+
+ public ReasonFlags getReasons()
+ {
+ return reasons;
+ }
+
+ public GeneralNames getCRLIssuer()
+ {
+ return cRLIssuer;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (distributionPoint != null)
+ {
+ //
+ // as this is a CHOICE it must be explicitly tagged
+ //
+ v.add(new DERTaggedObject(0, distributionPoint));
+ }
+
+ if (reasons != null)
+ {
+ v.add(new DERTaggedObject(false, 1, reasons));
+ }
+
+ if (cRLIssuer != null)
+ {
+ v.add(new DERTaggedObject(false, 2, cRLIssuer));
+ }
+
+ return new DERSequence(v);
+ }
+
+ public String toString()
+ {
+ String sep = System.getProperty("line.separator");
+ StringBuffer buf = new StringBuffer();
+ buf.append("DistributionPoint: [");
+ buf.append(sep);
+ if (distributionPoint != null)
+ {
+ appendObject(buf, sep, "distributionPoint", distributionPoint.toString());
+ }
+ if (reasons != null)
+ {
+ appendObject(buf, sep, "reasons", reasons.toString());
+ }
+ if (cRLIssuer != null)
+ {
+ appendObject(buf, sep, "cRLIssuer", cRLIssuer.toString());
+ }
+ buf.append("]");
+ buf.append(sep);
+ return buf.toString();
+ }
+
+ private void appendObject(StringBuffer buf, String sep, String name, String value)
+ {
+ String indent = " ";
+
+ buf.append(indent);
+ buf.append(name);
+ buf.append(":");
+ buf.append(sep);
+ buf.append(indent);
+ buf.append(indent);
+ buf.append(value);
+ buf.append(sep);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/DistributionPointName.java b/core/src/main/java/org/bouncycastle/asn1/x509/DistributionPointName.java
new file mode 100644
index 00000000..ee06efdc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/DistributionPointName.java
@@ -0,0 +1,138 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * The DistributionPointName object.
+ * <pre>
+ * DistributionPointName ::= CHOICE {
+ * fullName [0] GeneralNames,
+ * nameRelativeToCRLIssuer [1] RDN
+ * }
+ * </pre>
+ */
+public class DistributionPointName
+ extends ASN1Object
+ implements ASN1Choice
+{
+ ASN1Encodable name;
+ int type;
+
+ public static final int FULL_NAME = 0;
+ public static final int NAME_RELATIVE_TO_CRL_ISSUER = 1;
+
+ public static DistributionPointName getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1TaggedObject.getInstance(obj, true));
+ }
+
+ public static DistributionPointName getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DistributionPointName)
+ {
+ return (DistributionPointName)obj;
+ }
+ else if (obj instanceof ASN1TaggedObject)
+ {
+ return new DistributionPointName((ASN1TaggedObject)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
+ }
+
+ public DistributionPointName(
+ int type,
+ ASN1Encodable name)
+ {
+ this.type = type;
+ this.name = name;
+ }
+
+ public DistributionPointName(
+ GeneralNames name)
+ {
+ this(FULL_NAME, name);
+ }
+
+ /**
+ * Return the tag number applying to the underlying choice.
+ *
+ * @return the tag number for this point name.
+ */
+ public int getType()
+ {
+ return this.type;
+ }
+
+ /**
+ * Return the tagged object inside the distribution point name.
+ *
+ * @return the underlying choice item.
+ */
+ public ASN1Encodable getName()
+ {
+ return (ASN1Encodable)name;
+ }
+
+ public DistributionPointName(
+ ASN1TaggedObject obj)
+ {
+ this.type = obj.getTagNo();
+
+ if (type == 0)
+ {
+ this.name = GeneralNames.getInstance(obj, false);
+ }
+ else
+ {
+ this.name = ASN1Set.getInstance(obj, false);
+ }
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return new DERTaggedObject(false, type, name);
+ }
+
+ public String toString()
+ {
+ String sep = System.getProperty("line.separator");
+ StringBuffer buf = new StringBuffer();
+ buf.append("DistributionPointName: [");
+ buf.append(sep);
+ if (type == FULL_NAME)
+ {
+ appendObject(buf, sep, "fullName", name.toString());
+ }
+ else
+ {
+ appendObject(buf, sep, "nameRelativeToCRLIssuer", name.toString());
+ }
+ buf.append("]");
+ buf.append(sep);
+ return buf.toString();
+ }
+
+ private void appendObject(StringBuffer buf, String sep, String name, String value)
+ {
+ String indent = " ";
+
+ buf.append(indent);
+ buf.append(name);
+ buf.append(":");
+ buf.append(sep);
+ buf.append(indent);
+ buf.append(indent);
+ buf.append(value);
+ buf.append(sep);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java b/core/src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java
new file mode 100644
index 00000000..dcc1b1fa
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java
@@ -0,0 +1,147 @@
+package org.bouncycastle.asn1.x509;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * The extendedKeyUsage object.
+ * <pre>
+ * extendedKeyUsage ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
+ * </pre>
+ */
+public class ExtendedKeyUsage
+ extends ASN1Object
+{
+ Hashtable usageTable = new Hashtable();
+ ASN1Sequence seq;
+
+ public static ExtendedKeyUsage getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static ExtendedKeyUsage getInstance(
+ Object obj)
+ {
+ if (obj instanceof ExtendedKeyUsage)
+ {
+ return (ExtendedKeyUsage)obj;
+ }
+ else if (obj != null)
+ {
+ return new ExtendedKeyUsage(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static ExtendedKeyUsage fromExtensions(Extensions extensions)
+ {
+ return ExtendedKeyUsage.getInstance(extensions.getExtensionParsedValue(Extension.extendedKeyUsage));
+ }
+
+ public ExtendedKeyUsage(
+ KeyPurposeId usage)
+ {
+ this.seq = new DERSequence(usage);
+
+ this.usageTable.put(usage, usage);
+ }
+
+ private ExtendedKeyUsage(
+ ASN1Sequence seq)
+ {
+ this.seq = seq;
+
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ ASN1Encodable o = (ASN1Encodable)e.nextElement();
+ if (!(o.toASN1Primitive() instanceof ASN1ObjectIdentifier))
+ {
+ throw new IllegalArgumentException("Only ASN1ObjectIdentifiers allowed in ExtendedKeyUsage.");
+ }
+ this.usageTable.put(o, o);
+ }
+ }
+
+ public ExtendedKeyUsage(
+ KeyPurposeId[] usages)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ for (int i = 0; i != usages.length; i++)
+ {
+ v.add(usages[i]);
+ this.usageTable.put(usages[i], usages[i]);
+ }
+
+ this.seq = new DERSequence(v);
+ }
+
+ /**
+ * @deprecated use KeyPurposeId[] constructor.
+ */
+ public ExtendedKeyUsage(
+ Vector usages)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ Enumeration e = usages.elements();
+
+ while (e.hasMoreElements())
+ {
+ ASN1Primitive o = (ASN1Primitive)e.nextElement();
+
+ v.add(o);
+ this.usageTable.put(o, o);
+ }
+
+ this.seq = new DERSequence(v);
+ }
+
+ public boolean hasKeyPurposeId(
+ KeyPurposeId keyPurposeId)
+ {
+ return (usageTable.get(keyPurposeId) != null);
+ }
+
+ /**
+ * Returns all extended key usages.
+ * The returned vector contains DERObjectIdentifiers.
+ * @return An array with all key purposes.
+ */
+ public KeyPurposeId[] getUsages()
+ {
+ KeyPurposeId[] temp = new KeyPurposeId[seq.size()];
+
+ int i = 0;
+ for (Enumeration it = seq.getObjects(); it.hasMoreElements();)
+ {
+ temp[i++] = KeyPurposeId.getInstance(it.nextElement());
+ }
+ return temp;
+ }
+
+ public int size()
+ {
+ return usageTable.size();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return seq;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/Extension.java b/core/src/main/java/org/bouncycastle/asn1/x509/Extension.java
new file mode 100644
index 00000000..4d566b18
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/Extension.java
@@ -0,0 +1,321 @@
+package org.bouncycastle.asn1.x509;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Boolean;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * an object for the elements in the X.509 V3 extension block.
+ */
+public class Extension
+ extends ASN1Object
+{
+ /**
+ * Subject Directory Attributes
+ */
+ public static final ASN1ObjectIdentifier subjectDirectoryAttributes = new ASN1ObjectIdentifier("2.5.29.9");
+
+ /**
+ * Subject Key Identifier
+ */
+ public static final ASN1ObjectIdentifier subjectKeyIdentifier = new ASN1ObjectIdentifier("2.5.29.14");
+
+ /**
+ * Key Usage
+ */
+ public static final ASN1ObjectIdentifier keyUsage = new ASN1ObjectIdentifier("2.5.29.15");
+
+ /**
+ * Private Key Usage Period
+ */
+ public static final ASN1ObjectIdentifier privateKeyUsagePeriod = new ASN1ObjectIdentifier("2.5.29.16");
+
+ /**
+ * Subject Alternative Name
+ */
+ public static final ASN1ObjectIdentifier subjectAlternativeName = new ASN1ObjectIdentifier("2.5.29.17");
+
+ /**
+ * Issuer Alternative Name
+ */
+ public static final ASN1ObjectIdentifier issuerAlternativeName = new ASN1ObjectIdentifier("2.5.29.18");
+
+ /**
+ * Basic Constraints
+ */
+ public static final ASN1ObjectIdentifier basicConstraints = new ASN1ObjectIdentifier("2.5.29.19");
+
+ /**
+ * CRL Number
+ */
+ public static final ASN1ObjectIdentifier cRLNumber = new ASN1ObjectIdentifier("2.5.29.20");
+
+ /**
+ * Reason code
+ */
+ public static final ASN1ObjectIdentifier reasonCode = new ASN1ObjectIdentifier("2.5.29.21");
+
+ /**
+ * Hold Instruction Code
+ */
+ public static final ASN1ObjectIdentifier instructionCode = new ASN1ObjectIdentifier("2.5.29.23");
+
+ /**
+ * Invalidity Date
+ */
+ public static final ASN1ObjectIdentifier invalidityDate = new ASN1ObjectIdentifier("2.5.29.24");
+
+ /**
+ * Delta CRL indicator
+ */
+ public static final ASN1ObjectIdentifier deltaCRLIndicator = new ASN1ObjectIdentifier("2.5.29.27");
+
+ /**
+ * Issuing Distribution Point
+ */
+ public static final ASN1ObjectIdentifier issuingDistributionPoint = new ASN1ObjectIdentifier("2.5.29.28");
+
+ /**
+ * Certificate Issuer
+ */
+ public static final ASN1ObjectIdentifier certificateIssuer = new ASN1ObjectIdentifier("2.5.29.29");
+
+ /**
+ * Name Constraints
+ */
+ public static final ASN1ObjectIdentifier nameConstraints = new ASN1ObjectIdentifier("2.5.29.30");
+
+ /**
+ * CRL Distribution Points
+ */
+ public static final ASN1ObjectIdentifier cRLDistributionPoints = new ASN1ObjectIdentifier("2.5.29.31");
+
+ /**
+ * Certificate Policies
+ */
+ public static final ASN1ObjectIdentifier certificatePolicies = new ASN1ObjectIdentifier("2.5.29.32");
+
+ /**
+ * Policy Mappings
+ */
+ public static final ASN1ObjectIdentifier policyMappings = new ASN1ObjectIdentifier("2.5.29.33");
+
+ /**
+ * Authority Key Identifier
+ */
+ public static final ASN1ObjectIdentifier authorityKeyIdentifier = new ASN1ObjectIdentifier("2.5.29.35");
+
+ /**
+ * Policy Constraints
+ */
+ public static final ASN1ObjectIdentifier policyConstraints = new ASN1ObjectIdentifier("2.5.29.36");
+
+ /**
+ * Extended Key Usage
+ */
+ public static final ASN1ObjectIdentifier extendedKeyUsage = new ASN1ObjectIdentifier("2.5.29.37");
+
+ /**
+ * Freshest CRL
+ */
+ public static final ASN1ObjectIdentifier freshestCRL = new ASN1ObjectIdentifier("2.5.29.46");
+
+ /**
+ * Inhibit Any Policy
+ */
+ public static final ASN1ObjectIdentifier inhibitAnyPolicy = new ASN1ObjectIdentifier("2.5.29.54");
+
+ /**
+ * Authority Info Access
+ */
+ public static final ASN1ObjectIdentifier authorityInfoAccess = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.1");
+
+ /**
+ * Subject Info Access
+ */
+ public static final ASN1ObjectIdentifier subjectInfoAccess = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.11");
+
+ /**
+ * Logo Type
+ */
+ public static final ASN1ObjectIdentifier logoType = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.12");
+
+ /**
+ * BiometricInfo
+ */
+ public static final ASN1ObjectIdentifier biometricInfo = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.2");
+
+ /**
+ * QCStatements
+ */
+ public static final ASN1ObjectIdentifier qCStatements = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.3");
+
+ /**
+ * Audit identity extension in attribute certificates.
+ */
+ public static final ASN1ObjectIdentifier auditIdentity = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.4");
+
+ /**
+ * NoRevAvail extension in attribute certificates.
+ */
+ public static final ASN1ObjectIdentifier noRevAvail = new ASN1ObjectIdentifier("2.5.29.56");
+
+ /**
+ * TargetInformation extension in attribute certificates.
+ */
+ public static final ASN1ObjectIdentifier targetInformation = new ASN1ObjectIdentifier("2.5.29.55");
+
+ private ASN1ObjectIdentifier extnId;
+ private boolean critical;
+ private ASN1OctetString value;
+
+ public Extension(
+ ASN1ObjectIdentifier extnId,
+ ASN1Boolean critical,
+ ASN1OctetString value)
+ {
+ this(extnId, critical.isTrue(), value);
+ }
+
+ public Extension(
+ ASN1ObjectIdentifier extnId,
+ boolean critical,
+ byte[] value)
+ {
+ this(extnId, critical, new DEROctetString(value));
+ }
+
+ public Extension(
+ ASN1ObjectIdentifier extnId,
+ boolean critical,
+ ASN1OctetString value)
+ {
+ this.extnId = extnId;
+ this.critical = critical;
+ this.value = value;
+ }
+
+ private Extension(ASN1Sequence seq)
+ {
+ if (seq.size() == 2)
+ {
+ this.extnId = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+ this.critical = false;
+ this.value = ASN1OctetString.getInstance(seq.getObjectAt(1));
+ }
+ else if (seq.size() == 3)
+ {
+ this.extnId = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+ this.critical = ASN1Boolean.getInstance(seq.getObjectAt(1)).isTrue();
+ this.value = ASN1OctetString.getInstance(seq.getObjectAt(2));
+ }
+ else
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+ }
+
+ public static Extension getInstance(Object obj)
+ {
+ if (obj instanceof Extension)
+ {
+ return (Extension)obj;
+ }
+ else if (obj != null)
+ {
+ return new Extension(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public ASN1ObjectIdentifier getExtnId()
+ {
+ return extnId;
+ }
+
+ public boolean isCritical()
+ {
+ return critical;
+ }
+
+ public ASN1OctetString getExtnValue()
+ {
+ return value;
+ }
+
+ public ASN1Encodable getParsedValue()
+ {
+ return convertValueToObject(this);
+ }
+
+ public int hashCode()
+ {
+ if (this.isCritical())
+ {
+ return this.getExtnValue().hashCode() ^ this.getExtnId().hashCode();
+ }
+
+ return ~(this.getExtnValue().hashCode() ^ this.getExtnId().hashCode());
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof Extension))
+ {
+ return false;
+ }
+
+ Extension other = (Extension)o;
+
+ return other.getExtnId().equals(this.getExtnId())
+ && other.getExtnValue().equals(this.getExtnValue())
+ && (other.isCritical() == this.isCritical());
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(extnId);
+
+ if (critical)
+ {
+ v.add(ASN1Boolean.getInstance(true));
+ }
+
+ v.add(value);
+
+ return new DERSequence(v);
+ }
+
+ /**
+ * Convert the value of the passed in extension to an object
+ * @param ext the extension to parse
+ * @return the object the value string contains
+ * @exception IllegalArgumentException if conversion is not possible
+ */
+ private static ASN1Primitive convertValueToObject(
+ Extension ext)
+ throws IllegalArgumentException
+ {
+ try
+ {
+ return ASN1Primitive.fromByteArray(ext.getExtnValue().getOctets());
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("can't convert extension: " + e);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/Extensions.java b/core/src/main/java/org/bouncycastle/asn1/x509/Extensions.java
new file mode 100644
index 00000000..1aeed15d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/Extensions.java
@@ -0,0 +1,221 @@
+package org.bouncycastle.asn1.x509;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+public class Extensions
+ extends ASN1Object
+{
+ private Hashtable extensions = new Hashtable();
+ private Vector ordering = new Vector();
+
+ public static Extensions getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static Extensions getInstance(
+ Object obj)
+ {
+ if (obj instanceof Extensions)
+ {
+ return (Extensions)obj;
+ }
+ else if (obj != null)
+ {
+ return new Extensions(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Constructor from ASN1Sequence.
+ * <p/>
+ * the extensions are a list of constructed sequences, either with (OID, OctetString) or (OID, Boolean, OctetString)
+ */
+ private Extensions(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ Extension ext = Extension.getInstance(e.nextElement());
+
+ extensions.put(ext.getExtnId(), ext);
+ ordering.addElement(ext.getExtnId());
+ }
+ }
+
+ /**
+ * Base Constructor
+ *
+ * @param extension a single extension.
+ */
+ public Extensions(
+ Extension extension)
+ {
+ this.ordering.addElement(extension.getExtnId());
+ this.extensions.put(extension.getExtnId(), extension);
+ }
+
+ /**
+ * Base Constructor
+ *
+ * @param extensions an array of extensions.
+ */
+ public Extensions(
+ Extension[] extensions)
+ {
+ for (int i = 0; i != extensions.length; i++)
+ {
+ Extension ext = extensions[i];
+
+ this.ordering.addElement(ext.getExtnId());
+ this.extensions.put(ext.getExtnId(), ext);
+ }
+ }
+
+ /**
+ * return an Enumeration of the extension field's object ids.
+ */
+ public Enumeration oids()
+ {
+ return ordering.elements();
+ }
+
+ /**
+ * return the extension represented by the object identifier
+ * passed in.
+ *
+ * @return the extension if it's present, null otherwise.
+ */
+ public Extension getExtension(
+ ASN1ObjectIdentifier oid)
+ {
+ return (Extension)extensions.get(oid);
+ }
+
+ /**
+ * return the parsed value of the extension represented by the object identifier
+ * passed in.
+ *
+ * @return the parsed value of the extension if it's present, null otherwise.
+ */
+ public ASN1Encodable getExtensionParsedValue(ASN1ObjectIdentifier oid)
+ {
+ Extension ext = this.getExtension(oid);
+
+ if (ext != null)
+ {
+ return ext.getParsedValue();
+ }
+
+ return null;
+ }
+
+ /**
+ * <pre>
+ * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
+ *
+ * Extension ::= SEQUENCE {
+ * extnId EXTENSION.&amp;id ({ExtensionSet}),
+ * critical BOOLEAN DEFAULT FALSE,
+ * extnValue OCTET STRING }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+ Enumeration e = ordering.elements();
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
+ Extension ext = (Extension)extensions.get(oid);
+
+ vec.add(ext);
+ }
+
+ return new DERSequence(vec);
+ }
+
+ public boolean equivalent(
+ Extensions other)
+ {
+ if (extensions.size() != other.extensions.size())
+ {
+ return false;
+ }
+
+ Enumeration e1 = extensions.keys();
+
+ while (e1.hasMoreElements())
+ {
+ Object key = e1.nextElement();
+
+ if (!extensions.get(key).equals(other.extensions.get(key)))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public ASN1ObjectIdentifier[] getExtensionOIDs()
+ {
+ return toOidArray(ordering);
+ }
+
+ public ASN1ObjectIdentifier[] getNonCriticalExtensionOIDs()
+ {
+ return getExtensionOIDs(false);
+ }
+
+ public ASN1ObjectIdentifier[] getCriticalExtensionOIDs()
+ {
+ return getExtensionOIDs(true);
+ }
+
+ private ASN1ObjectIdentifier[] getExtensionOIDs(boolean isCritical)
+ {
+ Vector oidVec = new Vector();
+
+ for (int i = 0; i != ordering.size(); i++)
+ {
+ Object oid = ordering.elementAt(i);
+
+ if (((Extension)extensions.get(oid)).isCritical() == isCritical)
+ {
+ oidVec.addElement(oid);
+ }
+ }
+
+ return toOidArray(oidVec);
+ }
+
+ private ASN1ObjectIdentifier[] toOidArray(Vector oidVec)
+ {
+ ASN1ObjectIdentifier[] oids = new ASN1ObjectIdentifier[oidVec.size()];
+
+ for (int i = 0; i != oids.length; i++)
+ {
+ oids[i] = (ASN1ObjectIdentifier)oidVec.elementAt(i);
+ }
+ return oids;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/ExtensionsGenerator.java b/core/src/main/java/org/bouncycastle/asn1/x509/ExtensionsGenerator.java
new file mode 100644
index 00000000..270ef1c4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/ExtensionsGenerator.java
@@ -0,0 +1,94 @@
+package org.bouncycastle.asn1.x509;
+
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DEROctetString;
+
+/**
+ * Generator for X.509 extensions
+ */
+public class ExtensionsGenerator
+{
+ private Hashtable extensions = new Hashtable();
+ private Vector extOrdering = new Vector();
+
+ /**
+ * Reset the generator
+ */
+ public void reset()
+ {
+ extensions = new Hashtable();
+ extOrdering = new Vector();
+ }
+
+ /**
+ * Add an extension with the given oid and the passed in value to be included
+ * in the OCTET STRING associated with the extension.
+ *
+ * @param oid OID for the extension.
+ * @param critical true if critical, false otherwise.
+ * @param value the ASN.1 object to be included in the extension.
+ */
+ public void addExtension(
+ ASN1ObjectIdentifier oid,
+ boolean critical,
+ ASN1Encodable value)
+ throws IOException
+ {
+ this.addExtension(oid, critical, value.toASN1Primitive().getEncoded(ASN1Encoding.DER));
+ }
+
+ /**
+ * Add an extension with the given oid and the passed in byte array to be wrapped in the
+ * OCTET STRING associated with the extension.
+ *
+ * @param oid OID for the extension.
+ * @param critical true if critical, false otherwise.
+ * @param value the byte array to be wrapped.
+ */
+ public void addExtension(
+ ASN1ObjectIdentifier oid,
+ boolean critical,
+ byte[] value)
+ {
+ if (extensions.containsKey(oid))
+ {
+ throw new IllegalArgumentException("extension " + oid + " already added");
+ }
+
+ extOrdering.addElement(oid);
+ extensions.put(oid, new Extension(oid, critical, new DEROctetString(value)));
+ }
+
+ /**
+ * Return true if there are no extension present in this generator.
+ *
+ * @return true if empty, false otherwise
+ */
+ public boolean isEmpty()
+ {
+ return extOrdering.isEmpty();
+ }
+
+ /**
+ * Generate an Extensions object based on the current state of the generator.
+ *
+ * @return an X09Extensions object.
+ */
+ public Extensions generate()
+ {
+ Extension[] exts = new Extension[extOrdering.size()];
+
+ for (int i = 0; i != extOrdering.size(); i++)
+ {
+ exts[i] = (Extension)extensions.get(extOrdering.elementAt(i));
+ }
+
+ return new Extensions(exts);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/GeneralName.java b/core/src/main/java/org/bouncycastle/asn1/x509/GeneralName.java
new file mode 100644
index 00000000..1829ecd2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/GeneralName.java
@@ -0,0 +1,439 @@
+package org.bouncycastle.asn1.x509;
+
+import java.io.IOException;
+import java.util.StringTokenizer;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.util.IPAddress;
+
+/**
+ * The GeneralName object.
+ * <pre>
+ * GeneralName ::= CHOICE {
+ * otherName [0] OtherName,
+ * rfc822Name [1] IA5String,
+ * dNSName [2] IA5String,
+ * x400Address [3] ORAddress,
+ * directoryName [4] Name,
+ * ediPartyName [5] EDIPartyName,
+ * uniformResourceIdentifier [6] IA5String,
+ * iPAddress [7] OCTET STRING,
+ * registeredID [8] OBJECT IDENTIFIER}
+ *
+ * OtherName ::= SEQUENCE {
+ * type-id OBJECT IDENTIFIER,
+ * value [0] EXPLICIT ANY DEFINED BY type-id }
+ *
+ * EDIPartyName ::= SEQUENCE {
+ * nameAssigner [0] DirectoryString OPTIONAL,
+ * partyName [1] DirectoryString }
+ *
+ * Name ::= CHOICE { RDNSequence }
+ * </pre>
+ */
+public class GeneralName
+ extends ASN1Object
+ implements ASN1Choice
+{
+ public static final int otherName = 0;
+ public static final int rfc822Name = 1;
+ public static final int dNSName = 2;
+ public static final int x400Address = 3;
+ public static final int directoryName = 4;
+ public static final int ediPartyName = 5;
+ public static final int uniformResourceIdentifier = 6;
+ public static final int iPAddress = 7;
+ public static final int registeredID = 8;
+
+ private ASN1Encodable obj;
+ private int tag;
+
+ /**
+ * @deprecated use X500Name constructor.
+ * @param dirName
+ */
+ public GeneralName(
+ X509Name dirName)
+ {
+ this.obj = X500Name.getInstance(dirName);
+ this.tag = 4;
+ }
+
+ public GeneralName(
+ X500Name dirName)
+ {
+ this.obj = dirName;
+ this.tag = 4;
+ }
+
+ /**
+ * When the subjectAltName extension contains an Internet mail address,
+ * the address MUST be included as an rfc822Name. The format of an
+ * rfc822Name is an "addr-spec" as defined in RFC 822 [RFC 822].
+ *
+ * When the subjectAltName extension contains a domain name service
+ * label, the domain name MUST be stored in the dNSName (an IA5String).
+ * The name MUST be in the "preferred name syntax," as specified by RFC
+ * 1034 [RFC 1034].
+ *
+ * When the subjectAltName extension contains a URI, the name MUST be
+ * stored in the uniformResourceIdentifier (an IA5String). The name MUST
+ * be a non-relative URL, and MUST follow the URL syntax and encoding
+ * rules specified in [RFC 1738]. The name must include both a scheme
+ * (e.g., "http" or "ftp") and a scheme-specific-part. The scheme-
+ * specific-part must include a fully qualified domain name or IP
+ * address as the host.
+ *
+ * When the subjectAltName extension contains a iPAddress, the address
+ * MUST be stored in the octet string in "network byte order," as
+ * specified in RFC 791 [RFC 791]. The least significant bit (LSB) of
+ * each octet is the LSB of the corresponding byte in the network
+ * address. For IP Version 4, as specified in RFC 791, the octet string
+ * MUST contain exactly four octets. For IP Version 6, as specified in
+ * RFC 1883, the octet string MUST contain exactly sixteen octets [RFC
+ * 1883].
+ */
+ public GeneralName(
+ int tag,
+ ASN1Encodable name)
+ {
+ this.obj = name;
+ this.tag = tag;
+ }
+
+ /**
+ * Create a GeneralName for the given tag from the passed in String.
+ * <p>
+ * This constructor can handle:
+ * <ul>
+ * <li>rfc822Name
+ * <li>iPAddress
+ * <li>directoryName
+ * <li>dNSName
+ * <li>uniformResourceIdentifier
+ * <li>registeredID
+ * </ul>
+ * For x400Address, otherName and ediPartyName there is no common string
+ * format defined.
+ * <p>
+ * Note: A directory name can be encoded in different ways into a byte
+ * representation. Be aware of this if the byte representation is used for
+ * comparing results.
+ *
+ * @param tag tag number
+ * @param name string representation of name
+ * @throws IllegalArgumentException if the string encoding is not correct or * not supported.
+ */
+ public GeneralName(
+ int tag,
+ String name)
+ {
+ this.tag = tag;
+
+ if (tag == rfc822Name || tag == dNSName || tag == uniformResourceIdentifier)
+ {
+ this.obj = new DERIA5String(name);
+ }
+ else if (tag == registeredID)
+ {
+ this.obj = new ASN1ObjectIdentifier(name);
+ }
+ else if (tag == directoryName)
+ {
+ this.obj = new X500Name(name);
+ }
+ else if (tag == iPAddress)
+ {
+ byte[] enc = toGeneralNameEncoding(name);
+ if (enc != null)
+ {
+ this.obj = new DEROctetString(enc);
+ }
+ else
+ {
+ throw new IllegalArgumentException("IP Address is invalid");
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("can't process String for tag: " + tag);
+ }
+ }
+
+ public static GeneralName getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof GeneralName)
+ {
+ return (GeneralName)obj;
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject tagObj = (ASN1TaggedObject)obj;
+ int tag = tagObj.getTagNo();
+
+ switch (tag)
+ {
+ case otherName:
+ return new GeneralName(tag, ASN1Sequence.getInstance(tagObj, false));
+ case rfc822Name:
+ return new GeneralName(tag, DERIA5String.getInstance(tagObj, false));
+ case dNSName:
+ return new GeneralName(tag, DERIA5String.getInstance(tagObj, false));
+ case x400Address:
+ throw new IllegalArgumentException("unknown tag: " + tag);
+ case directoryName:
+ return new GeneralName(tag, X500Name.getInstance(tagObj, true));
+ case ediPartyName:
+ return new GeneralName(tag, ASN1Sequence.getInstance(tagObj, false));
+ case uniformResourceIdentifier:
+ return new GeneralName(tag, DERIA5String.getInstance(tagObj, false));
+ case iPAddress:
+ return new GeneralName(tag, ASN1OctetString.getInstance(tagObj, false));
+ case registeredID:
+ return new GeneralName(tag, ASN1ObjectIdentifier.getInstance(tagObj, false));
+ }
+ }
+
+ if (obj instanceof byte[])
+ {
+ try
+ {
+ return getInstance(ASN1Primitive.fromByteArray((byte[])obj));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("unable to parse encoded general name");
+ }
+ }
+
+ throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
+ }
+
+ public static GeneralName getInstance(
+ ASN1TaggedObject tagObj,
+ boolean explicit)
+ {
+ return GeneralName.getInstance(ASN1TaggedObject.getInstance(tagObj, true));
+ }
+
+ public int getTagNo()
+ {
+ return tag;
+ }
+
+ public ASN1Encodable getName()
+ {
+ return obj;
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+
+ buf.append(tag);
+ buf.append(": ");
+ switch (tag)
+ {
+ case rfc822Name:
+ case dNSName:
+ case uniformResourceIdentifier:
+ buf.append(DERIA5String.getInstance(obj).getString());
+ break;
+ case directoryName:
+ buf.append(X500Name.getInstance(obj).toString());
+ break;
+ default:
+ buf.append(obj.toString());
+ }
+ return buf.toString();
+ }
+
+ private byte[] toGeneralNameEncoding(String ip)
+ {
+ if (IPAddress.isValidIPv6WithNetmask(ip) || IPAddress.isValidIPv6(ip))
+ {
+ int slashIndex = ip.indexOf('/');
+
+ if (slashIndex < 0)
+ {
+ byte[] addr = new byte[16];
+ int[] parsedIp = parseIPv6(ip);
+ copyInts(parsedIp, addr, 0);
+
+ return addr;
+ }
+ else
+ {
+ byte[] addr = new byte[32];
+ int[] parsedIp = parseIPv6(ip.substring(0, slashIndex));
+ copyInts(parsedIp, addr, 0);
+ String mask = ip.substring(slashIndex + 1);
+ if (mask.indexOf(':') > 0)
+ {
+ parsedIp = parseIPv6(mask);
+ }
+ else
+ {
+ parsedIp = parseMask(mask);
+ }
+ copyInts(parsedIp, addr, 16);
+
+ return addr;
+ }
+ }
+ else if (IPAddress.isValidIPv4WithNetmask(ip) || IPAddress.isValidIPv4(ip))
+ {
+ int slashIndex = ip.indexOf('/');
+
+ if (slashIndex < 0)
+ {
+ byte[] addr = new byte[4];
+
+ parseIPv4(ip, addr, 0);
+
+ return addr;
+ }
+ else
+ {
+ byte[] addr = new byte[8];
+
+ parseIPv4(ip.substring(0, slashIndex), addr, 0);
+
+ String mask = ip.substring(slashIndex + 1);
+ if (mask.indexOf('.') > 0)
+ {
+ parseIPv4(mask, addr, 4);
+ }
+ else
+ {
+ parseIPv4Mask(mask, addr, 4);
+ }
+
+ return addr;
+ }
+ }
+
+ return null;
+ }
+
+ private void parseIPv4Mask(String mask, byte[] addr, int offset)
+ {
+ int maskVal = Integer.parseInt(mask);
+
+ for (int i = 0; i != maskVal; i++)
+ {
+ addr[(i / 8) + offset] |= 1 << (7 - (i % 8));
+ }
+ }
+
+ private void parseIPv4(String ip, byte[] addr, int offset)
+ {
+ StringTokenizer sTok = new StringTokenizer(ip, "./");
+ int index = 0;
+
+ while (sTok.hasMoreTokens())
+ {
+ addr[offset + index++] = (byte)Integer.parseInt(sTok.nextToken());
+ }
+ }
+
+ private int[] parseMask(String mask)
+ {
+ int[] res = new int[8];
+ int maskVal = Integer.parseInt(mask);
+
+ for (int i = 0; i != maskVal; i++)
+ {
+ res[i / 16] |= 1 << (15 - (i % 16));
+ }
+ return res;
+ }
+
+ private void copyInts(int[] parsedIp, byte[] addr, int offSet)
+ {
+ for (int i = 0; i != parsedIp.length; i++)
+ {
+ addr[(i * 2) + offSet] = (byte)(parsedIp[i] >> 8);
+ addr[(i * 2 + 1) + offSet] = (byte)parsedIp[i];
+ }
+ }
+
+ private int[] parseIPv6(String ip)
+ {
+ StringTokenizer sTok = new StringTokenizer(ip, ":", true);
+ int index = 0;
+ int[] val = new int[8];
+
+ if (ip.charAt(0) == ':' && ip.charAt(1) == ':')
+ {
+ sTok.nextToken(); // skip the first one
+ }
+
+ int doubleColon = -1;
+
+ while (sTok.hasMoreTokens())
+ {
+ String e = sTok.nextToken();
+
+ if (e.equals(":"))
+ {
+ doubleColon = index;
+ val[index++] = 0;
+ }
+ else
+ {
+ if (e.indexOf('.') < 0)
+ {
+ val[index++] = Integer.parseInt(e, 16);
+ if (sTok.hasMoreTokens())
+ {
+ sTok.nextToken();
+ }
+ }
+ else
+ {
+ StringTokenizer eTok = new StringTokenizer(e, ".");
+
+ val[index++] = (Integer.parseInt(eTok.nextToken()) << 8) | Integer.parseInt(eTok.nextToken());
+ val[index++] = (Integer.parseInt(eTok.nextToken()) << 8) | Integer.parseInt(eTok.nextToken());
+ }
+ }
+ }
+
+ if (index != val.length)
+ {
+ System.arraycopy(val, doubleColon, val, val.length - (index - doubleColon), index - doubleColon);
+ for (int i = doubleColon; i != val.length - (index - doubleColon); i++)
+ {
+ val[i] = 0;
+ }
+ }
+
+ return val;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ if (tag == directoryName) // directoryName is explicitly tagged as it is a CHOICE
+ {
+ return new DERTaggedObject(true, tag, obj);
+ }
+ else
+ {
+ return new DERTaggedObject(false, tag, obj);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/GeneralNames.java b/core/src/main/java/org/bouncycastle/asn1/x509/GeneralNames.java
new file mode 100644
index 00000000..7118d107
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/GeneralNames.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+public class GeneralNames
+ extends ASN1Object
+{
+ private final GeneralName[] names;
+
+ public static GeneralNames getInstance(
+ Object obj)
+ {
+ if (obj instanceof GeneralNames)
+ {
+ return (GeneralNames)obj;
+ }
+
+ if (obj != null)
+ {
+ return new GeneralNames(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static GeneralNames getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static GeneralNames fromExtensions(Extensions extensions, ASN1ObjectIdentifier extOID)
+ {
+ return GeneralNames.getInstance(extensions.getExtensionParsedValue(extOID));
+ }
+
+ /**
+ * Construct a GeneralNames object containing one GeneralName.
+ *
+ * @param name the name to be contained.
+ */
+ public GeneralNames(
+ GeneralName name)
+ {
+ this.names = new GeneralName[] { name };
+ }
+
+
+ public GeneralNames(
+ GeneralName[] names)
+ {
+ this.names = names;
+ }
+
+ private GeneralNames(
+ ASN1Sequence seq)
+ {
+ this.names = new GeneralName[seq.size()];
+
+ for (int i = 0; i != seq.size(); i++)
+ {
+ names[i] = GeneralName.getInstance(seq.getObjectAt(i));
+ }
+ }
+
+ public GeneralName[] getNames()
+ {
+ GeneralName[] tmp = new GeneralName[names.length];
+
+ System.arraycopy(names, 0, tmp, 0, names.length);
+
+ return tmp;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * GeneralNames ::= SEQUENCE SIZE {1..MAX} OF GeneralName
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return new DERSequence(names);
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ String sep = System.getProperty("line.separator");
+
+ buf.append("GeneralNames:");
+ buf.append(sep);
+
+ for (int i = 0; i != names.length; i++)
+ {
+ buf.append(" ");
+ buf.append(names[i]);
+ buf.append(sep);
+ }
+ return buf.toString();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/GeneralNamesBuilder.java b/core/src/main/java/org/bouncycastle/asn1/x509/GeneralNamesBuilder.java
new file mode 100644
index 00000000..14f0c2c1
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/GeneralNamesBuilder.java
@@ -0,0 +1,39 @@
+package org.bouncycastle.asn1.x509;
+
+import java.util.Vector;
+
+public class GeneralNamesBuilder
+{
+ private Vector names = new Vector();
+
+ public GeneralNamesBuilder addNames(GeneralNames names)
+ {
+ GeneralName[] n = names.getNames();
+
+ for (int i = 0; i != n.length; i++)
+ {
+ this.names.addElement(n[i]);
+ }
+
+ return this;
+ }
+
+ public GeneralNamesBuilder addName(GeneralName name)
+ {
+ names.addElement(name);
+
+ return this;
+ }
+
+ public GeneralNames build()
+ {
+ GeneralName[] tmp = new GeneralName[names.size()];
+
+ for (int i = 0; i != tmp.length; i++)
+ {
+ tmp[i] = (GeneralName)names.elementAt(i);
+ }
+
+ return new GeneralNames(tmp);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/GeneralSubtree.java b/core/src/main/java/org/bouncycastle/asn1/x509/GeneralSubtree.java
new file mode 100644
index 00000000..bf72ce63
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/GeneralSubtree.java
@@ -0,0 +1,218 @@
+package org.bouncycastle.asn1.x509;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * Class for containing a restriction object subtrees in NameConstraints. See
+ * RFC 3280.
+ *
+ * <pre>
+ *
+ * GeneralSubtree ::= SEQUENCE
+ * {
+ * base GeneralName,
+ * minimum [0] BaseDistance DEFAULT 0,
+ * maximum [1] BaseDistance OPTIONAL
+ * }
+ * </pre>
+ *
+ * @see org.bouncycastle.asn1.x509.NameConstraints
+ *
+ */
+public class GeneralSubtree
+ extends ASN1Object
+{
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+
+ private GeneralName base;
+
+ private ASN1Integer minimum;
+
+ private ASN1Integer maximum;
+
+ private GeneralSubtree(
+ ASN1Sequence seq)
+ {
+ base = GeneralName.getInstance(seq.getObjectAt(0));
+
+ switch (seq.size())
+ {
+ case 1:
+ break;
+ case 2:
+ ASN1TaggedObject o = ASN1TaggedObject.getInstance(seq.getObjectAt(1));
+ switch (o.getTagNo())
+ {
+ case 0:
+ minimum = ASN1Integer.getInstance(o, false);
+ break;
+ case 1:
+ maximum = ASN1Integer.getInstance(o, false);
+ break;
+ default:
+ throw new IllegalArgumentException("Bad tag number: "
+ + o.getTagNo());
+ }
+ break;
+ case 3:
+ {
+ {
+ ASN1TaggedObject oMin = ASN1TaggedObject.getInstance(seq.getObjectAt(1));
+ if (oMin.getTagNo() != 0)
+ {
+ throw new IllegalArgumentException("Bad tag number for 'minimum': " + oMin.getTagNo());
+ }
+ minimum = ASN1Integer.getInstance(oMin, false);
+ }
+
+ {
+ ASN1TaggedObject oMax = ASN1TaggedObject.getInstance(seq.getObjectAt(2));
+ if (oMax.getTagNo() != 1)
+ {
+ throw new IllegalArgumentException("Bad tag number for 'maximum': " + oMax.getTagNo());
+ }
+ maximum = ASN1Integer.getInstance(oMax, false);
+ }
+
+ break;
+ }
+ default:
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+ }
+
+ /**
+ * Constructor from a given details.
+ *
+ * According RFC 3280, the minimum and maximum fields are not used with any
+ * name forms, thus minimum MUST be zero, and maximum MUST be absent.
+ * <p>
+ * If minimum is <code>null</code>, zero is assumed, if
+ * maximum is <code>null</code>, maximum is absent.
+ *
+ * @param base
+ * A restriction.
+ * @param minimum
+ * Minimum
+ *
+ * @param maximum
+ * Maximum
+ */
+ public GeneralSubtree(
+ GeneralName base,
+ BigInteger minimum,
+ BigInteger maximum)
+ {
+ this.base = base;
+ if (maximum != null)
+ {
+ this.maximum = new ASN1Integer(maximum);
+ }
+ if (minimum == null)
+ {
+ this.minimum = null;
+ }
+ else
+ {
+ this.minimum = new ASN1Integer(minimum);
+ }
+ }
+
+ public GeneralSubtree(GeneralName base)
+ {
+ this(base, null, null);
+ }
+
+ public static GeneralSubtree getInstance(
+ ASN1TaggedObject o,
+ boolean explicit)
+ {
+ return new GeneralSubtree(ASN1Sequence.getInstance(o, explicit));
+ }
+
+ public static GeneralSubtree getInstance(
+ Object obj)
+ {
+ if (obj == null)
+ {
+ return null;
+ }
+
+ if (obj instanceof GeneralSubtree)
+ {
+ return (GeneralSubtree) obj;
+ }
+
+ return new GeneralSubtree(ASN1Sequence.getInstance(obj));
+ }
+
+ public GeneralName getBase()
+ {
+ return base;
+ }
+
+ public BigInteger getMinimum()
+ {
+ if (minimum == null)
+ {
+ return ZERO;
+ }
+
+ return minimum.getValue();
+ }
+
+ public BigInteger getMaximum()
+ {
+ if (maximum == null)
+ {
+ return null;
+ }
+
+ return maximum.getValue();
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ *
+ * Returns:
+ *
+ * <pre>
+ * GeneralSubtree ::= SEQUENCE
+ * {
+ * base GeneralName,
+ * minimum [0] BaseDistance DEFAULT 0,
+ * maximum [1] BaseDistance OPTIONAL
+ * }
+ * </pre>
+ *
+ * @return a ASN1Primitive
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(base);
+
+ if (minimum != null && !minimum.getValue().equals(ZERO))
+ {
+ v.add(new DERTaggedObject(false, 0, minimum));
+ }
+
+ if (maximum != null)
+ {
+ v.add(new DERTaggedObject(false, 1, maximum));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/Holder.java b/core/src/main/java/org/bouncycastle/asn1/x509/Holder.java
new file mode 100644
index 00000000..6ae6e354
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/Holder.java
@@ -0,0 +1,245 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * The Holder object.
+ * <p>
+ * For an v2 attribute certificate this is:
+ *
+ * <pre>
+ * Holder ::= SEQUENCE {
+ * baseCertificateID [0] IssuerSerial OPTIONAL,
+ * -- the issuer and serial number of
+ * -- the holder's Public Key Certificate
+ * entityName [1] GeneralNames OPTIONAL,
+ * -- the name of the claimant or role
+ * objectDigestInfo [2] ObjectDigestInfo OPTIONAL
+ * -- used to directly authenticate the holder,
+ * -- for example, an executable
+ * }
+ * </pre>
+ *
+ * <p>
+ * For an v1 attribute certificate this is:
+ *
+ * <pre>
+ * subject CHOICE {
+ * baseCertificateID [0] IssuerSerial,
+ * -- associated with a Public Key Certificate
+ * subjectName [1] GeneralNames },
+ * -- associated with a name
+ * </pre>
+ */
+public class Holder
+ extends ASN1Object
+{
+ public static final int V1_CERTIFICATE_HOLDER = 0;
+ public static final int V2_CERTIFICATE_HOLDER = 1;
+
+ IssuerSerial baseCertificateID;
+
+ GeneralNames entityName;
+
+ ObjectDigestInfo objectDigestInfo;
+
+ private int version = V2_CERTIFICATE_HOLDER;
+
+ public static Holder getInstance(Object obj)
+ {
+ if (obj instanceof Holder)
+ {
+ return (Holder)obj;
+ }
+ else if (obj instanceof ASN1TaggedObject)
+ {
+ return new Holder(ASN1TaggedObject.getInstance(obj));
+ }
+ else if (obj != null)
+ {
+ return new Holder(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Constructor for a holder for an V1 attribute certificate.
+ *
+ * @param tagObj The ASN.1 tagged holder object.
+ */
+ private Holder(ASN1TaggedObject tagObj)
+ {
+ switch (tagObj.getTagNo())
+ {
+ case 0:
+ baseCertificateID = IssuerSerial.getInstance(tagObj, false);
+ break;
+ case 1:
+ entityName = GeneralNames.getInstance(tagObj, false);
+ break;
+ default:
+ throw new IllegalArgumentException("unknown tag in Holder");
+ }
+ version = 0;
+ }
+
+ /**
+ * Constructor for a holder for an V2 attribute certificate.
+ *
+ * @param seq The ASN.1 sequence.
+ */
+ private Holder(ASN1Sequence seq)
+ {
+ if (seq.size() > 3)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ for (int i = 0; i != seq.size(); i++)
+ {
+ ASN1TaggedObject tObj = ASN1TaggedObject.getInstance(seq
+ .getObjectAt(i));
+
+ switch (tObj.getTagNo())
+ {
+ case 0:
+ baseCertificateID = IssuerSerial.getInstance(tObj, false);
+ break;
+ case 1:
+ entityName = GeneralNames.getInstance(tObj, false);
+ break;
+ case 2:
+ objectDigestInfo = ObjectDigestInfo.getInstance(tObj, false);
+ break;
+ default:
+ throw new IllegalArgumentException("unknown tag in Holder");
+ }
+ }
+ version = 1;
+ }
+
+ public Holder(IssuerSerial baseCertificateID)
+ {
+ this(baseCertificateID, V2_CERTIFICATE_HOLDER);
+ }
+
+ /**
+ * Constructs a holder from a IssuerSerial for a V1 or V2 certificate.
+ * .
+ * @param baseCertificateID The IssuerSerial.
+ * @param version The version of the attribute certificate.
+ */
+ public Holder(IssuerSerial baseCertificateID, int version)
+ {
+ this.baseCertificateID = baseCertificateID;
+ this.version = version;
+ }
+
+ /**
+ * Returns 1 for V2 attribute certificates or 0 for V1 attribute
+ * certificates.
+ * @return The version of the attribute certificate.
+ */
+ public int getVersion()
+ {
+ return version;
+ }
+
+ /**
+ * Constructs a holder with an entityName for V2 attribute certificates.
+ *
+ * @param entityName The entity or subject name.
+ */
+ public Holder(GeneralNames entityName)
+ {
+ this(entityName, V2_CERTIFICATE_HOLDER);
+ }
+
+ /**
+ * Constructs a holder with an entityName for V2 attribute certificates or
+ * with a subjectName for V1 attribute certificates.
+ *
+ * @param entityName The entity or subject name.
+ * @param version The version of the attribute certificate.
+ */
+ public Holder(GeneralNames entityName, int version)
+ {
+ this.entityName = entityName;
+ this.version = version;
+ }
+
+ /**
+ * Constructs a holder from an object digest info.
+ *
+ * @param objectDigestInfo The object digest info object.
+ */
+ public Holder(ObjectDigestInfo objectDigestInfo)
+ {
+ this.objectDigestInfo = objectDigestInfo;
+ }
+
+ public IssuerSerial getBaseCertificateID()
+ {
+ return baseCertificateID;
+ }
+
+ /**
+ * Returns the entityName for an V2 attribute certificate or the subjectName
+ * for an V1 attribute certificate.
+ *
+ * @return The entityname or subjectname.
+ */
+ public GeneralNames getEntityName()
+ {
+ return entityName;
+ }
+
+ public ObjectDigestInfo getObjectDigestInfo()
+ {
+ return objectDigestInfo;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ if (version == 1)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (baseCertificateID != null)
+ {
+ v.add(new DERTaggedObject(false, 0, baseCertificateID));
+ }
+
+ if (entityName != null)
+ {
+ v.add(new DERTaggedObject(false, 1, entityName));
+ }
+
+ if (objectDigestInfo != null)
+ {
+ v.add(new DERTaggedObject(false, 2, objectDigestInfo));
+ }
+
+ return new DERSequence(v);
+ }
+ else
+ {
+ if (entityName != null)
+ {
+ return new DERTaggedObject(false, 1, entityName);
+ }
+ else
+ {
+ return new DERTaggedObject(false, 0, baseCertificateID);
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/IetfAttrSyntax.java b/core/src/main/java/org/bouncycastle/asn1/x509/IetfAttrSyntax.java
new file mode 100644
index 00000000..5a701407
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/IetfAttrSyntax.java
@@ -0,0 +1,189 @@
+package org.bouncycastle.asn1.x509;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.DERUTF8String;
+
+/**
+ * Implementation of <code>IetfAttrSyntax</code> as specified by RFC3281.
+ */
+public class IetfAttrSyntax
+ extends ASN1Object
+{
+ public static final int VALUE_OCTETS = 1;
+ public static final int VALUE_OID = 2;
+ public static final int VALUE_UTF8 = 3;
+ GeneralNames policyAuthority = null;
+ Vector values = new Vector();
+ int valueChoice = -1;
+
+ public static IetfAttrSyntax getInstance(Object obj)
+ {
+ if (obj instanceof IetfAttrSyntax)
+ {
+ return (IetfAttrSyntax)obj;
+ }
+ if (obj != null)
+ {
+ return new IetfAttrSyntax(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ *
+ */
+ private IetfAttrSyntax(ASN1Sequence seq)
+ {
+ int i = 0;
+
+ if (seq.getObjectAt(0) instanceof ASN1TaggedObject)
+ {
+ policyAuthority = GeneralNames.getInstance(((ASN1TaggedObject)seq.getObjectAt(0)), false);
+ i++;
+ }
+ else if (seq.size() == 2)
+ { // VOMS fix
+ policyAuthority = GeneralNames.getInstance(seq.getObjectAt(0));
+ i++;
+ }
+
+ if (!(seq.getObjectAt(i) instanceof ASN1Sequence))
+ {
+ throw new IllegalArgumentException("Non-IetfAttrSyntax encoding");
+ }
+
+ seq = (ASN1Sequence)seq.getObjectAt(i);
+
+ for (Enumeration e = seq.getObjects(); e.hasMoreElements();)
+ {
+ ASN1Primitive obj = (ASN1Primitive)e.nextElement();
+ int type;
+
+ if (obj instanceof ASN1ObjectIdentifier)
+ {
+ type = VALUE_OID;
+ }
+ else if (obj instanceof DERUTF8String)
+ {
+ type = VALUE_UTF8;
+ }
+ else if (obj instanceof DEROctetString)
+ {
+ type = VALUE_OCTETS;
+ }
+ else
+ {
+ throw new IllegalArgumentException("Bad value type encoding IetfAttrSyntax");
+ }
+
+ if (valueChoice < 0)
+ {
+ valueChoice = type;
+ }
+
+ if (type != valueChoice)
+ {
+ throw new IllegalArgumentException("Mix of value types in IetfAttrSyntax");
+ }
+
+ values.addElement(obj);
+ }
+ }
+
+ public GeneralNames getPolicyAuthority()
+ {
+ return policyAuthority;
+ }
+
+ public int getValueType()
+ {
+ return valueChoice;
+ }
+
+ public Object[] getValues()
+ {
+ if (this.getValueType() == VALUE_OCTETS)
+ {
+ ASN1OctetString[] tmp = new ASN1OctetString[values.size()];
+
+ for (int i = 0; i != tmp.length; i++)
+ {
+ tmp[i] = (ASN1OctetString)values.elementAt(i);
+ }
+
+ return tmp;
+ }
+ else if (this.getValueType() == VALUE_OID)
+ {
+ ASN1ObjectIdentifier[] tmp = new ASN1ObjectIdentifier[values.size()];
+
+ for (int i = 0; i != tmp.length; i++)
+ {
+ tmp[i] = (ASN1ObjectIdentifier)values.elementAt(i);
+ }
+
+ return tmp;
+ }
+ else
+ {
+ DERUTF8String[] tmp = new DERUTF8String[values.size()];
+
+ for (int i = 0; i != tmp.length; i++)
+ {
+ tmp[i] = (DERUTF8String)values.elementAt(i);
+ }
+
+ return tmp;
+ }
+ }
+
+ /**
+ *
+ * <pre>
+ *
+ * IetfAttrSyntax ::= SEQUENCE {
+ * policyAuthority [0] GeneralNames OPTIONAL,
+ * values SEQUENCE OF CHOICE {
+ * octets OCTET STRING,
+ * oid OBJECT IDENTIFIER,
+ * string UTF8String
+ * }
+ * }
+ *
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (policyAuthority != null)
+ {
+ v.add(new DERTaggedObject(0, policyAuthority));
+ }
+
+ ASN1EncodableVector v2 = new ASN1EncodableVector();
+
+ for (Enumeration i = values.elements(); i.hasMoreElements();)
+ {
+ v2.add((ASN1Encodable)i.nextElement());
+ }
+
+ v.add(new DERSequence(v2));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/IssuerSerial.java b/core/src/main/java/org/bouncycastle/asn1/x509/IssuerSerial.java
new file mode 100644
index 00000000..8d3036b2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/IssuerSerial.java
@@ -0,0 +1,115 @@
+package org.bouncycastle.asn1.x509;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+
+public class IssuerSerial
+ extends ASN1Object
+{
+ GeneralNames issuer;
+ ASN1Integer serial;
+ DERBitString issuerUID;
+
+ public static IssuerSerial getInstance(
+ Object obj)
+ {
+ if (obj instanceof IssuerSerial)
+ {
+ return (IssuerSerial)obj;
+ }
+
+ if (obj != null)
+ {
+ return new IssuerSerial(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static IssuerSerial getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ private IssuerSerial(
+ ASN1Sequence seq)
+ {
+ if (seq.size() != 2 && seq.size() != 3)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ issuer = GeneralNames.getInstance(seq.getObjectAt(0));
+ serial = ASN1Integer.getInstance(seq.getObjectAt(1));
+
+ if (seq.size() == 3)
+ {
+ issuerUID = DERBitString.getInstance(seq.getObjectAt(2));
+ }
+ }
+
+ public IssuerSerial(
+ GeneralNames issuer,
+ BigInteger serial)
+ {
+ this(issuer, new ASN1Integer(serial));
+ }
+
+ public IssuerSerial(
+ GeneralNames issuer,
+ ASN1Integer serial)
+ {
+ this.issuer = issuer;
+ this.serial = serial;
+ }
+
+ public GeneralNames getIssuer()
+ {
+ return issuer;
+ }
+
+ public ASN1Integer getSerial()
+ {
+ return serial;
+ }
+
+ public DERBitString getIssuerUID()
+ {
+ return issuerUID;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * IssuerSerial ::= SEQUENCE {
+ * issuer GeneralNames,
+ * serial CertificateSerialNumber,
+ * issuerUID UniqueIdentifier OPTIONAL
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(issuer);
+ v.add(serial);
+
+ if (issuerUID != null)
+ {
+ v.add(issuerUID);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java b/core/src/main/java/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java
new file mode 100644
index 00000000..1f29162b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java
@@ -0,0 +1,274 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Boolean;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * <pre>
+ * IssuingDistributionPoint ::= SEQUENCE {
+ * distributionPoint [0] DistributionPointName OPTIONAL,
+ * onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE,
+ * onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE,
+ * onlySomeReasons [3] ReasonFlags OPTIONAL,
+ * indirectCRL [4] BOOLEAN DEFAULT FALSE,
+ * onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE }
+ * </pre>
+ */
+public class IssuingDistributionPoint
+ extends ASN1Object
+{
+ private DistributionPointName distributionPoint;
+
+ private boolean onlyContainsUserCerts;
+
+ private boolean onlyContainsCACerts;
+
+ private ReasonFlags onlySomeReasons;
+
+ private boolean indirectCRL;
+
+ private boolean onlyContainsAttributeCerts;
+
+ private ASN1Sequence seq;
+
+ public static IssuingDistributionPoint getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static IssuingDistributionPoint getInstance(
+ Object obj)
+ {
+ if (obj instanceof IssuingDistributionPoint)
+ {
+ return (IssuingDistributionPoint)obj;
+ }
+ else if (obj != null)
+ {
+ return new IssuingDistributionPoint(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Constructor from given details.
+ *
+ * @param distributionPoint
+ * May contain an URI as pointer to most current CRL.
+ * @param onlyContainsUserCerts Covers revocation information for end certificates.
+ * @param onlyContainsCACerts Covers revocation information for CA certificates.
+ *
+ * @param onlySomeReasons
+ * Which revocation reasons does this point cover.
+ * @param indirectCRL
+ * If <code>true</code> then the CRL contains revocation
+ * information about certificates ssued by other CAs.
+ * @param onlyContainsAttributeCerts Covers revocation information for attribute certificates.
+ */
+ public IssuingDistributionPoint(
+ DistributionPointName distributionPoint,
+ boolean onlyContainsUserCerts,
+ boolean onlyContainsCACerts,
+ ReasonFlags onlySomeReasons,
+ boolean indirectCRL,
+ boolean onlyContainsAttributeCerts)
+ {
+ this.distributionPoint = distributionPoint;
+ this.indirectCRL = indirectCRL;
+ this.onlyContainsAttributeCerts = onlyContainsAttributeCerts;
+ this.onlyContainsCACerts = onlyContainsCACerts;
+ this.onlyContainsUserCerts = onlyContainsUserCerts;
+ this.onlySomeReasons = onlySomeReasons;
+
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+ if (distributionPoint != null)
+ { // CHOICE item so explicitly tagged
+ vec.add(new DERTaggedObject(true, 0, distributionPoint));
+ }
+ if (onlyContainsUserCerts)
+ {
+ vec.add(new DERTaggedObject(false, 1, ASN1Boolean.getInstance(true)));
+ }
+ if (onlyContainsCACerts)
+ {
+ vec.add(new DERTaggedObject(false, 2, ASN1Boolean.getInstance(true)));
+ }
+ if (onlySomeReasons != null)
+ {
+ vec.add(new DERTaggedObject(false, 3, onlySomeReasons));
+ }
+ if (indirectCRL)
+ {
+ vec.add(new DERTaggedObject(false, 4, ASN1Boolean.getInstance(true)));
+ }
+ if (onlyContainsAttributeCerts)
+ {
+ vec.add(new DERTaggedObject(false, 5, ASN1Boolean.getInstance(true)));
+ }
+
+ seq = new DERSequence(vec);
+ }
+
+ /**
+ * Shorthand Constructor from given details.
+ *
+ * @param distributionPoint
+ * May contain an URI as pointer to most current CRL.
+ * @param indirectCRL
+ * If <code>true</code> then the CRL contains revocation
+ * information about certificates ssued by other CAs.
+ * @param onlyContainsAttributeCerts Covers revocation information for attribute certificates.
+ */
+ public IssuingDistributionPoint(
+ DistributionPointName distributionPoint,
+ boolean indirectCRL,
+ boolean onlyContainsAttributeCerts)
+ {
+ this(distributionPoint, false, false, null, indirectCRL, onlyContainsAttributeCerts);
+ }
+
+ /**
+ * Constructor from ASN1Sequence
+ */
+ private IssuingDistributionPoint(
+ ASN1Sequence seq)
+ {
+ this.seq = seq;
+
+ for (int i = 0; i != seq.size(); i++)
+ {
+ ASN1TaggedObject o = ASN1TaggedObject.getInstance(seq.getObjectAt(i));
+
+ switch (o.getTagNo())
+ {
+ case 0:
+ // CHOICE so explicit
+ distributionPoint = DistributionPointName.getInstance(o, true);
+ break;
+ case 1:
+ onlyContainsUserCerts = ASN1Boolean.getInstance(o, false).isTrue();
+ break;
+ case 2:
+ onlyContainsCACerts = ASN1Boolean.getInstance(o, false).isTrue();
+ break;
+ case 3:
+ onlySomeReasons = new ReasonFlags(ReasonFlags.getInstance(o, false));
+ break;
+ case 4:
+ indirectCRL = ASN1Boolean.getInstance(o, false).isTrue();
+ break;
+ case 5:
+ onlyContainsAttributeCerts = ASN1Boolean.getInstance(o, false).isTrue();
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "unknown tag in IssuingDistributionPoint");
+ }
+ }
+ }
+
+ public boolean onlyContainsUserCerts()
+ {
+ return onlyContainsUserCerts;
+ }
+
+ public boolean onlyContainsCACerts()
+ {
+ return onlyContainsCACerts;
+ }
+
+ public boolean isIndirectCRL()
+ {
+ return indirectCRL;
+ }
+
+ public boolean onlyContainsAttributeCerts()
+ {
+ return onlyContainsAttributeCerts;
+ }
+
+ /**
+ * @return Returns the distributionPoint.
+ */
+ public DistributionPointName getDistributionPoint()
+ {
+ return distributionPoint;
+ }
+
+ /**
+ * @return Returns the onlySomeReasons.
+ */
+ public ReasonFlags getOnlySomeReasons()
+ {
+ return onlySomeReasons;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return seq;
+ }
+
+ public String toString()
+ {
+ String sep = System.getProperty("line.separator");
+ StringBuffer buf = new StringBuffer();
+
+ buf.append("IssuingDistributionPoint: [");
+ buf.append(sep);
+ if (distributionPoint != null)
+ {
+ appendObject(buf, sep, "distributionPoint", distributionPoint.toString());
+ }
+ if (onlyContainsUserCerts)
+ {
+ appendObject(buf, sep, "onlyContainsUserCerts", booleanToString(onlyContainsUserCerts));
+ }
+ if (onlyContainsCACerts)
+ {
+ appendObject(buf, sep, "onlyContainsCACerts", booleanToString(onlyContainsCACerts));
+ }
+ if (onlySomeReasons != null)
+ {
+ appendObject(buf, sep, "onlySomeReasons", onlySomeReasons.toString());
+ }
+ if (onlyContainsAttributeCerts)
+ {
+ appendObject(buf, sep, "onlyContainsAttributeCerts", booleanToString(onlyContainsAttributeCerts));
+ }
+ if (indirectCRL)
+ {
+ appendObject(buf, sep, "indirectCRL", booleanToString(indirectCRL));
+ }
+ buf.append("]");
+ buf.append(sep);
+ return buf.toString();
+ }
+
+ private void appendObject(StringBuffer buf, String sep, String name, String value)
+ {
+ String indent = " ";
+
+ buf.append(indent);
+ buf.append(name);
+ buf.append(":");
+ buf.append(sep);
+ buf.append(indent);
+ buf.append(indent);
+ buf.append(value);
+ buf.append(sep);
+ }
+
+ private String booleanToString(boolean value)
+ {
+ return value ? "true" : "false";
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/KeyPurposeId.java b/core/src/main/java/org/bouncycastle/asn1/x509/KeyPurposeId.java
new file mode 100644
index 00000000..01980bec
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/KeyPurposeId.java
@@ -0,0 +1,157 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+
+/**
+ * The KeyPurposeId object.
+ * <pre>
+ * KeyPurposeId ::= OBJECT IDENTIFIER
+ *
+ * id-kp ::= OBJECT IDENTIFIER { iso(1) identified-organization(3)
+ * dod(6) internet(1) security(5) mechanisms(5) pkix(7) 3}
+ *
+ * </pre>
+ * To create a new KeyPurposeId where none of the below suit, use
+ * <pre>
+ * ASN1ObjectIdentifier newKeyPurposeIdOID = new ASN1ObjectIdentifier("1.3.6.1...");
+ *
+ * KeyPurposeId newKeyPurposeId = KeyPurposeId.getInstance(newKeyPurposeIdOID);
+ * </pre>
+ */
+public class KeyPurposeId
+ extends ASN1Object
+{
+ private static final ASN1ObjectIdentifier id_kp = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.3");
+
+ /**
+ * { 2 5 29 37 0 }
+ */
+ public static final KeyPurposeId anyExtendedKeyUsage = new KeyPurposeId(Extension.extendedKeyUsage.branch("0"));
+
+ /**
+ * { id-kp 1 }
+ */
+ public static final KeyPurposeId id_kp_serverAuth = new KeyPurposeId(id_kp.branch("1"));
+ /**
+ * { id-kp 2 }
+ */
+ public static final KeyPurposeId id_kp_clientAuth = new KeyPurposeId(id_kp.branch("2"));
+ /**
+ * { id-kp 3 }
+ */
+ public static final KeyPurposeId id_kp_codeSigning = new KeyPurposeId(id_kp.branch("3"));
+ /**
+ * { id-kp 4 }
+ */
+ public static final KeyPurposeId id_kp_emailProtection = new KeyPurposeId(id_kp.branch("4"));
+ /**
+ * Usage deprecated by RFC4945 - was { id-kp 5 }
+ */
+ public static final KeyPurposeId id_kp_ipsecEndSystem = new KeyPurposeId(id_kp.branch("5"));
+ /**
+ * Usage deprecated by RFC4945 - was { id-kp 6 }
+ */
+ public static final KeyPurposeId id_kp_ipsecTunnel = new KeyPurposeId(id_kp.branch("6"));
+ /**
+ * Usage deprecated by RFC4945 - was { idkp 7 }
+ */
+ public static final KeyPurposeId id_kp_ipsecUser = new KeyPurposeId(id_kp.branch("7"));
+ /**
+ * { id-kp 8 }
+ */
+ public static final KeyPurposeId id_kp_timeStamping = new KeyPurposeId(id_kp.branch("8"));
+ /**
+ * { id-kp 9 }
+ */
+ public static final KeyPurposeId id_kp_OCSPSigning = new KeyPurposeId(id_kp.branch("9"));
+ /**
+ * { id-kp 10 }
+ */
+ public static final KeyPurposeId id_kp_dvcs = new KeyPurposeId(id_kp.branch("10"));
+ /**
+ * { id-kp 11 }
+ */
+ public static final KeyPurposeId id_kp_sbgpCertAAServerAuth = new KeyPurposeId(id_kp.branch("11"));
+ /**
+ * { id-kp 12 }
+ */
+ public static final KeyPurposeId id_kp_scvp_responder = new KeyPurposeId(id_kp.branch("12"));
+ /**
+ * { id-kp 13 }
+ */
+ public static final KeyPurposeId id_kp_eapOverPPP = new KeyPurposeId(id_kp.branch("13"));
+ /**
+ * { id-kp 14 }
+ */
+ public static final KeyPurposeId id_kp_eapOverLAN = new KeyPurposeId(id_kp.branch("14"));
+ /**
+ * { id-kp 15 }
+ */
+ public static final KeyPurposeId id_kp_scvpServer = new KeyPurposeId(id_kp.branch("15"));
+ /**
+ * { id-kp 16 }
+ */
+ public static final KeyPurposeId id_kp_scvpClient = new KeyPurposeId(id_kp.branch("16"));
+ /**
+ * { id-kp 17 }
+ */
+ public static final KeyPurposeId id_kp_ipsecIKE = new KeyPurposeId(id_kp.branch("17"));
+ /**
+ * { id-kp 18 }
+ */
+ public static final KeyPurposeId id_kp_capwapAC = new KeyPurposeId(id_kp.branch("18"));
+ /**
+ * { id-kp 19 }
+ */
+ public static final KeyPurposeId id_kp_capwapWTP = new KeyPurposeId(id_kp.branch("19"));
+
+ //
+ // microsoft key purpose ids
+ //
+ /**
+ * { 1 3 6 1 4 1 311 20 2 2 }
+ */
+ public static final KeyPurposeId id_kp_smartcardlogon = new KeyPurposeId(new ASN1ObjectIdentifier("1.3.6.1.4.1.311.20.2.2"));
+
+ private ASN1ObjectIdentifier id;
+
+ private KeyPurposeId(ASN1ObjectIdentifier id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * @deprecated use getInstance and an OID or one of the constants above.
+ * @param id string representation of an OID.
+ */
+ public KeyPurposeId(String id)
+ {
+ this(new ASN1ObjectIdentifier(id));
+ }
+
+ public static KeyPurposeId getInstance(Object o)
+ {
+ if (o instanceof KeyPurposeId)
+ {
+ return (KeyPurposeId)o;
+ }
+ else if (o != null)
+ {
+ return new KeyPurposeId(ASN1ObjectIdentifier.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return id;
+ }
+
+ public String getId()
+ {
+ return id.getId();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java b/core/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java
new file mode 100644
index 00000000..2943c0b6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java
@@ -0,0 +1,102 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DERBitString;
+
+/**
+ * The KeyUsage object.
+ * <pre>
+ * id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 }
+ *
+ * KeyUsage ::= BIT STRING {
+ * digitalSignature (0),
+ * nonRepudiation (1),
+ * keyEncipherment (2),
+ * dataEncipherment (3),
+ * keyAgreement (4),
+ * keyCertSign (5),
+ * cRLSign (6),
+ * encipherOnly (7),
+ * decipherOnly (8) }
+ * </pre>
+ */
+public class KeyUsage
+ extends ASN1Object
+{
+ public static final int digitalSignature = (1 << 7);
+ public static final int nonRepudiation = (1 << 6);
+ public static final int keyEncipherment = (1 << 5);
+ public static final int dataEncipherment = (1 << 4);
+ public static final int keyAgreement = (1 << 3);
+ public static final int keyCertSign = (1 << 2);
+ public static final int cRLSign = (1 << 1);
+ public static final int encipherOnly = (1 << 0);
+ public static final int decipherOnly = (1 << 15);
+
+ private DERBitString bitString;
+
+ public static KeyUsage getInstance(Object obj) // needs to be DERBitString for other VMs
+ {
+ if (obj instanceof KeyUsage)
+ {
+ return (KeyUsage)obj;
+ }
+ else if (obj != null)
+ {
+ return new KeyUsage(DERBitString.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static KeyUsage fromExtensions(Extensions extensions)
+ {
+ return KeyUsage.getInstance(extensions.getExtensionParsedValue(Extension.keyUsage));
+ }
+
+ /**
+ * Basic constructor.
+ *
+ * @param usage - the bitwise OR of the Key Usage flags giving the
+ * allowed uses for the key.
+ * e.g. (KeyUsage.keyEncipherment | KeyUsage.dataEncipherment)
+ */
+ public KeyUsage(
+ int usage)
+ {
+ this.bitString = new DERBitString(usage);
+ }
+
+ private KeyUsage(
+ DERBitString bitString)
+ {
+ this.bitString = bitString;
+ }
+
+ public byte[] getBytes()
+ {
+ return bitString.getBytes();
+ }
+
+ public int getPadBits()
+ {
+ return bitString.getPadBits();
+ }
+
+ public String toString()
+ {
+ byte[] data = bitString.getBytes();
+
+ if (data.length == 1)
+ {
+ return "KeyUsage: 0x" + Integer.toHexString(data[0] & 0xff);
+ }
+ return "KeyUsage: 0x" + Integer.toHexString((data[1] & 0xff) << 8 | (data[0] & 0xff));
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return bitString;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/NameConstraints.java b/core/src/main/java/org/bouncycastle/asn1/x509/NameConstraints.java
new file mode 100644
index 00000000..0a923a85
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/NameConstraints.java
@@ -0,0 +1,118 @@
+package org.bouncycastle.asn1.x509;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class NameConstraints
+ extends ASN1Object
+{
+ private GeneralSubtree[] permitted, excluded;
+
+ public static NameConstraints getInstance(Object obj)
+ {
+ if (obj instanceof NameConstraints)
+ {
+ return (NameConstraints)obj;
+ }
+ if (obj != null)
+ {
+ return new NameConstraints(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private NameConstraints(ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+ while (e.hasMoreElements())
+ {
+ ASN1TaggedObject o = ASN1TaggedObject.getInstance(e.nextElement());
+ switch (o.getTagNo())
+ {
+ case 0:
+ permitted = createArray(ASN1Sequence.getInstance(o, false));
+ break;
+ case 1:
+ excluded = createArray(ASN1Sequence.getInstance(o, false));
+ break;
+ }
+ }
+ }
+
+ /**
+ * Constructor from a given details.
+ *
+ * <p>
+ * permitted and excluded are arrays of GeneralSubtree objects.
+ *
+ * @param permitted
+ * Permitted subtrees
+ * @param excluded
+ * Excludes subtrees
+ */
+ public NameConstraints(
+ GeneralSubtree[] permitted,
+ GeneralSubtree[] excluded)
+ {
+ if (permitted != null)
+ {
+ this.permitted = permitted;
+ }
+
+ if (excluded != null)
+ {
+ this.excluded = excluded;
+ }
+ }
+
+ private GeneralSubtree[] createArray(ASN1Sequence subtree)
+ {
+ GeneralSubtree[] ar = new GeneralSubtree[subtree.size()];
+
+ for (int i = 0; i != ar.length; i++)
+ {
+ ar[i] = GeneralSubtree.getInstance(subtree.getObjectAt(i));
+ }
+
+ return ar;
+ }
+
+ public GeneralSubtree[] getPermittedSubtrees()
+ {
+ return permitted;
+ }
+
+ public GeneralSubtree[] getExcludedSubtrees()
+ {
+ return excluded;
+ }
+
+ /*
+ * NameConstraints ::= SEQUENCE { permittedSubtrees [0] GeneralSubtrees
+ * OPTIONAL, excludedSubtrees [1] GeneralSubtrees OPTIONAL }
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (permitted != null)
+ {
+ v.add(new DERTaggedObject(false, 0, new DERSequence(permitted)));
+ }
+
+ if (excluded != null)
+ {
+ v.add(new DERTaggedObject(false, 1, new DERSequence(excluded)));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/NoticeReference.java b/core/src/main/java/org/bouncycastle/asn1/x509/NoticeReference.java
new file mode 100644
index 00000000..d46f524e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/NoticeReference.java
@@ -0,0 +1,170 @@
+package org.bouncycastle.asn1.x509;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * <code>NoticeReference</code> class, used in
+ * <code>CertificatePolicies</code> X509 V3 extensions
+ * (in policy qualifiers).
+ *
+ * <pre>
+ * NoticeReference ::= SEQUENCE {
+ * organization DisplayText,
+ * noticeNumbers SEQUENCE OF INTEGER }
+ *
+ * </pre>
+ *
+ * @see PolicyQualifierInfo
+ * @see PolicyInformation
+ */
+public class NoticeReference
+ extends ASN1Object
+{
+ private DisplayText organization;
+ private ASN1Sequence noticeNumbers;
+
+ private static ASN1EncodableVector convertVector(Vector numbers)
+ {
+ ASN1EncodableVector av = new ASN1EncodableVector();
+
+ Enumeration it = numbers.elements();
+
+ while (it.hasMoreElements())
+ {
+ Object o = it.nextElement();
+ ASN1Integer di;
+
+ if (o instanceof BigInteger)
+ {
+ di = new ASN1Integer((BigInteger)o);
+ }
+ else if (o instanceof Integer)
+ {
+ di = new ASN1Integer(((Integer)o).intValue());
+ }
+ else
+ {
+ throw new IllegalArgumentException();
+ }
+
+ av.add(di);
+ }
+ return av;
+ }
+
+ /**
+ * Creates a new <code>NoticeReference</code> instance.
+ *
+ * @param organization a <code>String</code> value
+ * @param numbers a <code>Vector</code> value
+ */
+ public NoticeReference(
+ String organization,
+ Vector numbers)
+ {
+ this(organization, convertVector(numbers));
+ }
+
+ /**
+ * Creates a new <code>NoticeReference</code> instance.
+ *
+ * @param organization a <code>String</code> value
+ * @param noticeNumbers an <code>ASN1EncodableVector</code> value
+ */
+ public NoticeReference(
+ String organization,
+ ASN1EncodableVector noticeNumbers)
+ {
+ this(new DisplayText(organization), noticeNumbers);
+ }
+
+ /**
+ * Creates a new <code>NoticeReference</code> instance.
+ *
+ * @param organization displayText
+ * @param noticeNumbers an <code>ASN1EncodableVector</code> value
+ */
+ public NoticeReference(
+ DisplayText organization,
+ ASN1EncodableVector noticeNumbers)
+ {
+ this.organization = organization;
+ this.noticeNumbers = new DERSequence(noticeNumbers);
+ }
+
+ /**
+ * Creates a new <code>NoticeReference</code> instance.
+ * <p>Useful for reconstructing a <code>NoticeReference</code>
+ * instance from its encodable/encoded form.
+ *
+ * @param as an <code>ASN1Sequence</code> value obtained from either
+ * calling @{link toASN1Primitive()} for a <code>NoticeReference</code>
+ * instance or from parsing it from a DER-encoded stream.
+ */
+ private NoticeReference(
+ ASN1Sequence as)
+ {
+ if (as.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + as.size());
+ }
+
+ organization = DisplayText.getInstance(as.getObjectAt(0));
+ noticeNumbers = ASN1Sequence.getInstance(as.getObjectAt(1));
+ }
+
+ public static NoticeReference getInstance(
+ Object as)
+ {
+ if (as instanceof NoticeReference)
+ {
+ return (NoticeReference)as;
+ }
+ else if (as != null)
+ {
+ return new NoticeReference(ASN1Sequence.getInstance(as));
+ }
+
+ return null;
+ }
+
+ public DisplayText getOrganization()
+ {
+ return organization;
+ }
+
+ public ASN1Integer[] getNoticeNumbers()
+ {
+ ASN1Integer[] tmp = new ASN1Integer[noticeNumbers.size()];
+
+ for (int i = 0; i != noticeNumbers.size(); i++)
+ {
+ tmp[i] = ASN1Integer.getInstance(noticeNumbers.getObjectAt(i));
+ }
+
+ return tmp;
+ }
+
+ /**
+ * Describe <code>toASN1Object</code> method here.
+ *
+ * @return a <code>ASN1Primitive</code> value
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector av = new ASN1EncodableVector();
+ av.add (organization);
+ av.add (noticeNumbers);
+ return new DERSequence (av);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/ObjectDigestInfo.java b/core/src/main/java/org/bouncycastle/asn1/x509/ObjectDigestInfo.java
new file mode 100644
index 00000000..c4668b76
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/ObjectDigestInfo.java
@@ -0,0 +1,190 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Enumerated;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * ObjectDigestInfo ASN.1 structure used in v2 attribute certificates.
+ *
+ * <pre>
+ *
+ * ObjectDigestInfo ::= SEQUENCE {
+ * digestedObjectType ENUMERATED {
+ * publicKey (0),
+ * publicKeyCert (1),
+ * otherObjectTypes (2) },
+ * -- otherObjectTypes MUST NOT
+ * -- be used in this profile
+ * otherObjectTypeID OBJECT IDENTIFIER OPTIONAL,
+ * digestAlgorithm AlgorithmIdentifier,
+ * objectDigest BIT STRING
+ * }
+ *
+ * </pre>
+ *
+ */
+public class ObjectDigestInfo
+ extends ASN1Object
+{
+ /**
+ * The public key is hashed.
+ */
+ public final static int publicKey = 0;
+
+ /**
+ * The public key certificate is hashed.
+ */
+ public final static int publicKeyCert = 1;
+
+ /**
+ * An other object is hashed.
+ */
+ public final static int otherObjectDigest = 2;
+
+ ASN1Enumerated digestedObjectType;
+
+ ASN1ObjectIdentifier otherObjectTypeID;
+
+ AlgorithmIdentifier digestAlgorithm;
+
+ DERBitString objectDigest;
+
+ public static ObjectDigestInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof ObjectDigestInfo)
+ {
+ return (ObjectDigestInfo)obj;
+ }
+
+ if (obj != null)
+ {
+ return new ObjectDigestInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static ObjectDigestInfo getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * Constructor from given details.
+ * <p>
+ * If <code>digestedObjectType</code> is not {@link #publicKeyCert} or
+ * {@link #publicKey} <code>otherObjectTypeID</code> must be given,
+ * otherwise it is ignored.
+ *
+ * @param digestedObjectType The digest object type.
+ * @param otherObjectTypeID The object type ID for
+ * <code>otherObjectDigest</code>.
+ * @param digestAlgorithm The algorithm identifier for the hash.
+ * @param objectDigest The hash value.
+ */
+ public ObjectDigestInfo(
+ int digestedObjectType,
+ ASN1ObjectIdentifier otherObjectTypeID,
+ AlgorithmIdentifier digestAlgorithm,
+ byte[] objectDigest)
+ {
+ this.digestedObjectType = new ASN1Enumerated(digestedObjectType);
+ if (digestedObjectType == otherObjectDigest)
+ {
+ this.otherObjectTypeID = otherObjectTypeID;
+ }
+
+ this.digestAlgorithm = digestAlgorithm;
+ this.objectDigest = new DERBitString(objectDigest);
+ }
+
+ private ObjectDigestInfo(
+ ASN1Sequence seq)
+ {
+ if (seq.size() > 4 || seq.size() < 3)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ digestedObjectType = ASN1Enumerated.getInstance(seq.getObjectAt(0));
+
+ int offset = 0;
+
+ if (seq.size() == 4)
+ {
+ otherObjectTypeID = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(1));
+ offset++;
+ }
+
+ digestAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1 + offset));
+
+ objectDigest = DERBitString.getInstance(seq.getObjectAt(2 + offset));
+ }
+
+ public ASN1Enumerated getDigestedObjectType()
+ {
+ return digestedObjectType;
+ }
+
+ public ASN1ObjectIdentifier getOtherObjectTypeID()
+ {
+ return otherObjectTypeID;
+ }
+
+ public AlgorithmIdentifier getDigestAlgorithm()
+ {
+ return digestAlgorithm;
+ }
+
+ public DERBitString getObjectDigest()
+ {
+ return objectDigest;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ *
+ * <pre>
+ *
+ * ObjectDigestInfo ::= SEQUENCE {
+ * digestedObjectType ENUMERATED {
+ * publicKey (0),
+ * publicKeyCert (1),
+ * otherObjectTypes (2) },
+ * -- otherObjectTypes MUST NOT
+ * -- be used in this profile
+ * otherObjectTypeID OBJECT IDENTIFIER OPTIONAL,
+ * digestAlgorithm AlgorithmIdentifier,
+ * objectDigest BIT STRING
+ * }
+ *
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(digestedObjectType);
+
+ if (otherObjectTypeID != null)
+ {
+ v.add(otherObjectTypeID);
+ }
+
+ v.add(digestAlgorithm);
+ v.add(objectDigest);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/PolicyInformation.java b/core/src/main/java/org/bouncycastle/asn1/x509/PolicyInformation.java
new file mode 100644
index 00000000..d1de26fc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/PolicyInformation.java
@@ -0,0 +1,87 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+public class PolicyInformation
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier policyIdentifier;
+ private ASN1Sequence policyQualifiers;
+
+ private PolicyInformation(
+ ASN1Sequence seq)
+ {
+ if (seq.size() < 1 || seq.size() > 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ policyIdentifier = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+
+ if (seq.size() > 1)
+ {
+ policyQualifiers = ASN1Sequence.getInstance(seq.getObjectAt(1));
+ }
+ }
+
+ public PolicyInformation(
+ ASN1ObjectIdentifier policyIdentifier)
+ {
+ this.policyIdentifier = policyIdentifier;
+ }
+
+ public PolicyInformation(
+ ASN1ObjectIdentifier policyIdentifier,
+ ASN1Sequence policyQualifiers)
+ {
+ this.policyIdentifier = policyIdentifier;
+ this.policyQualifiers = policyQualifiers;
+ }
+
+ public static PolicyInformation getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof PolicyInformation)
+ {
+ return (PolicyInformation)obj;
+ }
+
+ return new PolicyInformation(ASN1Sequence.getInstance(obj));
+ }
+
+ public ASN1ObjectIdentifier getPolicyIdentifier()
+ {
+ return policyIdentifier;
+ }
+
+ public ASN1Sequence getPolicyQualifiers()
+ {
+ return policyQualifiers;
+ }
+
+ /*
+ * PolicyInformation ::= SEQUENCE {
+ * policyIdentifier CertPolicyId,
+ * policyQualifiers SEQUENCE SIZE (1..MAX) OF
+ * PolicyQualifierInfo OPTIONAL }
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(policyIdentifier);
+
+ if (policyQualifiers != null)
+ {
+ v.add(policyQualifiers);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/PolicyMappings.java b/core/src/main/java/org/bouncycastle/asn1/x509/PolicyMappings.java
new file mode 100644
index 00000000..6afab95f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/PolicyMappings.java
@@ -0,0 +1,107 @@
+package org.bouncycastle.asn1.x509;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * PolicyMappings V3 extension, described in RFC3280.
+ * <pre>
+ * PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
+ * issuerDomainPolicy CertPolicyId,
+ * subjectDomainPolicy CertPolicyId }
+ * </pre>
+ *
+ * @see <a href="http://www.faqs.org/rfc/rfc3280.txt">RFC 3280, section 4.2.1.6</a>
+ */
+public class PolicyMappings
+ extends ASN1Object
+{
+ ASN1Sequence seq = null;
+
+ public static PolicyMappings getInstance(Object obj)
+ {
+ if (obj instanceof PolicyMappings)
+ {
+ return (PolicyMappings)obj;
+ }
+ if (obj != null)
+ {
+ return new PolicyMappings(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Creates a new <code>PolicyMappings</code> instance.
+ *
+ * @param seq an <code>ASN1Sequence</code> constructed as specified
+ * in RFC 3280
+ */
+ private PolicyMappings(ASN1Sequence seq)
+ {
+ this.seq = seq;
+ }
+
+ /**
+ * Creates a new <code>PolicyMappings</code> instance.
+ *
+ * @param mappings a <code>HashMap</code> value that maps
+ * <code>String</code> oids
+ * to other <code>String</code> oids.
+ * @deprecated use CertPolicyId constructors.
+ */
+ public PolicyMappings(Hashtable mappings)
+ {
+ ASN1EncodableVector dev = new ASN1EncodableVector();
+ Enumeration it = mappings.keys();
+
+ while (it.hasMoreElements())
+ {
+ String idp = (String)it.nextElement();
+ String sdp = (String)mappings.get(idp);
+ ASN1EncodableVector dv = new ASN1EncodableVector();
+ dv.add(new ASN1ObjectIdentifier(idp));
+ dv.add(new ASN1ObjectIdentifier(sdp));
+ dev.add(new DERSequence(dv));
+ }
+
+ seq = new DERSequence(dev);
+ }
+
+ public PolicyMappings(CertPolicyId issuerDomainPolicy, CertPolicyId subjectDomainPolicy)
+ {
+ ASN1EncodableVector dv = new ASN1EncodableVector();
+ dv.add(issuerDomainPolicy);
+ dv.add(subjectDomainPolicy);
+
+ seq = new DERSequence(new DERSequence(dv));
+ }
+
+ public PolicyMappings(CertPolicyId[] issuerDomainPolicy, CertPolicyId[] subjectDomainPolicy)
+ {
+ ASN1EncodableVector dev = new ASN1EncodableVector();
+
+ for (int i = 0; i != issuerDomainPolicy.length; i++)
+ {
+ ASN1EncodableVector dv = new ASN1EncodableVector();
+ dv.add(issuerDomainPolicy[i]);
+ dv.add(subjectDomainPolicy[i]);
+ dev.add(new DERSequence(dv));
+ }
+
+ seq = new DERSequence(dev);
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return seq;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierId.java b/core/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierId.java
new file mode 100644
index 00000000..82835f6a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierId.java
@@ -0,0 +1,31 @@
+
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+/**
+ * PolicyQualifierId, used in the CertificatePolicies
+ * X509V3 extension.
+ *
+ * <pre>
+ * id-qt OBJECT IDENTIFIER ::= { id-pkix 2 }
+ * id-qt-cps OBJECT IDENTIFIER ::= { id-qt 1 }
+ * id-qt-unotice OBJECT IDENTIFIER ::= { id-qt 2 }
+ * PolicyQualifierId ::=
+ * OBJECT IDENTIFIER (id-qt-cps | id-qt-unotice)
+ * </pre>
+ */
+public class PolicyQualifierId extends ASN1ObjectIdentifier
+{
+ private static final String id_qt = "1.3.6.1.5.5.7.2";
+
+ private PolicyQualifierId(String id)
+ {
+ super(id);
+ }
+
+ public static final PolicyQualifierId id_qt_cps =
+ new PolicyQualifierId(id_qt + ".1");
+ public static final PolicyQualifierId id_qt_unotice =
+ new PolicyQualifierId(id_qt + ".2");
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java b/core/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java
new file mode 100644
index 00000000..295accf3
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java
@@ -0,0 +1,114 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * Policy qualifiers, used in the X509V3 CertificatePolicies
+ * extension.
+ *
+ * <pre>
+ * PolicyQualifierInfo ::= SEQUENCE {
+ * policyQualifierId PolicyQualifierId,
+ * qualifier ANY DEFINED BY policyQualifierId }
+ * </pre>
+ */
+public class PolicyQualifierInfo
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier policyQualifierId;
+ private ASN1Encodable qualifier;
+
+ /**
+ * Creates a new <code>PolicyQualifierInfo</code> instance.
+ *
+ * @param policyQualifierId a <code>PolicyQualifierId</code> value
+ * @param qualifier the qualifier, defined by the above field.
+ */
+ public PolicyQualifierInfo(
+ ASN1ObjectIdentifier policyQualifierId,
+ ASN1Encodable qualifier)
+ {
+ this.policyQualifierId = policyQualifierId;
+ this.qualifier = qualifier;
+ }
+
+ /**
+ * Creates a new <code>PolicyQualifierInfo</code> containing a
+ * cPSuri qualifier.
+ *
+ * @param cps the CPS (certification practice statement) uri as a
+ * <code>String</code>.
+ */
+ public PolicyQualifierInfo(
+ String cps)
+ {
+ policyQualifierId = PolicyQualifierId.id_qt_cps;
+ qualifier = new DERIA5String (cps);
+ }
+
+ /**
+ * Creates a new <code>PolicyQualifierInfo</code> instance.
+ *
+ * @param as <code>PolicyQualifierInfo</code> X509 structure
+ * encoded as an ASN1Sequence.
+ */
+ public PolicyQualifierInfo(
+ ASN1Sequence as)
+ {
+ if (as.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + as.size());
+ }
+
+ policyQualifierId = ASN1ObjectIdentifier.getInstance(as.getObjectAt(0));
+ qualifier = as.getObjectAt(1);
+ }
+
+ public static PolicyQualifierInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof PolicyQualifierInfo)
+ {
+ return (PolicyQualifierInfo)obj;
+ }
+ else if (obj != null)
+ {
+ return new PolicyQualifierInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+
+ public ASN1ObjectIdentifier getPolicyQualifierId()
+ {
+ return policyQualifierId;
+ }
+
+ public ASN1Encodable getQualifier()
+ {
+ return qualifier;
+ }
+
+ /**
+ * Returns a DER-encodable representation of this instance.
+ *
+ * @return a <code>ASN1Primitive</code> value
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector dev = new ASN1EncodableVector();
+ dev.add(policyQualifierId);
+ dev.add(qualifier);
+
+ return new DERSequence(dev);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/PrivateKeyUsagePeriod.java b/core/src/main/java/org/bouncycastle/asn1/x509/PrivateKeyUsagePeriod.java
new file mode 100644
index 00000000..81669260
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/PrivateKeyUsagePeriod.java
@@ -0,0 +1,84 @@
+package org.bouncycastle.asn1.x509;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERGeneralizedTime;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * <pre>
+ * PrivateKeyUsagePeriod ::= SEQUENCE {
+ * notBefore [0] GeneralizedTime OPTIONAL,
+ * notAfter [1] GeneralizedTime OPTIONAL }
+ * </pre>
+ */
+public class PrivateKeyUsagePeriod
+ extends ASN1Object
+{
+ public static PrivateKeyUsagePeriod getInstance(Object obj)
+ {
+ if (obj instanceof PrivateKeyUsagePeriod)
+ {
+ return (PrivateKeyUsagePeriod)obj;
+ }
+
+ if (obj != null)
+ {
+ return new PrivateKeyUsagePeriod(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private DERGeneralizedTime _notBefore, _notAfter;
+
+ private PrivateKeyUsagePeriod(ASN1Sequence seq)
+ {
+ Enumeration en = seq.getObjects();
+ while (en.hasMoreElements())
+ {
+ ASN1TaggedObject tObj = (ASN1TaggedObject)en.nextElement();
+
+ if (tObj.getTagNo() == 0)
+ {
+ _notBefore = DERGeneralizedTime.getInstance(tObj, false);
+ }
+ else if (tObj.getTagNo() == 1)
+ {
+ _notAfter = DERGeneralizedTime.getInstance(tObj, false);
+ }
+ }
+ }
+
+ public DERGeneralizedTime getNotBefore()
+ {
+ return _notBefore;
+ }
+
+ public DERGeneralizedTime getNotAfter()
+ {
+ return _notAfter;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (_notBefore != null)
+ {
+ v.add(new DERTaggedObject(false, 0, _notBefore));
+ }
+ if (_notAfter != null)
+ {
+ v.add(new DERTaggedObject(false, 1, _notAfter));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/RSAPublicKeyStructure.java b/core/src/main/java/org/bouncycastle/asn1/x509/RSAPublicKeyStructure.java
new file mode 100644
index 00000000..91c87256
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/RSAPublicKeyStructure.java
@@ -0,0 +1,98 @@
+package org.bouncycastle.asn1.x509;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * @deprecated use org.bouncycastle.asn1.pkcs.RSAPublicKey
+ */
+public class RSAPublicKeyStructure
+ extends ASN1Object
+{
+ private BigInteger modulus;
+ private BigInteger publicExponent;
+
+ public static RSAPublicKeyStructure getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static RSAPublicKeyStructure getInstance(
+ Object obj)
+ {
+ if(obj == null || obj instanceof RSAPublicKeyStructure)
+ {
+ return (RSAPublicKeyStructure)obj;
+ }
+
+ if(obj instanceof ASN1Sequence)
+ {
+ return new RSAPublicKeyStructure((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid RSAPublicKeyStructure: " + obj.getClass().getName());
+ }
+
+ public RSAPublicKeyStructure(
+ BigInteger modulus,
+ BigInteger publicExponent)
+ {
+ this.modulus = modulus;
+ this.publicExponent = publicExponent;
+ }
+
+ public RSAPublicKeyStructure(
+ ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ Enumeration e = seq.getObjects();
+
+ modulus = ASN1Integer.getInstance(e.nextElement()).getPositiveValue();
+ publicExponent = ASN1Integer.getInstance(e.nextElement()).getPositiveValue();
+ }
+
+ public BigInteger getModulus()
+ {
+ return modulus;
+ }
+
+ public BigInteger getPublicExponent()
+ {
+ return publicExponent;
+ }
+
+ /**
+ * This outputs the key in PKCS1v2 format.
+ * <pre>
+ * RSAPublicKey ::= SEQUENCE {
+ * modulus INTEGER, -- n
+ * publicExponent INTEGER, -- e
+ * }
+ * </pre>
+ * <p>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new ASN1Integer(getModulus()));
+ v.add(new ASN1Integer(getPublicExponent()));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/ReasonFlags.java b/core/src/main/java/org/bouncycastle/asn1/x509/ReasonFlags.java
new file mode 100644
index 00000000..612e2c5e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/ReasonFlags.java
@@ -0,0 +1,85 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.DERBitString;
+
+/**
+ * The ReasonFlags object.
+ * <pre>
+ * ReasonFlags ::= BIT STRING {
+ * unused (0),
+ * keyCompromise (1),
+ * cACompromise (2),
+ * affiliationChanged (3),
+ * superseded (4),
+ * cessationOfOperation (5),
+ * certificateHold (6),
+ * privilegeWithdrawn (7),
+ * aACompromise (8) }
+ * </pre>
+ */
+public class ReasonFlags
+ extends DERBitString
+{
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int UNUSED = (1 << 7);
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int KEY_COMPROMISE = (1 << 6);
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int CA_COMPROMISE = (1 << 5);
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int AFFILIATION_CHANGED = (1 << 4);
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int SUPERSEDED = (1 << 3);
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int CESSATION_OF_OPERATION = (1 << 2);
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int CERTIFICATE_HOLD = (1 << 1);
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int PRIVILEGE_WITHDRAWN = (1 << 0);
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int AA_COMPROMISE = (1 << 15);
+
+ public static final int unused = (1 << 7);
+ public static final int keyCompromise = (1 << 6);
+ public static final int cACompromise = (1 << 5);
+ public static final int affiliationChanged = (1 << 4);
+ public static final int superseded = (1 << 3);
+ public static final int cessationOfOperation = (1 << 2);
+ public static final int certificateHold = (1 << 1);
+ public static final int privilegeWithdrawn = (1 << 0);
+ public static final int aACompromise = (1 << 15);
+
+ /**
+ * @param reasons - the bitwise OR of the Key Reason flags giving the
+ * allowed uses for the key.
+ */
+ public ReasonFlags(
+ int reasons)
+ {
+ super(getBytes(reasons), getPadBits(reasons));
+ }
+
+ public ReasonFlags(
+ DERBitString reasons)
+ {
+ super(reasons.getBytes(), reasons.getPadBits());
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/RoleSyntax.java b/core/src/main/java/org/bouncycastle/asn1/x509/RoleSyntax.java
new file mode 100644
index 00000000..7558c12d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/RoleSyntax.java
@@ -0,0 +1,237 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1String;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * Implementation of the RoleSyntax object as specified by the RFC3281.
+ *
+ * <pre>
+ * RoleSyntax ::= SEQUENCE {
+ * roleAuthority [0] GeneralNames OPTIONAL,
+ * roleName [1] GeneralName
+ * }
+ * </pre>
+ */
+public class RoleSyntax
+ extends ASN1Object
+{
+ private GeneralNames roleAuthority;
+ private GeneralName roleName;
+
+ /**
+ * RoleSyntax factory method.
+ * @param obj the object used to construct an instance of <code>
+ * RoleSyntax</code>. It must be an instance of <code>RoleSyntax
+ * </code> or <code>ASN1Sequence</code>.
+ * @return the instance of <code>RoleSyntax</code> built from the
+ * supplied object.
+ * @throws java.lang.IllegalArgumentException if the object passed
+ * to the factory is not an instance of <code>RoleSyntax</code> or
+ * <code>ASN1Sequence</code>.
+ */
+ public static RoleSyntax getInstance(
+ Object obj)
+ {
+
+ if (obj instanceof RoleSyntax)
+ {
+ return (RoleSyntax)obj;
+ }
+ else if (obj != null)
+ {
+ return new RoleSyntax(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Constructor.
+ * @param roleAuthority the role authority of this RoleSyntax.
+ * @param roleName the role name of this RoleSyntax.
+ */
+ public RoleSyntax(
+ GeneralNames roleAuthority,
+ GeneralName roleName)
+ {
+ if(roleName == null ||
+ roleName.getTagNo() != GeneralName.uniformResourceIdentifier ||
+ ((ASN1String)roleName.getName()).getString().equals(""))
+ {
+ throw new IllegalArgumentException("the role name MUST be non empty and MUST " +
+ "use the URI option of GeneralName");
+ }
+ this.roleAuthority = roleAuthority;
+ this.roleName = roleName;
+ }
+
+ /**
+ * Constructor. Invoking this constructor is the same as invoking
+ * <code>new RoleSyntax(null, roleName)</code>.
+ * @param roleName the role name of this RoleSyntax.
+ */
+ public RoleSyntax(
+ GeneralName roleName)
+ {
+ this(null, roleName);
+ }
+
+ /**
+ * Utility constructor. Takes a <code>String</code> argument representing
+ * the role name, builds a <code>GeneralName</code> to hold the role name
+ * and calls the constructor that takes a <code>GeneralName</code>.
+ * @param roleName
+ */
+ public RoleSyntax(
+ String roleName)
+ {
+ this(new GeneralName(GeneralName.uniformResourceIdentifier,
+ (roleName == null)? "": roleName));
+ }
+
+ /**
+ * Constructor that builds an instance of <code>RoleSyntax</code> by
+ * extracting the encoded elements from the <code>ASN1Sequence</code>
+ * object supplied.
+ * @param seq an instance of <code>ASN1Sequence</code> that holds
+ * the encoded elements used to build this <code>RoleSyntax</code>.
+ */
+ private RoleSyntax(
+ ASN1Sequence seq)
+ {
+ if (seq.size() < 1 || seq.size() > 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ for (int i = 0; i != seq.size(); i++)
+ {
+ ASN1TaggedObject taggedObject = ASN1TaggedObject.getInstance(seq.getObjectAt(i));
+ switch (taggedObject.getTagNo())
+ {
+ case 0:
+ roleAuthority = GeneralNames.getInstance(taggedObject, false);
+ break;
+ case 1:
+ roleName = GeneralName.getInstance(taggedObject, true);
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown tag in RoleSyntax");
+ }
+ }
+ }
+
+ /**
+ * Gets the role authority of this RoleSyntax.
+ * @return an instance of <code>GeneralNames</code> holding the
+ * role authority of this RoleSyntax.
+ */
+ public GeneralNames getRoleAuthority()
+ {
+ return this.roleAuthority;
+ }
+
+ /**
+ * Gets the role name of this RoleSyntax.
+ * @return an instance of <code>GeneralName</code> holding the
+ * role name of this RoleSyntax.
+ */
+ public GeneralName getRoleName()
+ {
+ return this.roleName;
+ }
+
+ /**
+ * Gets the role name as a <code>java.lang.String</code> object.
+ * @return the role name of this RoleSyntax represented as a
+ * <code>java.lang.String</code> object.
+ */
+ public String getRoleNameAsString()
+ {
+ ASN1String str = (ASN1String)this.roleName.getName();
+
+ return str.getString();
+ }
+
+ /**
+ * Gets the role authority as a <code>String[]</code> object.
+ * @return the role authority of this RoleSyntax represented as a
+ * <code>String[]</code> array.
+ */
+ public String[] getRoleAuthorityAsString()
+ {
+ if(roleAuthority == null)
+ {
+ return new String[0];
+ }
+
+ GeneralName[] names = roleAuthority.getNames();
+ String[] namesString = new String[names.length];
+ for(int i = 0; i < names.length; i++)
+ {
+ ASN1Encodable value = names[i].getName();
+ if(value instanceof ASN1String)
+ {
+ namesString[i] = ((ASN1String)value).getString();
+ }
+ else
+ {
+ namesString[i] = value.toString();
+ }
+ }
+ return namesString;
+ }
+
+ /**
+ * Implementation of the method <code>toASN1Object</code> as
+ * required by the superclass <code>ASN1Encodable</code>.
+ *
+ * <pre>
+ * RoleSyntax ::= SEQUENCE {
+ * roleAuthority [0] GeneralNames OPTIONAL,
+ * roleName [1] GeneralName
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ if(this.roleAuthority != null)
+ {
+ v.add(new DERTaggedObject(false, 0, roleAuthority));
+ }
+ v.add(new DERTaggedObject(true, 1, roleName));
+
+ return new DERSequence(v);
+ }
+
+ public String toString()
+ {
+ StringBuffer buff = new StringBuffer("Name: " + this.getRoleNameAsString() +
+ " - Auth: ");
+ if(this.roleAuthority == null || roleAuthority.getNames().length == 0)
+ {
+ buff.append("N/A");
+ }
+ else
+ {
+ String[] names = this.getRoleAuthorityAsString();
+ buff.append('[').append(names[0]);
+ for(int i = 1; i < names.length; i++)
+ {
+ buff.append(", ").append(names[i]);
+ }
+ buff.append(']');
+ }
+ return buff.toString();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/SubjectDirectoryAttributes.java b/core/src/main/java/org/bouncycastle/asn1/x509/SubjectDirectoryAttributes.java
new file mode 100644
index 00000000..95918024
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/SubjectDirectoryAttributes.java
@@ -0,0 +1,144 @@
+package org.bouncycastle.asn1.x509;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * This extension may contain further X.500 attributes of the subject. See also
+ * RFC 3039.
+ *
+ * <pre>
+ * SubjectDirectoryAttributes ::= Attributes
+ * Attributes ::= SEQUENCE SIZE (1..MAX) OF Attribute
+ * Attribute ::= SEQUENCE
+ * {
+ * type AttributeType
+ * values SET OF AttributeValue
+ * }
+ *
+ * AttributeType ::= OBJECT IDENTIFIER
+ * AttributeValue ::= ANY DEFINED BY AttributeType
+ * </pre>
+ *
+ * @see org.bouncycastle.asn1.x500.style.BCStyle for AttributeType ObjectIdentifiers.
+ */
+public class SubjectDirectoryAttributes
+ extends ASN1Object
+{
+ private Vector attributes = new Vector();
+
+ public static SubjectDirectoryAttributes getInstance(
+ Object obj)
+ {
+ if (obj instanceof SubjectDirectoryAttributes)
+ {
+ return (SubjectDirectoryAttributes)obj;
+ }
+
+ if (obj != null)
+ {
+ return new SubjectDirectoryAttributes(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Constructor from ASN1Sequence.
+ *
+ * The sequence is of type SubjectDirectoryAttributes:
+ *
+ * <pre>
+ * SubjectDirectoryAttributes ::= Attributes
+ * Attributes ::= SEQUENCE SIZE (1..MAX) OF Attribute
+ * Attribute ::= SEQUENCE
+ * {
+ * type AttributeType
+ * values SET OF AttributeValue
+ * }
+ *
+ * AttributeType ::= OBJECT IDENTIFIER
+ * AttributeValue ::= ANY DEFINED BY AttributeType
+ * </pre>
+ *
+ * @param seq
+ * The ASN.1 sequence.
+ */
+ private SubjectDirectoryAttributes(ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ ASN1Sequence s = ASN1Sequence.getInstance(e.nextElement());
+ attributes.addElement(Attribute.getInstance(s));
+ }
+ }
+
+ /**
+ * Constructor from a vector of attributes.
+ *
+ * The vector consists of attributes of type {@link Attribute Attribute}
+ *
+ * @param attributes
+ * The attributes.
+ *
+ */
+ public SubjectDirectoryAttributes(Vector attributes)
+ {
+ Enumeration e = attributes.elements();
+
+ while (e.hasMoreElements())
+ {
+ this.attributes.addElement(e.nextElement());
+ }
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ *
+ * Returns:
+ *
+ * <pre>
+ * SubjectDirectoryAttributes ::= Attributes
+ * Attributes ::= SEQUENCE SIZE (1..MAX) OF Attribute
+ * Attribute ::= SEQUENCE
+ * {
+ * type AttributeType
+ * values SET OF AttributeValue
+ * }
+ *
+ * AttributeType ::= OBJECT IDENTIFIER
+ * AttributeValue ::= ANY DEFINED BY AttributeType
+ * </pre>
+ *
+ * @return a ASN1Primitive
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+ Enumeration e = attributes.elements();
+
+ while (e.hasMoreElements())
+ {
+
+ vec.add((Attribute)e.nextElement());
+ }
+
+ return new DERSequence(vec);
+ }
+
+ /**
+ * @return Returns the attributes.
+ */
+ public Vector getAttributes()
+ {
+ return attributes;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/SubjectKeyIdentifier.java b/core/src/main/java/org/bouncycastle/asn1/x509/SubjectKeyIdentifier.java
new file mode 100644
index 00000000..bcaf5604
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/SubjectKeyIdentifier.java
@@ -0,0 +1,135 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+
+/**
+ * The SubjectKeyIdentifier object.
+ * <pre>
+ * SubjectKeyIdentifier::= OCTET STRING
+ * </pre>
+ */
+public class SubjectKeyIdentifier
+ extends ASN1Object
+{
+ private byte[] keyidentifier;
+
+ public static SubjectKeyIdentifier getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1OctetString.getInstance(obj, explicit));
+ }
+
+ public static SubjectKeyIdentifier getInstance(
+ Object obj)
+ {
+ if (obj instanceof SubjectKeyIdentifier)
+ {
+ return (SubjectKeyIdentifier)obj;
+ }
+ else if (obj != null)
+ {
+ return new SubjectKeyIdentifier(ASN1OctetString.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static SubjectKeyIdentifier fromExtensions(Extensions extensions)
+ {
+ return SubjectKeyIdentifier.getInstance(extensions.getExtensionParsedValue(Extension.subjectKeyIdentifier));
+ }
+
+ public SubjectKeyIdentifier(
+ byte[] keyid)
+ {
+ this.keyidentifier = keyid;
+ }
+
+ protected SubjectKeyIdentifier(
+ ASN1OctetString keyid)
+ {
+ this.keyidentifier = keyid.getOctets();
+ }
+
+ public byte[] getKeyIdentifier()
+ {
+ return keyidentifier;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return new DEROctetString(keyidentifier);
+ }
+
+
+ /**
+ * Calculates the keyidentifier using a SHA1 hash over the BIT STRING
+ * from SubjectPublicKeyInfo as defined in RFC3280.
+ *
+ * @param spki the subject public key info.
+ * @deprecated
+ */
+ public SubjectKeyIdentifier(
+ SubjectPublicKeyInfo spki)
+ {
+ this.keyidentifier = getDigest(spki);
+ }
+
+ /**
+ * Return a RFC 3280 type 1 key identifier. As in:
+ * <pre>
+ * (1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the
+ * value of the BIT STRING subjectPublicKey (excluding the tag,
+ * length, and number of unused bits).
+ * </pre>
+ * @param keyInfo the key info object containing the subjectPublicKey field.
+ * @return the key identifier.
+ * @deprecated use org.bouncycastle.cert.X509ExtensionUtils.createSubjectKeyIdentifier
+ */
+ public static SubjectKeyIdentifier createSHA1KeyIdentifier(SubjectPublicKeyInfo keyInfo)
+ {
+ return new SubjectKeyIdentifier(keyInfo);
+ }
+
+ /**
+ * Return a RFC 3280 type 2 key identifier. As in:
+ * <pre>
+ * (2) The keyIdentifier is composed of a four bit type field with
+ * the value 0100 followed by the least significant 60 bits of the
+ * SHA-1 hash of the value of the BIT STRING subjectPublicKey.
+ * </pre>
+ * @param keyInfo the key info object containing the subjectPublicKey field.
+ * @return the key identifier.
+ * @deprecated use org.bouncycastle.cert.X509ExtensionUtils.createTruncatedSubjectKeyIdentifier
+ */
+ public static SubjectKeyIdentifier createTruncatedSHA1KeyIdentifier(SubjectPublicKeyInfo keyInfo)
+ {
+ byte[] dig = getDigest(keyInfo);
+ byte[] id = new byte[8];
+
+ System.arraycopy(dig, dig.length - 8, id, 0, id.length);
+
+ id[0] &= 0x0f;
+ id[0] |= 0x40;
+
+ return new SubjectKeyIdentifier(id);
+ }
+
+ private static byte[] getDigest(SubjectPublicKeyInfo spki)
+ {
+ Digest digest = new SHA1Digest();
+ byte[] resBuf = new byte[digest.getDigestSize()];
+
+ byte[] bytes = spki.getPublicKeyData().getBytes();
+ digest.update(bytes, 0, bytes.length);
+ digest.doFinal(resBuf, 0);
+ return resBuf;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java b/core/src/main/java/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java
new file mode 100644
index 00000000..9e09cd72
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java
@@ -0,0 +1,156 @@
+package org.bouncycastle.asn1.x509;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * The object that contains the public key stored in a certficate.
+ * <p>
+ * The getEncoded() method in the public keys in the JCE produces a DER
+ * encoded one of these.
+ */
+public class SubjectPublicKeyInfo
+ extends ASN1Object
+{
+ private AlgorithmIdentifier algId;
+ private DERBitString keyData;
+
+ public static SubjectPublicKeyInfo getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static SubjectPublicKeyInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof SubjectPublicKeyInfo)
+ {
+ return (SubjectPublicKeyInfo)obj;
+ }
+ else if (obj != null)
+ {
+ return new SubjectPublicKeyInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public SubjectPublicKeyInfo(
+ AlgorithmIdentifier algId,
+ ASN1Encodable publicKey)
+ throws IOException
+ {
+ this.keyData = new DERBitString(publicKey);
+ this.algId = algId;
+ }
+
+ public SubjectPublicKeyInfo(
+ AlgorithmIdentifier algId,
+ byte[] publicKey)
+ {
+ this.keyData = new DERBitString(publicKey);
+ this.algId = algId;
+ }
+
+ public SubjectPublicKeyInfo(
+ ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ Enumeration e = seq.getObjects();
+
+ this.algId = AlgorithmIdentifier.getInstance(e.nextElement());
+ this.keyData = DERBitString.getInstance(e.nextElement());
+ }
+
+ public AlgorithmIdentifier getAlgorithm()
+ {
+ return algId;
+ }
+
+ /**
+ * @deprecated use getAlgorithm()
+ * @return alg ID.
+ */
+ public AlgorithmIdentifier getAlgorithmId()
+ {
+ return algId;
+ }
+
+ /**
+ * for when the public key is an encoded object - if the bitstring
+ * can't be decoded this routine throws an IOException.
+ *
+ * @exception IOException - if the bit string doesn't represent a DER
+ * encoded object.
+ * @return the public key as an ASN.1 primitive.
+ */
+ public ASN1Primitive parsePublicKey()
+ throws IOException
+ {
+ ASN1InputStream aIn = new ASN1InputStream(keyData.getBytes());
+
+ return aIn.readObject();
+ }
+
+ /**
+ * for when the public key is an encoded object - if the bitstring
+ * can't be decoded this routine throws an IOException.
+ *
+ * @exception IOException - if the bit string doesn't represent a DER
+ * encoded object.
+ * @deprecated use parsePublicKey
+ * @return the public key as an ASN.1 primitive.
+ */
+ public ASN1Primitive getPublicKey()
+ throws IOException
+ {
+ ASN1InputStream aIn = new ASN1InputStream(keyData.getBytes());
+
+ return aIn.readObject();
+ }
+
+ /**
+ * for when the public key is raw bits.
+ *
+ * @return the public key as the raw bit string...
+ */
+ public DERBitString getPublicKeyData()
+ {
+ return keyData;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * SubjectPublicKeyInfo ::= SEQUENCE {
+ * algorithm AlgorithmIdentifier,
+ * publicKey BIT STRING }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(algId);
+ v.add(keyData);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/TBSCertList.java b/core/src/main/java/org/bouncycastle/asn1/x509/TBSCertList.java
new file mode 100644
index 00000000..ce657a76
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/TBSCertList.java
@@ -0,0 +1,309 @@
+package org.bouncycastle.asn1.x509;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERGeneralizedTime;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.DERUTCTime;
+import org.bouncycastle.asn1.x500.X500Name;
+
+/**
+ * PKIX RFC-2459 - TBSCertList object.
+ * <pre>
+ * TBSCertList ::= SEQUENCE {
+ * version Version OPTIONAL,
+ * -- if present, shall be v2
+ * signature AlgorithmIdentifier,
+ * issuer Name,
+ * thisUpdate Time,
+ * nextUpdate Time OPTIONAL,
+ * revokedCertificates SEQUENCE OF SEQUENCE {
+ * userCertificate CertificateSerialNumber,
+ * revocationDate Time,
+ * crlEntryExtensions Extensions OPTIONAL
+ * -- if present, shall be v2
+ * } OPTIONAL,
+ * crlExtensions [0] EXPLICIT Extensions OPTIONAL
+ * -- if present, shall be v2
+ * }
+ * </pre>
+ */
+public class TBSCertList
+ extends ASN1Object
+{
+ public static class CRLEntry
+ extends ASN1Object
+ {
+ ASN1Sequence seq;
+
+ Extensions crlEntryExtensions;
+
+ private CRLEntry(
+ ASN1Sequence seq)
+ {
+ if (seq.size() < 2 || seq.size() > 3)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ this.seq = seq;
+ }
+
+ public static CRLEntry getInstance(Object o)
+ {
+ if (o instanceof CRLEntry)
+ {
+ return ((CRLEntry)o);
+ }
+ else if (o != null)
+ {
+ return new CRLEntry(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public ASN1Integer getUserCertificate()
+ {
+ return ASN1Integer.getInstance(seq.getObjectAt(0));
+ }
+
+ public Time getRevocationDate()
+ {
+ return Time.getInstance(seq.getObjectAt(1));
+ }
+
+ public Extensions getExtensions()
+ {
+ if (crlEntryExtensions == null && seq.size() == 3)
+ {
+ crlEntryExtensions = Extensions.getInstance(seq.getObjectAt(2));
+ }
+
+ return crlEntryExtensions;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return seq;
+ }
+
+ public boolean hasExtensions()
+ {
+ return seq.size() == 3;
+ }
+ }
+
+ private class RevokedCertificatesEnumeration
+ implements Enumeration
+ {
+ private final Enumeration en;
+
+ RevokedCertificatesEnumeration(Enumeration en)
+ {
+ this.en = en;
+ }
+
+ public boolean hasMoreElements()
+ {
+ return en.hasMoreElements();
+ }
+
+ public Object nextElement()
+ {
+ return CRLEntry.getInstance(en.nextElement());
+ }
+ }
+
+ private class EmptyEnumeration
+ implements Enumeration
+ {
+ public boolean hasMoreElements()
+ {
+ return false;
+ }
+
+ public Object nextElement()
+ {
+ return null; // TODO: check exception handling
+ }
+ }
+
+ ASN1Integer version;
+ AlgorithmIdentifier signature;
+ X500Name issuer;
+ Time thisUpdate;
+ Time nextUpdate;
+ ASN1Sequence revokedCertificates;
+ Extensions crlExtensions;
+
+ public static TBSCertList getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static TBSCertList getInstance(
+ Object obj)
+ {
+ if (obj instanceof TBSCertList)
+ {
+ return (TBSCertList)obj;
+ }
+ else if (obj != null)
+ {
+ return new TBSCertList(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public TBSCertList(
+ ASN1Sequence seq)
+ {
+ if (seq.size() < 3 || seq.size() > 7)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ int seqPos = 0;
+
+ if (seq.getObjectAt(seqPos) instanceof ASN1Integer)
+ {
+ version = ASN1Integer.getInstance(seq.getObjectAt(seqPos++));
+ }
+ else
+ {
+ version = null; // version is optional
+ }
+
+ signature = AlgorithmIdentifier.getInstance(seq.getObjectAt(seqPos++));
+ issuer = X500Name.getInstance(seq.getObjectAt(seqPos++));
+ thisUpdate = Time.getInstance(seq.getObjectAt(seqPos++));
+
+ if (seqPos < seq.size()
+ && (seq.getObjectAt(seqPos) instanceof DERUTCTime
+ || seq.getObjectAt(seqPos) instanceof DERGeneralizedTime
+ || seq.getObjectAt(seqPos) instanceof Time))
+ {
+ nextUpdate = Time.getInstance(seq.getObjectAt(seqPos++));
+ }
+
+ if (seqPos < seq.size()
+ && !(seq.getObjectAt(seqPos) instanceof DERTaggedObject))
+ {
+ revokedCertificates = ASN1Sequence.getInstance(seq.getObjectAt(seqPos++));
+ }
+
+ if (seqPos < seq.size()
+ && seq.getObjectAt(seqPos) instanceof DERTaggedObject)
+ {
+ crlExtensions = Extensions.getInstance(ASN1Sequence.getInstance((ASN1TaggedObject)seq.getObjectAt(seqPos), true));
+ }
+ }
+
+ public int getVersionNumber()
+ {
+ if (version == null)
+ {
+ return 1;
+ }
+ return version.getValue().intValue() + 1;
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public AlgorithmIdentifier getSignature()
+ {
+ return signature;
+ }
+
+ public X500Name getIssuer()
+ {
+ return issuer;
+ }
+
+ public Time getThisUpdate()
+ {
+ return thisUpdate;
+ }
+
+ public Time getNextUpdate()
+ {
+ return nextUpdate;
+ }
+
+ public CRLEntry[] getRevokedCertificates()
+ {
+ if (revokedCertificates == null)
+ {
+ return new CRLEntry[0];
+ }
+
+ CRLEntry[] entries = new CRLEntry[revokedCertificates.size()];
+
+ for (int i = 0; i < entries.length; i++)
+ {
+ entries[i] = CRLEntry.getInstance(revokedCertificates.getObjectAt(i));
+ }
+
+ return entries;
+ }
+
+ public Enumeration getRevokedCertificateEnumeration()
+ {
+ if (revokedCertificates == null)
+ {
+ return new EmptyEnumeration();
+ }
+
+ return new RevokedCertificatesEnumeration(revokedCertificates.getObjects());
+ }
+
+ public Extensions getExtensions()
+ {
+ return crlExtensions;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (version != null)
+ {
+ v.add(version);
+ }
+ v.add(signature);
+ v.add(issuer);
+
+ v.add(thisUpdate);
+ if (nextUpdate != null)
+ {
+ v.add(nextUpdate);
+ }
+
+ // Add CRLEntries if they exist
+ if (revokedCertificates != null)
+ {
+ v.add(revokedCertificates);
+ }
+
+ if (crlExtensions != null)
+ {
+ v.add(new DERTaggedObject(0, crlExtensions));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/TBSCertificate.java b/core/src/main/java/org/bouncycastle/asn1/x509/TBSCertificate.java
new file mode 100644
index 00000000..dc419649
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/TBSCertificate.java
@@ -0,0 +1,192 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x500.X500Name;
+
+/**
+ * The TBSCertificate object.
+ * <pre>
+ * TBSCertificate ::= SEQUENCE {
+ * version [ 0 ] Version DEFAULT v1(0),
+ * serialNumber CertificateSerialNumber,
+ * signature AlgorithmIdentifier,
+ * issuer Name,
+ * validity Validity,
+ * subject Name,
+ * subjectPublicKeyInfo SubjectPublicKeyInfo,
+ * issuerUniqueID [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ * subjectUniqueID [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ * extensions [ 3 ] Extensions OPTIONAL
+ * }
+ * </pre>
+ * <p>
+ * Note: issuerUniqueID and subjectUniqueID are both deprecated by the IETF. This class
+ * will parse them, but you really shouldn't be creating new ones.
+ */
+public class TBSCertificate
+ extends ASN1Object
+{
+ ASN1Sequence seq;
+
+ ASN1Integer version;
+ ASN1Integer serialNumber;
+ AlgorithmIdentifier signature;
+ X500Name issuer;
+ Time startDate, endDate;
+ X500Name subject;
+ SubjectPublicKeyInfo subjectPublicKeyInfo;
+ DERBitString issuerUniqueId;
+ DERBitString subjectUniqueId;
+ Extensions extensions;
+
+ public static TBSCertificate getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static TBSCertificate getInstance(
+ Object obj)
+ {
+ if (obj instanceof TBSCertificate)
+ {
+ return (TBSCertificate)obj;
+ }
+ else if (obj != null)
+ {
+ return new TBSCertificate(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private TBSCertificate(
+ ASN1Sequence seq)
+ {
+ int seqStart = 0;
+
+ this.seq = seq;
+
+ //
+ // some certficates don't include a version number - we assume v1
+ //
+ if (seq.getObjectAt(0) instanceof DERTaggedObject)
+ {
+ version = ASN1Integer.getInstance((ASN1TaggedObject)seq.getObjectAt(0), true);
+ }
+ else
+ {
+ seqStart = -1; // field 0 is missing!
+ version = new ASN1Integer(0);
+ }
+
+ serialNumber = ASN1Integer.getInstance(seq.getObjectAt(seqStart + 1));
+
+ signature = AlgorithmIdentifier.getInstance(seq.getObjectAt(seqStart + 2));
+ issuer = X500Name.getInstance(seq.getObjectAt(seqStart + 3));
+
+ //
+ // before and after dates
+ //
+ ASN1Sequence dates = (ASN1Sequence)seq.getObjectAt(seqStart + 4);
+
+ startDate = Time.getInstance(dates.getObjectAt(0));
+ endDate = Time.getInstance(dates.getObjectAt(1));
+
+ subject = X500Name.getInstance(seq.getObjectAt(seqStart + 5));
+
+ //
+ // public key info.
+ //
+ subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(seq.getObjectAt(seqStart + 6));
+
+ for (int extras = seq.size() - (seqStart + 6) - 1; extras > 0; extras--)
+ {
+ DERTaggedObject extra = (DERTaggedObject)seq.getObjectAt(seqStart + 6 + extras);
+
+ switch (extra.getTagNo())
+ {
+ case 1:
+ issuerUniqueId = DERBitString.getInstance(extra, false);
+ break;
+ case 2:
+ subjectUniqueId = DERBitString.getInstance(extra, false);
+ break;
+ case 3:
+ extensions = Extensions.getInstance(ASN1Sequence.getInstance(extra, true));
+ }
+ }
+ }
+
+ public int getVersionNumber()
+ {
+ return version.getValue().intValue() + 1;
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public ASN1Integer getSerialNumber()
+ {
+ return serialNumber;
+ }
+
+ public AlgorithmIdentifier getSignature()
+ {
+ return signature;
+ }
+
+ public X500Name getIssuer()
+ {
+ return issuer;
+ }
+
+ public Time getStartDate()
+ {
+ return startDate;
+ }
+
+ public Time getEndDate()
+ {
+ return endDate;
+ }
+
+ public X500Name getSubject()
+ {
+ return subject;
+ }
+
+ public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
+ {
+ return subjectPublicKeyInfo;
+ }
+
+ public DERBitString getIssuerUniqueId()
+ {
+ return issuerUniqueId;
+ }
+
+ public DERBitString getSubjectUniqueId()
+ {
+ return subjectUniqueId;
+ }
+
+ public Extensions getExtensions()
+ {
+ return extensions;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return seq;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/TBSCertificateStructure.java b/core/src/main/java/org/bouncycastle/asn1/x509/TBSCertificateStructure.java
new file mode 100644
index 00000000..2c5d920a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/TBSCertificateStructure.java
@@ -0,0 +1,194 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x500.X500Name;
+
+/**
+ * The TBSCertificate object.
+ * <pre>
+ * TBSCertificate ::= SEQUENCE {
+ * version [ 0 ] Version DEFAULT v1(0),
+ * serialNumber CertificateSerialNumber,
+ * signature AlgorithmIdentifier,
+ * issuer Name,
+ * validity Validity,
+ * subject Name,
+ * subjectPublicKeyInfo SubjectPublicKeyInfo,
+ * issuerUniqueID [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ * subjectUniqueID [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ * extensions [ 3 ] Extensions OPTIONAL
+ * }
+ * </pre>
+ * <p>
+ * Note: issuerUniqueID and subjectUniqueID are both deprecated by the IETF. This class
+ * will parse them, but you really shouldn't be creating new ones.
+ */
+public class TBSCertificateStructure
+ extends ASN1Object
+ implements X509ObjectIdentifiers, PKCSObjectIdentifiers
+{
+ ASN1Sequence seq;
+
+ ASN1Integer version;
+ ASN1Integer serialNumber;
+ AlgorithmIdentifier signature;
+ X500Name issuer;
+ Time startDate, endDate;
+ X500Name subject;
+ SubjectPublicKeyInfo subjectPublicKeyInfo;
+ DERBitString issuerUniqueId;
+ DERBitString subjectUniqueId;
+ X509Extensions extensions;
+
+ public static TBSCertificateStructure getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static TBSCertificateStructure getInstance(
+ Object obj)
+ {
+ if (obj instanceof TBSCertificateStructure)
+ {
+ return (TBSCertificateStructure)obj;
+ }
+ else if (obj != null)
+ {
+ return new TBSCertificateStructure(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public TBSCertificateStructure(
+ ASN1Sequence seq)
+ {
+ int seqStart = 0;
+
+ this.seq = seq;
+
+ //
+ // some certficates don't include a version number - we assume v1
+ //
+ if (seq.getObjectAt(0) instanceof DERTaggedObject)
+ {
+ version = ASN1Integer.getInstance((ASN1TaggedObject)seq.getObjectAt(0), true);
+ }
+ else
+ {
+ seqStart = -1; // field 0 is missing!
+ version = new ASN1Integer(0);
+ }
+
+ serialNumber = ASN1Integer.getInstance(seq.getObjectAt(seqStart + 1));
+
+ signature = AlgorithmIdentifier.getInstance(seq.getObjectAt(seqStart + 2));
+ issuer = X500Name.getInstance(seq.getObjectAt(seqStart + 3));
+
+ //
+ // before and after dates
+ //
+ ASN1Sequence dates = (ASN1Sequence)seq.getObjectAt(seqStart + 4);
+
+ startDate = Time.getInstance(dates.getObjectAt(0));
+ endDate = Time.getInstance(dates.getObjectAt(1));
+
+ subject = X500Name.getInstance(seq.getObjectAt(seqStart + 5));
+
+ //
+ // public key info.
+ //
+ subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(seq.getObjectAt(seqStart + 6));
+
+ for (int extras = seq.size() - (seqStart + 6) - 1; extras > 0; extras--)
+ {
+ DERTaggedObject extra = (DERTaggedObject)seq.getObjectAt(seqStart + 6 + extras);
+
+ switch (extra.getTagNo())
+ {
+ case 1:
+ issuerUniqueId = DERBitString.getInstance(extra, false);
+ break;
+ case 2:
+ subjectUniqueId = DERBitString.getInstance(extra, false);
+ break;
+ case 3:
+ extensions = X509Extensions.getInstance(extra);
+ }
+ }
+ }
+
+ public int getVersion()
+ {
+ return version.getValue().intValue() + 1;
+ }
+
+ public ASN1Integer getVersionNumber()
+ {
+ return version;
+ }
+
+ public ASN1Integer getSerialNumber()
+ {
+ return serialNumber;
+ }
+
+ public AlgorithmIdentifier getSignature()
+ {
+ return signature;
+ }
+
+ public X500Name getIssuer()
+ {
+ return issuer;
+ }
+
+ public Time getStartDate()
+ {
+ return startDate;
+ }
+
+ public Time getEndDate()
+ {
+ return endDate;
+ }
+
+ public X500Name getSubject()
+ {
+ return subject;
+ }
+
+ public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
+ {
+ return subjectPublicKeyInfo;
+ }
+
+ public DERBitString getIssuerUniqueId()
+ {
+ return issuerUniqueId;
+ }
+
+ public DERBitString getSubjectUniqueId()
+ {
+ return subjectUniqueId;
+ }
+
+ public X509Extensions getExtensions()
+ {
+ return extensions;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return seq;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/Target.java b/core/src/main/java/org/bouncycastle/asn1/x509/Target.java
new file mode 100644
index 00000000..b302f5a2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/Target.java
@@ -0,0 +1,138 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * Target structure used in target information extension for attribute
+ * certificates from RFC 3281.
+ *
+ * <pre>
+ * Target ::= CHOICE {
+ * targetName [0] GeneralName,
+ * targetGroup [1] GeneralName,
+ * targetCert [2] TargetCert
+ * }
+ * </pre>
+ *
+ * <p>
+ * The targetCert field is currently not supported and must not be used
+ * according to RFC 3281.
+ */
+public class Target
+ extends ASN1Object
+ implements ASN1Choice
+{
+ public static final int targetName = 0;
+ public static final int targetGroup = 1;
+
+ private GeneralName targName;
+ private GeneralName targGroup;
+
+ /**
+ * Creates an instance of a Target from the given object.
+ * <p>
+ * <code>obj</code> can be a Target or a {@link ASN1TaggedObject}
+ *
+ * @param obj The object.
+ * @return A Target instance.
+ * @throws IllegalArgumentException if the given object cannot be
+ * interpreted as Target.
+ */
+ public static Target getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof Target)
+ {
+ return (Target) obj;
+ }
+ else if (obj instanceof ASN1TaggedObject)
+ {
+ return new Target((ASN1TaggedObject)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory: "
+ + obj.getClass());
+ }
+
+ /**
+ * Constructor from ASN1TaggedObject.
+ *
+ * @param tagObj The tagged object.
+ * @throws IllegalArgumentException if the encoding is wrong.
+ */
+ private Target(ASN1TaggedObject tagObj)
+ {
+ switch (tagObj.getTagNo())
+ {
+ case targetName: // GeneralName is already a choice so explicit
+ targName = GeneralName.getInstance(tagObj, true);
+ break;
+ case targetGroup:
+ targGroup = GeneralName.getInstance(tagObj, true);
+ break;
+ default:
+ throw new IllegalArgumentException("unknown tag: " + tagObj.getTagNo());
+ }
+ }
+
+ /**
+ * Constructor from given details.
+ * <p>
+ * Exactly one of the parameters must be not <code>null</code>.
+ *
+ * @param type the choice type to apply to the name.
+ * @param name the general name.
+ * @throws IllegalArgumentException if type is invalid.
+ */
+ public Target(int type, GeneralName name)
+ {
+ this(new DERTaggedObject(type, name));
+ }
+
+ /**
+ * @return Returns the targetGroup.
+ */
+ public GeneralName getTargetGroup()
+ {
+ return targGroup;
+ }
+
+ /**
+ * @return Returns the targetName.
+ */
+ public GeneralName getTargetName()
+ {
+ return targName;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ *
+ * Returns:
+ *
+ * <pre>
+ * Target ::= CHOICE {
+ * targetName [0] GeneralName,
+ * targetGroup [1] GeneralName,
+ * targetCert [2] TargetCert
+ * }
+ * </pre>
+ *
+ * @return a ASN1Primitive
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ // GeneralName is a choice already so most be explicitly tagged
+ if (targName != null)
+ {
+ return new DERTaggedObject(true, 0, targName);
+ }
+ else
+ {
+ return new DERTaggedObject(true, 1, targGroup);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/TargetInformation.java b/core/src/main/java/org/bouncycastle/asn1/x509/TargetInformation.java
new file mode 100644
index 00000000..eb892b9d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/TargetInformation.java
@@ -0,0 +1,120 @@
+package org.bouncycastle.asn1.x509;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * Target information extension for attributes certificates according to RFC
+ * 3281.
+ *
+ * <pre>
+ * SEQUENCE OF Targets
+ * </pre>
+ *
+ */
+public class TargetInformation
+ extends ASN1Object
+{
+ private ASN1Sequence targets;
+
+ /**
+ * Creates an instance of a TargetInformation from the given object.
+ * <p>
+ * <code>obj</code> can be a TargetInformation or a {@link ASN1Sequence}
+ *
+ * @param obj The object.
+ * @return A TargetInformation instance.
+ * @throws IllegalArgumentException if the given object cannot be
+ * interpreted as TargetInformation.
+ */
+ public static TargetInformation getInstance(Object obj)
+ {
+ if (obj instanceof TargetInformation)
+ {
+ return (TargetInformation)obj;
+ }
+ else if (obj != null)
+ {
+ return new TargetInformation(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Constructor from a ASN1Sequence.
+ *
+ * @param seq The ASN1Sequence.
+ * @throws IllegalArgumentException if the sequence does not contain
+ * correctly encoded Targets elements.
+ */
+ private TargetInformation(ASN1Sequence seq)
+ {
+ targets = seq;
+ }
+
+ /**
+ * Returns the targets in this target information extension.
+ *
+ * @return Returns the targets.
+ */
+ public Targets[] getTargetsObjects()
+ {
+ Targets[] copy = new Targets[targets.size()];
+ int count = 0;
+ for (Enumeration e = targets.getObjects(); e.hasMoreElements();)
+ {
+ copy[count++] = Targets.getInstance(e.nextElement());
+ }
+ return copy;
+ }
+
+ /**
+ * Constructs a target information from a single targets element.
+ * According to RFC 3281 only one targets element must be produced.
+ *
+ * @param targets A Targets instance.
+ */
+ public TargetInformation(Targets targets)
+ {
+ this.targets = new DERSequence(targets);
+ }
+
+ /**
+ * According to RFC 3281 only one targets element must be produced. If
+ * multiple targets are given they must be merged in
+ * into one targets element.
+ *
+ * @param targets An array with {@link Targets}.
+ */
+ public TargetInformation(Target[] targets)
+ {
+ this(new Targets(targets));
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ *
+ * Returns:
+ *
+ * <pre>
+ * SEQUENCE OF Targets
+ * </pre>
+ *
+ * <p>
+ * According to RFC 3281 only one targets element must be produced. If
+ * multiple targets are given in the constructor they are merged into one
+ * targets element. If this was produced from a
+ * {@link org.bouncycastle.asn1.ASN1Sequence} the encoding is kept.
+ *
+ * @return a ASN1Primitive
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return targets;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/Targets.java b/core/src/main/java/org/bouncycastle/asn1/x509/Targets.java
new file mode 100644
index 00000000..4c7d0625
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/Targets.java
@@ -0,0 +1,121 @@
+package org.bouncycastle.asn1.x509;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * Targets structure used in target information extension for attribute
+ * certificates from RFC 3281.
+ *
+ * <pre>
+ * Targets ::= SEQUENCE OF Target
+ *
+ * Target ::= CHOICE {
+ * targetName [0] GeneralName,
+ * targetGroup [1] GeneralName,
+ * targetCert [2] TargetCert
+ * }
+ *
+ * TargetCert ::= SEQUENCE {
+ * targetCertificate IssuerSerial,
+ * targetName GeneralName OPTIONAL,
+ * certDigestInfo ObjectDigestInfo OPTIONAL
+ * }
+ * </pre>
+ *
+ * @see org.bouncycastle.asn1.x509.Target
+ * @see org.bouncycastle.asn1.x509.TargetInformation
+ */
+public class Targets
+ extends ASN1Object
+{
+ private ASN1Sequence targets;
+
+ /**
+ * Creates an instance of a Targets from the given object.
+ * <p>
+ * <code>obj</code> can be a Targets or a {@link ASN1Sequence}
+ *
+ * @param obj The object.
+ * @return A Targets instance.
+ * @throws IllegalArgumentException if the given object cannot be
+ * interpreted as Target.
+ */
+ public static Targets getInstance(Object obj)
+ {
+ if (obj instanceof Targets)
+ {
+ return (Targets)obj;
+ }
+ else if (obj != null)
+ {
+ return new Targets(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Constructor from ASN1Sequence.
+ *
+ * @param targets The ASN.1 SEQUENCE.
+ * @throws IllegalArgumentException if the contents of the sequence are
+ * invalid.
+ */
+ private Targets(ASN1Sequence targets)
+ {
+ this.targets = targets;
+ }
+
+ /**
+ * Constructor from given targets.
+ * <p>
+ * The vector is copied.
+ *
+ * @param targets A <code>Vector</code> of {@link Target}s.
+ * @see Target
+ * @throws IllegalArgumentException if the vector contains not only Targets.
+ */
+ public Targets(Target[] targets)
+ {
+ this.targets = new DERSequence(targets);
+ }
+
+ /**
+ * Returns the targets in a <code>Vector</code>.
+ * <p>
+ * The vector is cloned before it is returned.
+ *
+ * @return Returns the targets.
+ */
+ public Target[] getTargets()
+ {
+ Target[] targs = new Target[targets.size()];
+ int count = 0;
+ for (Enumeration e = targets.getObjects(); e.hasMoreElements();)
+ {
+ targs[count++] = Target.getInstance(e.nextElement());
+ }
+ return targs;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ *
+ * Returns:
+ *
+ * <pre>
+ * Targets ::= SEQUENCE OF Target
+ * </pre>
+ *
+ * @return a ASN1Primitive
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return targets;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/Time.java b/core/src/main/java/org/bouncycastle/asn1/x509/Time.java
new file mode 100644
index 00000000..5bffedc5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/Time.java
@@ -0,0 +1,133 @@
+package org.bouncycastle.asn1.x509;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.SimpleTimeZone;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERGeneralizedTime;
+import org.bouncycastle.asn1.DERUTCTime;
+
+public class Time
+ extends ASN1Object
+ implements ASN1Choice
+{
+ ASN1Primitive time;
+
+ public static Time getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject()); // must be explicitly tagged
+ }
+
+ public Time(
+ ASN1Primitive time)
+ {
+ if (!(time instanceof DERUTCTime)
+ && !(time instanceof DERGeneralizedTime))
+ {
+ throw new IllegalArgumentException("unknown object passed to Time");
+ }
+
+ this.time = time;
+ }
+
+ /**
+ * creates a time object from a given date - if the date is between 1950
+ * and 2049 a UTCTime object is generated, otherwise a GeneralizedTime
+ * is used.
+ */
+ public Time(
+ Date date)
+ {
+ SimpleTimeZone tz = new SimpleTimeZone(0, "Z");
+ SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss");
+
+ dateF.setTimeZone(tz);
+
+ String d = dateF.format(date) + "Z";
+ int year = Integer.parseInt(d.substring(0, 4));
+
+ if (year < 1950 || year > 2049)
+ {
+ time = new DERGeneralizedTime(d);
+ }
+ else
+ {
+ time = new DERUTCTime(d.substring(2));
+ }
+ }
+
+ public static Time getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof Time)
+ {
+ return (Time)obj;
+ }
+ else if (obj instanceof DERUTCTime)
+ {
+ return new Time((DERUTCTime)obj);
+ }
+ else if (obj instanceof DERGeneralizedTime)
+ {
+ return new Time((DERGeneralizedTime)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
+ }
+
+ public String getTime()
+ {
+ if (time instanceof DERUTCTime)
+ {
+ return ((DERUTCTime)time).getAdjustedTime();
+ }
+ else
+ {
+ return ((DERGeneralizedTime)time).getTime();
+ }
+ }
+
+ public Date getDate()
+ {
+ try
+ {
+ if (time instanceof DERUTCTime)
+ {
+ return ((DERUTCTime)time).getAdjustedDate();
+ }
+ else
+ {
+ return ((DERGeneralizedTime)time).getDate();
+ }
+ }
+ catch (ParseException e)
+ { // this should never happen
+ throw new IllegalStateException("invalid date string: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * Time ::= CHOICE {
+ * utcTime UTCTime,
+ * generalTime GeneralizedTime }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return time;
+ }
+
+ public String toString()
+ {
+ return getTime();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/UserNotice.java b/core/src/main/java/org/bouncycastle/asn1/x509/UserNotice.java
new file mode 100644
index 00000000..ebc04051
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/UserNotice.java
@@ -0,0 +1,132 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * <code>UserNotice</code> class, used in
+ * <code>CertificatePolicies</code> X509 extensions (in policy
+ * qualifiers).
+ * <pre>
+ * UserNotice ::= SEQUENCE {
+ * noticeRef NoticeReference OPTIONAL,
+ * explicitText DisplayText OPTIONAL}
+ *
+ * </pre>
+ *
+ * @see PolicyQualifierId
+ * @see PolicyInformation
+ */
+public class UserNotice
+ extends ASN1Object
+{
+ private NoticeReference noticeRef;
+ private DisplayText explicitText;
+
+ /**
+ * Creates a new <code>UserNotice</code> instance.
+ *
+ * @param noticeRef a <code>NoticeReference</code> value
+ * @param explicitText a <code>DisplayText</code> value
+ */
+ public UserNotice(
+ NoticeReference noticeRef,
+ DisplayText explicitText)
+ {
+ this.noticeRef = noticeRef;
+ this.explicitText = explicitText;
+ }
+
+ /**
+ * Creates a new <code>UserNotice</code> instance.
+ *
+ * @param noticeRef a <code>NoticeReference</code> value
+ * @param str the explicitText field as a String.
+ */
+ public UserNotice(
+ NoticeReference noticeRef,
+ String str)
+ {
+ this(noticeRef, new DisplayText(str));
+ }
+
+ /**
+ * Creates a new <code>UserNotice</code> instance.
+ * <p>Useful from reconstructing a <code>UserNotice</code> instance
+ * from its encodable/encoded form.
+ *
+ * @param as an <code>ASN1Sequence</code> value obtained from either
+ * calling @{link toASN1Primitive()} for a <code>UserNotice</code>
+ * instance or from parsing it from a DER-encoded stream.
+ */
+ private UserNotice(
+ ASN1Sequence as)
+ {
+ if (as.size() == 2)
+ {
+ noticeRef = NoticeReference.getInstance(as.getObjectAt(0));
+ explicitText = DisplayText.getInstance(as.getObjectAt(1));
+ }
+ else if (as.size() == 1)
+ {
+ if (as.getObjectAt(0).toASN1Primitive() instanceof ASN1Sequence)
+ {
+ noticeRef = NoticeReference.getInstance(as.getObjectAt(0));
+ }
+ else
+ {
+ explicitText = DisplayText.getInstance(as.getObjectAt(0));
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + as.size());
+ }
+ }
+
+ public static UserNotice getInstance(
+ Object obj)
+ {
+ if (obj instanceof UserNotice)
+ {
+ return (UserNotice)obj;
+ }
+
+ if (obj != null)
+ {
+ return new UserNotice(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public NoticeReference getNoticeRef()
+ {
+ return noticeRef;
+ }
+
+ public DisplayText getExplicitText()
+ {
+ return explicitText;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector av = new ASN1EncodableVector();
+
+ if (noticeRef != null)
+ {
+ av.add(noticeRef);
+ }
+
+ if (explicitText != null)
+ {
+ av.add(explicitText);
+ }
+
+ return new DERSequence(av);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/V1TBSCertificateGenerator.java b/core/src/main/java/org/bouncycastle/asn1/x509/V1TBSCertificateGenerator.java
new file mode 100644
index 00000000..fe4cb5eb
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/V1TBSCertificateGenerator.java
@@ -0,0 +1,144 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1UTCTime;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x500.X500Name;
+
+/**
+ * Generator for Version 1 TBSCertificateStructures.
+ * <pre>
+ * TBSCertificate ::= SEQUENCE {
+ * version [ 0 ] Version DEFAULT v1(0),
+ * serialNumber CertificateSerialNumber,
+ * signature AlgorithmIdentifier,
+ * issuer Name,
+ * validity Validity,
+ * subject Name,
+ * subjectPublicKeyInfo SubjectPublicKeyInfo,
+ * }
+ * </pre>
+ *
+ */
+public class V1TBSCertificateGenerator
+{
+ DERTaggedObject version = new DERTaggedObject(true, 0, new ASN1Integer(0));
+
+ ASN1Integer serialNumber;
+ AlgorithmIdentifier signature;
+ X500Name issuer;
+ Time startDate, endDate;
+ X500Name subject;
+ SubjectPublicKeyInfo subjectPublicKeyInfo;
+
+ public V1TBSCertificateGenerator()
+ {
+ }
+
+ public void setSerialNumber(
+ ASN1Integer serialNumber)
+ {
+ this.serialNumber = serialNumber;
+ }
+
+ public void setSignature(
+ AlgorithmIdentifier signature)
+ {
+ this.signature = signature;
+ }
+
+ /**
+ * @deprecated use X500Name method
+ */
+ public void setIssuer(
+ X509Name issuer)
+ {
+ this.issuer = X500Name.getInstance(issuer.toASN1Primitive());
+ }
+
+ public void setIssuer(
+ X500Name issuer)
+ {
+ this.issuer = issuer;
+ }
+
+ public void setStartDate(
+ Time startDate)
+ {
+ this.startDate = startDate;
+ }
+
+ public void setStartDate(
+ ASN1UTCTime startDate)
+ {
+ this.startDate = new Time(startDate);
+ }
+
+ public void setEndDate(
+ Time endDate)
+ {
+ this.endDate = endDate;
+ }
+
+ public void setEndDate(
+ ASN1UTCTime endDate)
+ {
+ this.endDate = new Time(endDate);
+ }
+
+ /**
+ * @deprecated use X500Name method
+ */
+ public void setSubject(
+ X509Name subject)
+ {
+ this.subject = X500Name.getInstance(subject.toASN1Primitive());
+ }
+
+ public void setSubject(
+ X500Name subject)
+ {
+ this.subject = subject;
+ }
+
+ public void setSubjectPublicKeyInfo(
+ SubjectPublicKeyInfo pubKeyInfo)
+ {
+ this.subjectPublicKeyInfo = pubKeyInfo;
+ }
+
+ public TBSCertificate generateTBSCertificate()
+ {
+ if ((serialNumber == null) || (signature == null)
+ || (issuer == null) || (startDate == null) || (endDate == null)
+ || (subject == null) || (subjectPublicKeyInfo == null))
+ {
+ throw new IllegalStateException("not all mandatory fields set in V1 TBScertificate generator");
+ }
+
+ ASN1EncodableVector seq = new ASN1EncodableVector();
+
+ // seq.add(version); - not required as default value.
+ seq.add(serialNumber);
+ seq.add(signature);
+ seq.add(issuer);
+
+ //
+ // before and after dates
+ //
+ ASN1EncodableVector validity = new ASN1EncodableVector();
+
+ validity.add(startDate);
+ validity.add(endDate);
+
+ seq.add(new DERSequence(validity));
+
+ seq.add(subject);
+
+ seq.add(subjectPublicKeyInfo);
+
+ return TBSCertificate.getInstance(new DERSequence(seq));
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/V2AttributeCertificateInfoGenerator.java b/core/src/main/java/org/bouncycastle/asn1/x509/V2AttributeCertificateInfoGenerator.java
new file mode 100644
index 00000000..635a69e8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/V2AttributeCertificateInfoGenerator.java
@@ -0,0 +1,158 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERSet;
+
+/**
+ * Generator for Version 2 AttributeCertificateInfo
+ * <pre>
+ * AttributeCertificateInfo ::= SEQUENCE {
+ * version AttCertVersion -- version is v2,
+ * holder Holder,
+ * issuer AttCertIssuer,
+ * signature AlgorithmIdentifier,
+ * serialNumber CertificateSerialNumber,
+ * attrCertValidityPeriod AttCertValidityPeriod,
+ * attributes SEQUENCE OF Attribute,
+ * issuerUniqueID UniqueIdentifier OPTIONAL,
+ * extensions Extensions OPTIONAL
+ * }
+ * </pre>
+ *
+ */
+public class V2AttributeCertificateInfoGenerator
+{
+ private ASN1Integer version;
+ private Holder holder;
+ private AttCertIssuer issuer;
+ private AlgorithmIdentifier signature;
+ private ASN1Integer serialNumber;
+ private ASN1EncodableVector attributes;
+ private DERBitString issuerUniqueID;
+ private Extensions extensions;
+
+ // Note: validity period start/end dates stored directly
+ //private AttCertValidityPeriod attrCertValidityPeriod;
+ private ASN1GeneralizedTime startDate, endDate;
+
+ public V2AttributeCertificateInfoGenerator()
+ {
+ this.version = new ASN1Integer(1);
+ attributes = new ASN1EncodableVector();
+ }
+
+ public void setHolder(Holder holder)
+ {
+ this.holder = holder;
+ }
+
+ public void addAttribute(String oid, ASN1Encodable value)
+ {
+ attributes.add(new Attribute(new ASN1ObjectIdentifier(oid), new DERSet(value)));
+ }
+
+ /**
+ * @param attribute
+ */
+ public void addAttribute(Attribute attribute)
+ {
+ attributes.add(attribute);
+ }
+
+ public void setSerialNumber(
+ ASN1Integer serialNumber)
+ {
+ this.serialNumber = serialNumber;
+ }
+
+ public void setSignature(
+ AlgorithmIdentifier signature)
+ {
+ this.signature = signature;
+ }
+
+ public void setIssuer(
+ AttCertIssuer issuer)
+ {
+ this.issuer = issuer;
+ }
+
+ public void setStartDate(
+ ASN1GeneralizedTime startDate)
+ {
+ this.startDate = startDate;
+ }
+
+ public void setEndDate(
+ ASN1GeneralizedTime endDate)
+ {
+ this.endDate = endDate;
+ }
+
+ public void setIssuerUniqueID(
+ DERBitString issuerUniqueID)
+ {
+ this.issuerUniqueID = issuerUniqueID;
+ }
+
+ /**
+ * @deprecated use method taking Extensions
+ * @param extensions
+ */
+ public void setExtensions(
+ X509Extensions extensions)
+ {
+ this.extensions = Extensions.getInstance(extensions.toASN1Primitive());
+ }
+
+ public void setExtensions(
+ Extensions extensions)
+ {
+ this.extensions = extensions;
+ }
+
+ public AttributeCertificateInfo generateAttributeCertificateInfo()
+ {
+ if ((serialNumber == null) || (signature == null)
+ || (issuer == null) || (startDate == null) || (endDate == null)
+ || (holder == null) || (attributes == null))
+ {
+ throw new IllegalStateException("not all mandatory fields set in V2 AttributeCertificateInfo generator");
+ }
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+ v.add(holder);
+ v.add(issuer);
+ v.add(signature);
+ v.add(serialNumber);
+
+ //
+ // before and after dates => AttCertValidityPeriod
+ //
+ AttCertValidityPeriod validity = new AttCertValidityPeriod(startDate, endDate);
+ v.add(validity);
+
+ // Attributes
+ v.add(new DERSequence(attributes));
+
+ if (issuerUniqueID != null)
+ {
+ v.add(issuerUniqueID);
+ }
+
+ if (extensions != null)
+ {
+ v.add(extensions);
+ }
+
+ return AttributeCertificateInfo.getInstance(new DERSequence(v));
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/V2Form.java b/core/src/main/java/org/bouncycastle/asn1/x509/V2Form.java
new file mode 100644
index 00000000..5cee8471
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/V2Form.java
@@ -0,0 +1,157 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class V2Form
+ extends ASN1Object
+{
+ GeneralNames issuerName;
+ IssuerSerial baseCertificateID;
+ ObjectDigestInfo objectDigestInfo;
+
+ public static V2Form getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static V2Form getInstance(
+ Object obj)
+ {
+ if (obj instanceof V2Form)
+ {
+ return (V2Form)obj;
+ }
+ else if (obj != null)
+ {
+ return new V2Form(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public V2Form(
+ GeneralNames issuerName)
+ {
+ this(issuerName, null, null);
+ }
+
+ public V2Form(
+ GeneralNames issuerName,
+ IssuerSerial baseCertificateID)
+ {
+ this(issuerName, baseCertificateID, null);
+ }
+
+ public V2Form(
+ GeneralNames issuerName,
+ ObjectDigestInfo objectDigestInfo)
+ {
+ this(issuerName, null, objectDigestInfo);
+ }
+
+ public V2Form(
+ GeneralNames issuerName,
+ IssuerSerial baseCertificateID,
+ ObjectDigestInfo objectDigestInfo)
+ {
+ this.issuerName = issuerName;
+ this.baseCertificateID = baseCertificateID;
+ this.objectDigestInfo = objectDigestInfo;
+ }
+
+ /**
+ * @deprecated use getInstance().
+ */
+ public V2Form(
+ ASN1Sequence seq)
+ {
+ if (seq.size() > 3)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ int index = 0;
+
+ if (!(seq.getObjectAt(0) instanceof ASN1TaggedObject))
+ {
+ index++;
+ this.issuerName = GeneralNames.getInstance(seq.getObjectAt(0));
+ }
+
+ for (int i = index; i != seq.size(); i++)
+ {
+ ASN1TaggedObject o = ASN1TaggedObject.getInstance(seq.getObjectAt(i));
+ if (o.getTagNo() == 0)
+ {
+ baseCertificateID = IssuerSerial.getInstance(o, false);
+ }
+ else if (o.getTagNo() == 1)
+ {
+ objectDigestInfo = ObjectDigestInfo.getInstance(o, false);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Bad tag number: "
+ + o.getTagNo());
+ }
+ }
+ }
+
+ public GeneralNames getIssuerName()
+ {
+ return issuerName;
+ }
+
+ public IssuerSerial getBaseCertificateID()
+ {
+ return baseCertificateID;
+ }
+
+ public ObjectDigestInfo getObjectDigestInfo()
+ {
+ return objectDigestInfo;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * V2Form ::= SEQUENCE {
+ * issuerName GeneralNames OPTIONAL,
+ * baseCertificateID [0] IssuerSerial OPTIONAL,
+ * objectDigestInfo [1] ObjectDigestInfo OPTIONAL
+ * -- issuerName MUST be present in this profile
+ * -- baseCertificateID and objectDigestInfo MUST NOT
+ * -- be present in this profile
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (issuerName != null)
+ {
+ v.add(issuerName);
+ }
+
+ if (baseCertificateID != null)
+ {
+ v.add(new DERTaggedObject(false, 0, baseCertificateID));
+ }
+
+ if (objectDigestInfo != null)
+ {
+ v.add(new DERTaggedObject(false, 1, objectDigestInfo));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/V2TBSCertListGenerator.java b/core/src/main/java/org/bouncycastle/asn1/x509/V2TBSCertListGenerator.java
new file mode 100644
index 00000000..869f5bc5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/V2TBSCertListGenerator.java
@@ -0,0 +1,281 @@
+package org.bouncycastle.asn1.x509;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1UTCTime;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x500.X500Name;
+
+/**
+ * Generator for Version 2 TBSCertList structures.
+ * <pre>
+ * TBSCertList ::= SEQUENCE {
+ * version Version OPTIONAL,
+ * -- if present, shall be v2
+ * signature AlgorithmIdentifier,
+ * issuer Name,
+ * thisUpdate Time,
+ * nextUpdate Time OPTIONAL,
+ * revokedCertificates SEQUENCE OF SEQUENCE {
+ * userCertificate CertificateSerialNumber,
+ * revocationDate Time,
+ * crlEntryExtensions Extensions OPTIONAL
+ * -- if present, shall be v2
+ * } OPTIONAL,
+ * crlExtensions [0] EXPLICIT Extensions OPTIONAL
+ * -- if present, shall be v2
+ * }
+ * </pre>
+ *
+ * <b>Note: This class may be subject to change</b>
+ */
+public class V2TBSCertListGenerator
+{
+ private ASN1Integer version = new ASN1Integer(1);
+ private AlgorithmIdentifier signature;
+ private X500Name issuer;
+ private Time thisUpdate, nextUpdate=null;
+ private Extensions extensions = null;
+ private ASN1EncodableVector crlentries = new ASN1EncodableVector();
+
+ private final static ASN1Sequence[] reasons;
+
+ static
+ {
+ reasons = new ASN1Sequence[11];
+
+ reasons[0] = createReasonExtension(CRLReason.unspecified);
+ reasons[1] = createReasonExtension(CRLReason.keyCompromise);
+ reasons[2] = createReasonExtension(CRLReason.cACompromise);
+ reasons[3] = createReasonExtension(CRLReason.affiliationChanged);
+ reasons[4] = createReasonExtension(CRLReason.superseded);
+ reasons[5] = createReasonExtension(CRLReason.cessationOfOperation);
+ reasons[6] = createReasonExtension(CRLReason.certificateHold);
+ reasons[7] = createReasonExtension(7); // 7 -> unknown
+ reasons[8] = createReasonExtension(CRLReason.removeFromCRL);
+ reasons[9] = createReasonExtension(CRLReason.privilegeWithdrawn);
+ reasons[10] = createReasonExtension(CRLReason.aACompromise);
+ }
+
+ public V2TBSCertListGenerator()
+ {
+ }
+
+
+ public void setSignature(
+ AlgorithmIdentifier signature)
+ {
+ this.signature = signature;
+ }
+
+ /**
+ * @deprecated use X500Name method
+ */
+ public void setIssuer(
+ X509Name issuer)
+ {
+ this.issuer = X500Name.getInstance(issuer.toASN1Primitive());
+ }
+
+ public void setIssuer(X500Name issuer)
+ {
+ this.issuer = issuer;
+ }
+
+ public void setThisUpdate(
+ ASN1UTCTime thisUpdate)
+ {
+ this.thisUpdate = new Time(thisUpdate);
+ }
+
+ public void setNextUpdate(
+ ASN1UTCTime nextUpdate)
+ {
+ this.nextUpdate = new Time(nextUpdate);
+ }
+
+ public void setThisUpdate(
+ Time thisUpdate)
+ {
+ this.thisUpdate = thisUpdate;
+ }
+
+ public void setNextUpdate(
+ Time nextUpdate)
+ {
+ this.nextUpdate = nextUpdate;
+ }
+
+ public void addCRLEntry(
+ ASN1Sequence crlEntry)
+ {
+ crlentries.add(crlEntry);
+ }
+
+ public void addCRLEntry(ASN1Integer userCertificate, ASN1UTCTime revocationDate, int reason)
+ {
+ addCRLEntry(userCertificate, new Time(revocationDate), reason);
+ }
+
+ public void addCRLEntry(ASN1Integer userCertificate, Time revocationDate, int reason)
+ {
+ addCRLEntry(userCertificate, revocationDate, reason, null);
+ }
+
+ public void addCRLEntry(ASN1Integer userCertificate, Time revocationDate, int reason, ASN1GeneralizedTime invalidityDate)
+ {
+ if (reason != 0)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (reason < reasons.length)
+ {
+ if (reason < 0)
+ {
+ throw new IllegalArgumentException("invalid reason value: " + reason);
+ }
+ v.add(reasons[reason]);
+ }
+ else
+ {
+ v.add(createReasonExtension(reason));
+ }
+
+ if (invalidityDate != null)
+ {
+ v.add(createInvalidityDateExtension(invalidityDate));
+ }
+
+ internalAddCRLEntry(userCertificate, revocationDate, new DERSequence(v));
+ }
+ else if (invalidityDate != null)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(createInvalidityDateExtension(invalidityDate));
+
+ internalAddCRLEntry(userCertificate, revocationDate, new DERSequence(v));
+ }
+ else
+ {
+ addCRLEntry(userCertificate, revocationDate, null);
+ }
+ }
+
+ private void internalAddCRLEntry(ASN1Integer userCertificate, Time revocationDate, ASN1Sequence extensions)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(userCertificate);
+ v.add(revocationDate);
+
+ if (extensions != null)
+ {
+ v.add(extensions);
+ }
+
+ addCRLEntry(new DERSequence(v));
+ }
+
+ public void addCRLEntry(ASN1Integer userCertificate, Time revocationDate, Extensions extensions)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(userCertificate);
+ v.add(revocationDate);
+
+ if (extensions != null)
+ {
+ v.add(extensions);
+ }
+
+ addCRLEntry(new DERSequence(v));
+ }
+
+ public void setExtensions(
+ X509Extensions extensions)
+ {
+ setExtensions(Extensions.getInstance(extensions));
+ }
+
+ public void setExtensions(
+ Extensions extensions)
+ {
+ this.extensions = extensions;
+ }
+
+ public TBSCertList generateTBSCertList()
+ {
+ if ((signature == null) || (issuer == null) || (thisUpdate == null))
+ {
+ throw new IllegalStateException("Not all mandatory fields set in V2 TBSCertList generator.");
+ }
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+ v.add(signature);
+ v.add(issuer);
+
+ v.add(thisUpdate);
+ if (nextUpdate != null)
+ {
+ v.add(nextUpdate);
+ }
+
+ // Add CRLEntries if they exist
+ if (crlentries.size() != 0)
+ {
+ v.add(new DERSequence(crlentries));
+ }
+
+ if (extensions != null)
+ {
+ v.add(new DERTaggedObject(0, extensions));
+ }
+
+ return new TBSCertList(new DERSequence(v));
+ }
+
+ private static ASN1Sequence createReasonExtension(int reasonCode)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ CRLReason crlReason = CRLReason.lookup(reasonCode);
+
+ try
+ {
+ v.add(Extension.reasonCode);
+ v.add(new DEROctetString(crlReason.getEncoded()));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("error encoding reason: " + e);
+ }
+
+ return new DERSequence(v);
+ }
+
+ private static ASN1Sequence createInvalidityDateExtension(ASN1GeneralizedTime invalidityDate)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ try
+ {
+ v.add(Extension.invalidityDate);
+ v.add(new DEROctetString(invalidityDate.getEncoded()));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("error encoding reason: " + e);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java b/core/src/main/java/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java
new file mode 100644
index 00000000..3d923b61
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java
@@ -0,0 +1,212 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.DERUTCTime;
+import org.bouncycastle.asn1.x500.X500Name;
+
+/**
+ * Generator for Version 3 TBSCertificateStructures.
+ * <pre>
+ * TBSCertificate ::= SEQUENCE {
+ * version [ 0 ] Version DEFAULT v1(0),
+ * serialNumber CertificateSerialNumber,
+ * signature AlgorithmIdentifier,
+ * issuer Name,
+ * validity Validity,
+ * subject Name,
+ * subjectPublicKeyInfo SubjectPublicKeyInfo,
+ * issuerUniqueID [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ * subjectUniqueID [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ * extensions [ 3 ] Extensions OPTIONAL
+ * }
+ * </pre>
+ *
+ */
+public class V3TBSCertificateGenerator
+{
+ DERTaggedObject version = new DERTaggedObject(true, 0, new ASN1Integer(2));
+
+ ASN1Integer serialNumber;
+ AlgorithmIdentifier signature;
+ X500Name issuer;
+ Time startDate, endDate;
+ X500Name subject;
+ SubjectPublicKeyInfo subjectPublicKeyInfo;
+ Extensions extensions;
+
+ private boolean altNamePresentAndCritical;
+ private DERBitString issuerUniqueID;
+ private DERBitString subjectUniqueID;
+
+ public V3TBSCertificateGenerator()
+ {
+ }
+
+ public void setSerialNumber(
+ ASN1Integer serialNumber)
+ {
+ this.serialNumber = serialNumber;
+ }
+
+ public void setSignature(
+ AlgorithmIdentifier signature)
+ {
+ this.signature = signature;
+ }
+
+ /**
+ * @deprecated use X500Name method
+ */
+ public void setIssuer(
+ X509Name issuer)
+ {
+ this.issuer = X500Name.getInstance(issuer);
+ }
+
+ public void setIssuer(
+ X500Name issuer)
+ {
+ this.issuer = issuer;
+ }
+
+ public void setStartDate(
+ DERUTCTime startDate)
+ {
+ this.startDate = new Time(startDate);
+ }
+
+ public void setStartDate(
+ Time startDate)
+ {
+ this.startDate = startDate;
+ }
+
+ public void setEndDate(
+ DERUTCTime endDate)
+ {
+ this.endDate = new Time(endDate);
+ }
+
+ public void setEndDate(
+ Time endDate)
+ {
+ this.endDate = endDate;
+ }
+
+ /**
+ * @deprecated use X500Name method
+ */
+ public void setSubject(
+ X509Name subject)
+ {
+ this.subject = X500Name.getInstance(subject.toASN1Primitive());
+ }
+
+ public void setSubject(
+ X500Name subject)
+ {
+ this.subject = subject;
+ }
+
+ public void setIssuerUniqueID(
+ DERBitString uniqueID)
+ {
+ this.issuerUniqueID = uniqueID;
+ }
+
+ public void setSubjectUniqueID(
+ DERBitString uniqueID)
+ {
+ this.subjectUniqueID = uniqueID;
+ }
+
+ public void setSubjectPublicKeyInfo(
+ SubjectPublicKeyInfo pubKeyInfo)
+ {
+ this.subjectPublicKeyInfo = pubKeyInfo;
+ }
+
+ /**
+ * @deprecated use method taking Extensions
+ * @param extensions
+ */
+ public void setExtensions(
+ X509Extensions extensions)
+ {
+ setExtensions(Extensions.getInstance(extensions));
+ }
+
+ public void setExtensions(
+ Extensions extensions)
+ {
+ this.extensions = extensions;
+ if (extensions != null)
+ {
+ Extension altName = extensions.getExtension(Extension.subjectAlternativeName);
+
+ if (altName != null && altName.isCritical())
+ {
+ altNamePresentAndCritical = true;
+ }
+ }
+ }
+
+ public TBSCertificate generateTBSCertificate()
+ {
+ if ((serialNumber == null) || (signature == null)
+ || (issuer == null) || (startDate == null) || (endDate == null)
+ || (subject == null && !altNamePresentAndCritical) || (subjectPublicKeyInfo == null))
+ {
+ throw new IllegalStateException("not all mandatory fields set in V3 TBScertificate generator");
+ }
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+ v.add(serialNumber);
+ v.add(signature);
+ v.add(issuer);
+
+ //
+ // before and after dates
+ //
+ ASN1EncodableVector validity = new ASN1EncodableVector();
+
+ validity.add(startDate);
+ validity.add(endDate);
+
+ v.add(new DERSequence(validity));
+
+ if (subject != null)
+ {
+ v.add(subject);
+ }
+ else
+ {
+ v.add(new DERSequence());
+ }
+
+ v.add(subjectPublicKeyInfo);
+
+ if (issuerUniqueID != null)
+ {
+ v.add(new DERTaggedObject(false, 1, issuerUniqueID));
+ }
+
+ if (subjectUniqueID != null)
+ {
+ v.add(new DERTaggedObject(false, 2, subjectUniqueID));
+ }
+
+ if (extensions != null)
+ {
+ v.add(new DERTaggedObject(true, 3, extensions));
+ }
+
+ return TBSCertificate.getInstance(new DERSequence(v));
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/X509AttributeIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/x509/X509AttributeIdentifiers.java
new file mode 100644
index 00000000..0ed12f7e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/X509AttributeIdentifiers.java
@@ -0,0 +1,29 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface X509AttributeIdentifiers
+{
+ /**
+ * @deprecated use id_at_role
+ */
+ static final ASN1ObjectIdentifier RoleSyntax = new ASN1ObjectIdentifier("2.5.4.72");
+
+ static final ASN1ObjectIdentifier id_pe_ac_auditIdentity = X509ObjectIdentifiers.id_pe.branch("4");
+ static final ASN1ObjectIdentifier id_pe_aaControls = X509ObjectIdentifiers.id_pe.branch("6");
+ static final ASN1ObjectIdentifier id_pe_ac_proxying = X509ObjectIdentifiers.id_pe.branch("10");
+
+ static final ASN1ObjectIdentifier id_ce_targetInformation= X509ObjectIdentifiers.id_ce.branch("55");
+
+ static final ASN1ObjectIdentifier id_aca = X509ObjectIdentifiers.id_pkix.branch("10");
+
+ static final ASN1ObjectIdentifier id_aca_authenticationInfo = id_aca.branch("1");
+ static final ASN1ObjectIdentifier id_aca_accessIdentity = id_aca.branch("2");
+ static final ASN1ObjectIdentifier id_aca_chargingIdentity = id_aca.branch("3");
+ static final ASN1ObjectIdentifier id_aca_group = id_aca.branch("4");
+ // { id-aca 5 } is reserved
+ static final ASN1ObjectIdentifier id_aca_encAttrs = id_aca.branch("6");
+
+ static final ASN1ObjectIdentifier id_at_role = new ASN1ObjectIdentifier("2.5.4.72");
+ static final ASN1ObjectIdentifier id_at_clearance = new ASN1ObjectIdentifier("2.5.1.5.55");
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/X509CertificateStructure.java b/core/src/main/java/org/bouncycastle/asn1/x509/X509CertificateStructure.java
new file mode 100644
index 00000000..6830030e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/X509CertificateStructure.java
@@ -0,0 +1,129 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x500.X500Name;
+
+/**
+ * an X509Certificate structure.
+ * <pre>
+ * Certificate ::= SEQUENCE {
+ * tbsCertificate TBSCertificate,
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signature BIT STRING
+ * }
+ * </pre>
+ * @deprecated use org.bouncycastle.asn1.x509.Certificate
+ */
+public class X509CertificateStructure
+ extends ASN1Object
+ implements X509ObjectIdentifiers, PKCSObjectIdentifiers
+{
+ ASN1Sequence seq;
+ TBSCertificateStructure tbsCert;
+ AlgorithmIdentifier sigAlgId;
+ DERBitString sig;
+
+ public static X509CertificateStructure getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static X509CertificateStructure getInstance(
+ Object obj)
+ {
+ if (obj instanceof X509CertificateStructure)
+ {
+ return (X509CertificateStructure)obj;
+ }
+ else if (obj != null)
+ {
+ return new X509CertificateStructure(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public X509CertificateStructure(
+ ASN1Sequence seq)
+ {
+ this.seq = seq;
+
+ //
+ // correct x509 certficate
+ //
+ if (seq.size() == 3)
+ {
+ tbsCert = TBSCertificateStructure.getInstance(seq.getObjectAt(0));
+ sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
+
+ sig = DERBitString.getInstance(seq.getObjectAt(2));
+ }
+ else
+ {
+ throw new IllegalArgumentException("sequence wrong size for a certificate");
+ }
+ }
+
+ public TBSCertificateStructure getTBSCertificate()
+ {
+ return tbsCert;
+ }
+
+ public int getVersion()
+ {
+ return tbsCert.getVersion();
+ }
+
+ public ASN1Integer getSerialNumber()
+ {
+ return tbsCert.getSerialNumber();
+ }
+
+ public X500Name getIssuer()
+ {
+ return tbsCert.getIssuer();
+ }
+
+ public Time getStartDate()
+ {
+ return tbsCert.getStartDate();
+ }
+
+ public Time getEndDate()
+ {
+ return tbsCert.getEndDate();
+ }
+
+ public X500Name getSubject()
+ {
+ return tbsCert.getSubject();
+ }
+
+ public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
+ {
+ return tbsCert.getSubjectPublicKeyInfo();
+ }
+
+ public AlgorithmIdentifier getSignatureAlgorithm()
+ {
+ return sigAlgId;
+ }
+
+ public DERBitString getSignature()
+ {
+ return sig;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return seq;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java b/core/src/main/java/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java
new file mode 100644
index 00000000..0ae0f802
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java
@@ -0,0 +1,65 @@
+package org.bouncycastle.asn1.x509;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DERGeneralizedTime;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.DERPrintableString;
+import org.bouncycastle.asn1.DERUTF8String;
+
+/**
+ * The default converter for X509 DN entries when going from their
+ * string value to ASN.1 strings.
+ */
+public class X509DefaultEntryConverter
+ extends X509NameEntryConverter
+{
+ /**
+ * Apply default coversion for the given value depending on the oid
+ * and the character range of the value.
+ *
+ * @param oid the object identifier for the DN entry
+ * @param value the value associated with it
+ * @return the ASN.1 equivalent for the string value.
+ */
+ public ASN1Primitive getConvertedValue(
+ ASN1ObjectIdentifier oid,
+ String value)
+ {
+ if (value.length() != 0 && value.charAt(0) == '#')
+ {
+ try
+ {
+ return convertHexEncoded(value, 1);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("can't recode value for oid " + oid.getId());
+ }
+ }
+ else
+ {
+ if (value.length() != 0 && value.charAt(0) == '\\')
+ {
+ value = value.substring(1);
+ }
+ if (oid.equals(X509Name.EmailAddress) || oid.equals(X509Name.DC))
+ {
+ return new DERIA5String(value);
+ }
+ else if (oid.equals(X509Name.DATE_OF_BIRTH)) // accept time string as well as # (for compatibility)
+ {
+ return new DERGeneralizedTime(value);
+ }
+ else if (oid.equals(X509Name.C) || oid.equals(X509Name.SN) || oid.equals(X509Name.DN_QUALIFIER)
+ || oid.equals(X509Name.TELEPHONE_NUMBER))
+ {
+ return new DERPrintableString(value);
+ }
+ }
+
+ return new DERUTF8String(value);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/X509Extension.java b/core/src/main/java/org/bouncycastle/asn1/x509/X509Extension.java
new file mode 100644
index 00000000..f020bcb5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/X509Extension.java
@@ -0,0 +1,248 @@
+package org.bouncycastle.asn1.x509;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DERBoolean;
+
+/**
+ * an object for the elements in the X.509 V3 extension block.
+ */
+public class X509Extension
+{
+ /**
+ * Subject Directory Attributes
+ */
+ public static final ASN1ObjectIdentifier subjectDirectoryAttributes = new ASN1ObjectIdentifier("2.5.29.9");
+
+ /**
+ * Subject Key Identifier
+ */
+ public static final ASN1ObjectIdentifier subjectKeyIdentifier = new ASN1ObjectIdentifier("2.5.29.14");
+
+ /**
+ * Key Usage
+ */
+ public static final ASN1ObjectIdentifier keyUsage = new ASN1ObjectIdentifier("2.5.29.15");
+
+ /**
+ * Private Key Usage Period
+ */
+ public static final ASN1ObjectIdentifier privateKeyUsagePeriod = new ASN1ObjectIdentifier("2.5.29.16");
+
+ /**
+ * Subject Alternative Name
+ */
+ public static final ASN1ObjectIdentifier subjectAlternativeName = new ASN1ObjectIdentifier("2.5.29.17");
+
+ /**
+ * Issuer Alternative Name
+ */
+ public static final ASN1ObjectIdentifier issuerAlternativeName = new ASN1ObjectIdentifier("2.5.29.18");
+
+ /**
+ * Basic Constraints
+ */
+ public static final ASN1ObjectIdentifier basicConstraints = new ASN1ObjectIdentifier("2.5.29.19");
+
+ /**
+ * CRL Number
+ */
+ public static final ASN1ObjectIdentifier cRLNumber = new ASN1ObjectIdentifier("2.5.29.20");
+
+ /**
+ * Reason code
+ */
+ public static final ASN1ObjectIdentifier reasonCode = new ASN1ObjectIdentifier("2.5.29.21");
+
+ /**
+ * Hold Instruction Code
+ */
+ public static final ASN1ObjectIdentifier instructionCode = new ASN1ObjectIdentifier("2.5.29.23");
+
+ /**
+ * Invalidity Date
+ */
+ public static final ASN1ObjectIdentifier invalidityDate = new ASN1ObjectIdentifier("2.5.29.24");
+
+ /**
+ * Delta CRL indicator
+ */
+ public static final ASN1ObjectIdentifier deltaCRLIndicator = new ASN1ObjectIdentifier("2.5.29.27");
+
+ /**
+ * Issuing Distribution Point
+ */
+ public static final ASN1ObjectIdentifier issuingDistributionPoint = new ASN1ObjectIdentifier("2.5.29.28");
+
+ /**
+ * Certificate Issuer
+ */
+ public static final ASN1ObjectIdentifier certificateIssuer = new ASN1ObjectIdentifier("2.5.29.29");
+
+ /**
+ * Name Constraints
+ */
+ public static final ASN1ObjectIdentifier nameConstraints = new ASN1ObjectIdentifier("2.5.29.30");
+
+ /**
+ * CRL Distribution Points
+ */
+ public static final ASN1ObjectIdentifier cRLDistributionPoints = new ASN1ObjectIdentifier("2.5.29.31");
+
+ /**
+ * Certificate Policies
+ */
+ public static final ASN1ObjectIdentifier certificatePolicies = new ASN1ObjectIdentifier("2.5.29.32");
+
+ /**
+ * Policy Mappings
+ */
+ public static final ASN1ObjectIdentifier policyMappings = new ASN1ObjectIdentifier("2.5.29.33");
+
+ /**
+ * Authority Key Identifier
+ */
+ public static final ASN1ObjectIdentifier authorityKeyIdentifier = new ASN1ObjectIdentifier("2.5.29.35");
+
+ /**
+ * Policy Constraints
+ */
+ public static final ASN1ObjectIdentifier policyConstraints = new ASN1ObjectIdentifier("2.5.29.36");
+
+ /**
+ * Extended Key Usage
+ */
+ public static final ASN1ObjectIdentifier extendedKeyUsage = new ASN1ObjectIdentifier("2.5.29.37");
+
+ /**
+ * Freshest CRL
+ */
+ public static final ASN1ObjectIdentifier freshestCRL = new ASN1ObjectIdentifier("2.5.29.46");
+
+ /**
+ * Inhibit Any Policy
+ */
+ public static final ASN1ObjectIdentifier inhibitAnyPolicy = new ASN1ObjectIdentifier("2.5.29.54");
+
+ /**
+ * Authority Info Access
+ */
+ public static final ASN1ObjectIdentifier authorityInfoAccess = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.1");
+
+ /**
+ * Subject Info Access
+ */
+ public static final ASN1ObjectIdentifier subjectInfoAccess = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.11");
+
+ /**
+ * Logo Type
+ */
+ public static final ASN1ObjectIdentifier logoType = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.12");
+
+ /**
+ * BiometricInfo
+ */
+ public static final ASN1ObjectIdentifier biometricInfo = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.2");
+
+ /**
+ * QCStatements
+ */
+ public static final ASN1ObjectIdentifier qCStatements = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.3");
+
+ /**
+ * Audit identity extension in attribute certificates.
+ */
+ public static final ASN1ObjectIdentifier auditIdentity = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.4");
+
+ /**
+ * NoRevAvail extension in attribute certificates.
+ */
+ public static final ASN1ObjectIdentifier noRevAvail = new ASN1ObjectIdentifier("2.5.29.56");
+
+ /**
+ * TargetInformation extension in attribute certificates.
+ */
+ public static final ASN1ObjectIdentifier targetInformation = new ASN1ObjectIdentifier("2.5.29.55");
+
+ boolean critical;
+ ASN1OctetString value;
+
+ public X509Extension(
+ DERBoolean critical,
+ ASN1OctetString value)
+ {
+ this.critical = critical.isTrue();
+ this.value = value;
+ }
+
+ public X509Extension(
+ boolean critical,
+ ASN1OctetString value)
+ {
+ this.critical = critical;
+ this.value = value;
+ }
+
+ public boolean isCritical()
+ {
+ return critical;
+ }
+
+ public ASN1OctetString getValue()
+ {
+ return value;
+ }
+
+ public ASN1Encodable getParsedValue()
+ {
+ return convertValueToObject(this);
+ }
+
+ public int hashCode()
+ {
+ if (this.isCritical())
+ {
+ return this.getValue().hashCode();
+ }
+
+ return ~this.getValue().hashCode();
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof X509Extension))
+ {
+ return false;
+ }
+
+ X509Extension other = (X509Extension)o;
+
+ return other.getValue().equals(this.getValue())
+ && (other.isCritical() == this.isCritical());
+ }
+
+ /**
+ * Convert the value of the passed in extension to an object
+ * @param ext the extension to parse
+ * @return the object the value string contains
+ * @exception IllegalArgumentException if conversion is not possible
+ */
+ public static ASN1Primitive convertValueToObject(
+ X509Extension ext)
+ throws IllegalArgumentException
+ {
+ try
+ {
+ return ASN1Primitive.fromByteArray(ext.getValue().getOctets());
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("can't convert extension: " + e);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/X509Extensions.java b/core/src/main/java/org/bouncycastle/asn1/x509/X509Extensions.java
new file mode 100644
index 00000000..c72e3cc0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/X509Extensions.java
@@ -0,0 +1,489 @@
+package org.bouncycastle.asn1.x509;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBoolean;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * @deprecated use Extensions
+ */
+public class X509Extensions
+ extends ASN1Object
+{
+ /**
+ * Subject Directory Attributes
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier SubjectDirectoryAttributes = new ASN1ObjectIdentifier("2.5.29.9");
+
+ /**
+ * Subject Key Identifier
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier SubjectKeyIdentifier = new ASN1ObjectIdentifier("2.5.29.14");
+
+ /**
+ * Key Usage
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier KeyUsage = new ASN1ObjectIdentifier("2.5.29.15");
+
+ /**
+ * Private Key Usage Period
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier PrivateKeyUsagePeriod = new ASN1ObjectIdentifier("2.5.29.16");
+
+ /**
+ * Subject Alternative Name
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier SubjectAlternativeName = new ASN1ObjectIdentifier("2.5.29.17");
+
+ /**
+ * Issuer Alternative Name
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier IssuerAlternativeName = new ASN1ObjectIdentifier("2.5.29.18");
+
+ /**
+ * Basic Constraints
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier BasicConstraints = new ASN1ObjectIdentifier("2.5.29.19");
+
+ /**
+ * CRL Number
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier CRLNumber = new ASN1ObjectIdentifier("2.5.29.20");
+
+ /**
+ * Reason code
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier ReasonCode = new ASN1ObjectIdentifier("2.5.29.21");
+
+ /**
+ * Hold Instruction Code
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier InstructionCode = new ASN1ObjectIdentifier("2.5.29.23");
+
+ /**
+ * Invalidity Date
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier InvalidityDate = new ASN1ObjectIdentifier("2.5.29.24");
+
+ /**
+ * Delta CRL indicator
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier DeltaCRLIndicator = new ASN1ObjectIdentifier("2.5.29.27");
+
+ /**
+ * Issuing Distribution Point
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier IssuingDistributionPoint = new ASN1ObjectIdentifier("2.5.29.28");
+
+ /**
+ * Certificate Issuer
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier CertificateIssuer = new ASN1ObjectIdentifier("2.5.29.29");
+
+ /**
+ * Name Constraints
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier NameConstraints = new ASN1ObjectIdentifier("2.5.29.30");
+
+ /**
+ * CRL Distribution Points
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier CRLDistributionPoints = new ASN1ObjectIdentifier("2.5.29.31");
+
+ /**
+ * Certificate Policies
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier CertificatePolicies = new ASN1ObjectIdentifier("2.5.29.32");
+
+ /**
+ * Policy Mappings
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier PolicyMappings = new ASN1ObjectIdentifier("2.5.29.33");
+
+ /**
+ * Authority Key Identifier
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier AuthorityKeyIdentifier = new ASN1ObjectIdentifier("2.5.29.35");
+
+ /**
+ * Policy Constraints
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier PolicyConstraints = new ASN1ObjectIdentifier("2.5.29.36");
+
+ /**
+ * Extended Key Usage
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier ExtendedKeyUsage = new ASN1ObjectIdentifier("2.5.29.37");
+
+ /**
+ * Freshest CRL
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier FreshestCRL = new ASN1ObjectIdentifier("2.5.29.46");
+
+ /**
+ * Inhibit Any Policy
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier InhibitAnyPolicy = new ASN1ObjectIdentifier("2.5.29.54");
+
+ /**
+ * Authority Info Access
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier AuthorityInfoAccess = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.1");
+
+ /**
+ * Subject Info Access
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier SubjectInfoAccess = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.11");
+
+ /**
+ * Logo Type
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier LogoType = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.12");
+
+ /**
+ * BiometricInfo
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier BiometricInfo = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.2");
+
+ /**
+ * QCStatements
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier QCStatements = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.3");
+
+ /**
+ * Audit identity extension in attribute certificates.
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier AuditIdentity = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.4");
+
+ /**
+ * NoRevAvail extension in attribute certificates.
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier NoRevAvail = new ASN1ObjectIdentifier("2.5.29.56");
+
+ /**
+ * TargetInformation extension in attribute certificates.
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier TargetInformation = new ASN1ObjectIdentifier("2.5.29.55");
+
+ private Hashtable extensions = new Hashtable();
+ private Vector ordering = new Vector();
+
+ public static X509Extensions getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static X509Extensions getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof X509Extensions)
+ {
+ return (X509Extensions)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new X509Extensions((ASN1Sequence)obj);
+ }
+
+ if (obj instanceof Extensions)
+ {
+ return new X509Extensions((ASN1Sequence)((Extensions)obj).toASN1Primitive());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * Constructor from ASN1Sequence.
+ *
+ * the extensions are a list of constructed sequences, either with (OID, OctetString) or (OID, Boolean, OctetString)
+ */
+ public X509Extensions(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ ASN1Sequence s = ASN1Sequence.getInstance(e.nextElement());
+
+ if (s.size() == 3)
+ {
+ extensions.put(s.getObjectAt(0), new X509Extension(DERBoolean.getInstance(s.getObjectAt(1)), ASN1OctetString.getInstance(s.getObjectAt(2))));
+ }
+ else if (s.size() == 2)
+ {
+ extensions.put(s.getObjectAt(0), new X509Extension(false, ASN1OctetString.getInstance(s.getObjectAt(1))));
+ }
+ else
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + s.size());
+ }
+
+ ordering.addElement(s.getObjectAt(0));
+ }
+ }
+
+ /**
+ * constructor from a table of extensions.
+ * <p>
+ * it's is assumed the table contains OID/String pairs.
+ */
+ public X509Extensions(
+ Hashtable extensions)
+ {
+ this(null, extensions);
+ }
+
+ /**
+ * Constructor from a table of extensions with ordering.
+ * <p>
+ * It's is assumed the table contains OID/String pairs.
+ * @deprecated use Extensions
+ */
+ public X509Extensions(
+ Vector ordering,
+ Hashtable extensions)
+ {
+ Enumeration e;
+
+ if (ordering == null)
+ {
+ e = extensions.keys();
+ }
+ else
+ {
+ e = ordering.elements();
+ }
+
+ while (e.hasMoreElements())
+ {
+ this.ordering.addElement(ASN1ObjectIdentifier.getInstance(e.nextElement()));
+ }
+
+ e = this.ordering.elements();
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(e.nextElement());
+ X509Extension ext = (X509Extension)extensions.get(oid);
+
+ this.extensions.put(oid, ext);
+ }
+ }
+
+ /**
+ * Constructor from two vectors
+ *
+ * @param objectIDs a vector of the object identifiers.
+ * @param values a vector of the extension values.
+ * @deprecated use Extensions
+ */
+ public X509Extensions(
+ Vector objectIDs,
+ Vector values)
+ {
+ Enumeration e = objectIDs.elements();
+
+ while (e.hasMoreElements())
+ {
+ this.ordering.addElement(e.nextElement());
+ }
+
+ int count = 0;
+
+ e = this.ordering.elements();
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
+ X509Extension ext = (X509Extension)values.elementAt(count);
+
+ this.extensions.put(oid, ext);
+ count++;
+ }
+ }
+
+ /**
+ * return an Enumeration of the extension field's object ids.
+ */
+ public Enumeration oids()
+ {
+ return ordering.elements();
+ }
+
+ /**
+ * return the extension represented by the object identifier
+ * passed in.
+ *
+ * @return the extension if it's present, null otherwise.
+ */
+ public X509Extension getExtension(
+ DERObjectIdentifier oid)
+ {
+ return (X509Extension)extensions.get(oid);
+ }
+
+ /**
+ * @deprecated
+ * @param oid
+ * @return
+ */
+ public X509Extension getExtension(
+ ASN1ObjectIdentifier oid)
+ {
+ return (X509Extension)extensions.get(oid);
+ }
+
+ /**
+ * <pre>
+ * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
+ *
+ * Extension ::= SEQUENCE {
+ * extnId EXTENSION.&amp;id ({ExtensionSet}),
+ * critical BOOLEAN DEFAULT FALSE,
+ * extnValue OCTET STRING }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+ Enumeration e = ordering.elements();
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
+ X509Extension ext = (X509Extension)extensions.get(oid);
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(oid);
+
+ if (ext.isCritical())
+ {
+ v.add(DERBoolean.TRUE);
+ }
+
+ v.add(ext.getValue());
+
+ vec.add(new DERSequence(v));
+ }
+
+ return new DERSequence(vec);
+ }
+
+ public boolean equivalent(
+ X509Extensions other)
+ {
+ if (extensions.size() != other.extensions.size())
+ {
+ return false;
+ }
+
+ Enumeration e1 = extensions.keys();
+
+ while (e1.hasMoreElements())
+ {
+ Object key = e1.nextElement();
+
+ if (!extensions.get(key).equals(other.extensions.get(key)))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public ASN1ObjectIdentifier[] getExtensionOIDs()
+ {
+ return toOidArray(ordering);
+ }
+
+ public ASN1ObjectIdentifier[] getNonCriticalExtensionOIDs()
+ {
+ return getExtensionOIDs(false);
+ }
+
+ public ASN1ObjectIdentifier[] getCriticalExtensionOIDs()
+ {
+ return getExtensionOIDs(true);
+ }
+
+ private ASN1ObjectIdentifier[] getExtensionOIDs(boolean isCritical)
+ {
+ Vector oidVec = new Vector();
+
+ for (int i = 0; i != ordering.size(); i++)
+ {
+ Object oid = ordering.elementAt(i);
+
+ if (((X509Extension)extensions.get(oid)).isCritical() == isCritical)
+ {
+ oidVec.addElement(oid);
+ }
+ }
+
+ return toOidArray(oidVec);
+ }
+
+ private ASN1ObjectIdentifier[] toOidArray(Vector oidVec)
+ {
+ ASN1ObjectIdentifier[] oids = new ASN1ObjectIdentifier[oidVec.size()];
+
+ for (int i = 0; i != oids.length; i++)
+ {
+ oids[i] = (ASN1ObjectIdentifier)oidVec.elementAt(i);
+ }
+ return oids;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/X509ExtensionsGenerator.java b/core/src/main/java/org/bouncycastle/asn1/x509/X509ExtensionsGenerator.java
new file mode 100644
index 00000000..468d1b96
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/X509ExtensionsGenerator.java
@@ -0,0 +1,117 @@
+package org.bouncycastle.asn1.x509;
+
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DEROctetString;
+
+/**
+ * Generator for X.509 extensions
+ * @deprecated use org.bouncycastle.asn1.x509.ExtensionsGenerator
+ */
+public class X509ExtensionsGenerator
+{
+ private Hashtable extensions = new Hashtable();
+ private Vector extOrdering = new Vector();
+
+ /**
+ * Reset the generator
+ */
+ public void reset()
+ {
+ extensions = new Hashtable();
+ extOrdering = new Vector();
+ }
+
+ /**
+ * @deprecated use ASN1ObjectIdentifier
+ */
+ public void addExtension(
+ DERObjectIdentifier oid,
+ boolean critical,
+ ASN1Encodable value)
+ {
+ addExtension(new ASN1ObjectIdentifier(oid.getId()), critical, value);
+ }
+
+ /**
+ * @deprecated use ASN1ObjectIdentifier
+ */
+ public void addExtension(
+ DERObjectIdentifier oid,
+ boolean critical,
+ byte[] value)
+ {
+ addExtension(new ASN1ObjectIdentifier(oid.getId()), critical, value);
+ }
+
+ /**
+ * Add an extension with the given oid and the passed in value to be included
+ * in the OCTET STRING associated with the extension.
+ *
+ * @param oid OID for the extension.
+ * @param critical true if critical, false otherwise.
+ * @param value the ASN.1 object to be included in the extension.
+ */
+ public void addExtension(
+ ASN1ObjectIdentifier oid,
+ boolean critical,
+ ASN1Encodable value)
+ {
+ try
+ {
+ this.addExtension(oid, critical, value.toASN1Primitive().getEncoded(ASN1Encoding.DER));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("error encoding value: " + e);
+ }
+ }
+
+ /**
+ * Add an extension with the given oid and the passed in byte array to be wrapped in the
+ * OCTET STRING associated with the extension.
+ *
+ * @param oid OID for the extension.
+ * @param critical true if critical, false otherwise.
+ * @param value the byte array to be wrapped.
+ */
+ public void addExtension(
+ ASN1ObjectIdentifier oid,
+ boolean critical,
+ byte[] value)
+ {
+ if (extensions.containsKey(oid))
+ {
+ throw new IllegalArgumentException("extension " + oid + " already added");
+ }
+
+ extOrdering.addElement(oid);
+ extensions.put(oid, new X509Extension(critical, new DEROctetString(value)));
+ }
+
+ /**
+ * Return true if there are no extension present in this generator.
+ *
+ * @return true if empty, false otherwise
+ */
+ public boolean isEmpty()
+ {
+ return extOrdering.isEmpty();
+ }
+
+ /**
+ * Generate an X509Extensions object based on the current state of the generator.
+ *
+ * @return an X09Extensions object.
+ */
+ public X509Extensions generate()
+ {
+ return new X509Extensions(extOrdering, extensions);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/X509Name.java b/core/src/main/java/org/bouncycastle/asn1/x509/X509Name.java
new file mode 100644
index 00000000..af2c9a93
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/X509Name.java
@@ -0,0 +1,1381 @@
+package org.bouncycastle.asn1.x509;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1String;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.DERUniversalString;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Hex;
+
+/**
+ * <pre>
+ * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+ *
+ * RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue
+ *
+ * AttributeTypeAndValue ::= SEQUENCE {
+ * type OBJECT IDENTIFIER,
+ * value ANY }
+ * </pre>
+ * @deprecated use org.bouncycastle.asn1.x500.X500Name.
+ */
+public class X509Name
+ extends ASN1Object
+{
+ /**
+ * country code - StringType(SIZE(2))
+ * @deprecated use a X500NameStyle
+ */
+ public static final ASN1ObjectIdentifier C = new ASN1ObjectIdentifier("2.5.4.6");
+
+ /**
+ * organization - StringType(SIZE(1..64))
+ * @deprecated use a X500NameStyle
+ */
+ public static final ASN1ObjectIdentifier O = new ASN1ObjectIdentifier("2.5.4.10");
+
+ /**
+ * organizational unit name - StringType(SIZE(1..64))
+ * @deprecated use a X500NameStyle
+ */
+ public static final ASN1ObjectIdentifier OU = new ASN1ObjectIdentifier("2.5.4.11");
+
+ /**
+ * Title
+ * @deprecated use a X500NameStyle
+ */
+ public static final ASN1ObjectIdentifier T = new ASN1ObjectIdentifier("2.5.4.12");
+
+ /**
+ * common name - StringType(SIZE(1..64))
+ * @deprecated use a X500NameStyle
+ */
+ public static final ASN1ObjectIdentifier CN = new ASN1ObjectIdentifier("2.5.4.3");
+
+ /**
+ * device serial number name - StringType(SIZE(1..64))
+ */
+ public static final ASN1ObjectIdentifier SN = new ASN1ObjectIdentifier("2.5.4.5");
+
+ /**
+ * street - StringType(SIZE(1..64))
+ */
+ public static final ASN1ObjectIdentifier STREET = new ASN1ObjectIdentifier("2.5.4.9");
+
+ /**
+ * device serial number name - StringType(SIZE(1..64))
+ */
+ public static final ASN1ObjectIdentifier SERIALNUMBER = SN;
+
+ /**
+ * locality name - StringType(SIZE(1..64))
+ */
+ public static final ASN1ObjectIdentifier L = new ASN1ObjectIdentifier("2.5.4.7");
+
+ /**
+ * state, or province name - StringType(SIZE(1..64))
+ */
+ public static final ASN1ObjectIdentifier ST = new ASN1ObjectIdentifier("2.5.4.8");
+
+ /**
+ * Naming attributes of type X520name
+ */
+ public static final ASN1ObjectIdentifier SURNAME = new ASN1ObjectIdentifier("2.5.4.4");
+ public static final ASN1ObjectIdentifier GIVENNAME = new ASN1ObjectIdentifier("2.5.4.42");
+ public static final ASN1ObjectIdentifier INITIALS = new ASN1ObjectIdentifier("2.5.4.43");
+ public static final ASN1ObjectIdentifier GENERATION = new ASN1ObjectIdentifier("2.5.4.44");
+ public static final ASN1ObjectIdentifier UNIQUE_IDENTIFIER = new ASN1ObjectIdentifier("2.5.4.45");
+
+ /**
+ * businessCategory - DirectoryString(SIZE(1..128)
+ */
+ public static final ASN1ObjectIdentifier BUSINESS_CATEGORY = new ASN1ObjectIdentifier(
+ "2.5.4.15");
+
+ /**
+ * postalCode - DirectoryString(SIZE(1..40)
+ */
+ public static final ASN1ObjectIdentifier POSTAL_CODE = new ASN1ObjectIdentifier(
+ "2.5.4.17");
+
+ /**
+ * dnQualifier - DirectoryString(SIZE(1..64)
+ */
+ public static final ASN1ObjectIdentifier DN_QUALIFIER = new ASN1ObjectIdentifier(
+ "2.5.4.46");
+
+ /**
+ * RFC 3039 Pseudonym - DirectoryString(SIZE(1..64)
+ */
+ public static final ASN1ObjectIdentifier PSEUDONYM = new ASN1ObjectIdentifier(
+ "2.5.4.65");
+
+
+ /**
+ * RFC 3039 DateOfBirth - GeneralizedTime - YYYYMMDD000000Z
+ */
+ public static final ASN1ObjectIdentifier DATE_OF_BIRTH = new ASN1ObjectIdentifier(
+ "1.3.6.1.5.5.7.9.1");
+
+ /**
+ * RFC 3039 PlaceOfBirth - DirectoryString(SIZE(1..128)
+ */
+ public static final ASN1ObjectIdentifier PLACE_OF_BIRTH = new ASN1ObjectIdentifier(
+ "1.3.6.1.5.5.7.9.2");
+
+ /**
+ * RFC 3039 Gender - PrintableString (SIZE(1)) -- "M", "F", "m" or "f"
+ */
+ public static final ASN1ObjectIdentifier GENDER = new ASN1ObjectIdentifier(
+ "1.3.6.1.5.5.7.9.3");
+
+ /**
+ * RFC 3039 CountryOfCitizenship - PrintableString (SIZE (2)) -- ISO 3166
+ * codes only
+ */
+ public static final ASN1ObjectIdentifier COUNTRY_OF_CITIZENSHIP = new ASN1ObjectIdentifier(
+ "1.3.6.1.5.5.7.9.4");
+
+ /**
+ * RFC 3039 CountryOfResidence - PrintableString (SIZE (2)) -- ISO 3166
+ * codes only
+ */
+ public static final ASN1ObjectIdentifier COUNTRY_OF_RESIDENCE = new ASN1ObjectIdentifier(
+ "1.3.6.1.5.5.7.9.5");
+
+
+ /**
+ * ISIS-MTT NameAtBirth - DirectoryString(SIZE(1..64)
+ */
+ public static final ASN1ObjectIdentifier NAME_AT_BIRTH = new ASN1ObjectIdentifier("1.3.36.8.3.14");
+
+ /**
+ * RFC 3039 PostalAddress - SEQUENCE SIZE (1..6) OF
+ * DirectoryString(SIZE(1..30))
+ */
+ public static final ASN1ObjectIdentifier POSTAL_ADDRESS = new ASN1ObjectIdentifier("2.5.4.16");
+
+ /**
+ * RFC 2256 dmdName
+ */
+ public static final ASN1ObjectIdentifier DMD_NAME = new ASN1ObjectIdentifier("2.5.4.54");
+
+ /**
+ * id-at-telephoneNumber
+ */
+ public static final ASN1ObjectIdentifier TELEPHONE_NUMBER = X509ObjectIdentifiers.id_at_telephoneNumber;
+
+ /**
+ * id-at-name
+ */
+ public static final ASN1ObjectIdentifier NAME = X509ObjectIdentifiers.id_at_name;
+
+ /**
+ * Email address (RSA PKCS#9 extension) - IA5String.
+ * <p>Note: if you're trying to be ultra orthodox, don't use this! It shouldn't be in here.
+ * @deprecated use a X500NameStyle
+ */
+ public static final ASN1ObjectIdentifier EmailAddress = PKCSObjectIdentifiers.pkcs_9_at_emailAddress;
+
+ /**
+ * more from PKCS#9
+ */
+ public static final ASN1ObjectIdentifier UnstructuredName = PKCSObjectIdentifiers.pkcs_9_at_unstructuredName;
+ public static final ASN1ObjectIdentifier UnstructuredAddress = PKCSObjectIdentifiers.pkcs_9_at_unstructuredAddress;
+
+ /**
+ * email address in Verisign certificates
+ */
+ public static final ASN1ObjectIdentifier E = EmailAddress;
+
+ /*
+ * others...
+ */
+ public static final ASN1ObjectIdentifier DC = new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.25");
+
+ /**
+ * LDAP User id.
+ */
+ public static final ASN1ObjectIdentifier UID = new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.1");
+
+ /**
+ * determines whether or not strings should be processed and printed
+ * from back to front.
+ */
+ public static boolean DefaultReverse = false;
+
+ /**
+ * default look up table translating OID values into their common symbols following
+ * the convention in RFC 2253 with a few extras
+ */
+ public static final Hashtable DefaultSymbols = new Hashtable();
+
+ /**
+ * look up table translating OID values into their common symbols following the convention in RFC 2253
+ *
+ */
+ public static final Hashtable RFC2253Symbols = new Hashtable();
+
+ /**
+ * look up table translating OID values into their common symbols following the convention in RFC 1779
+ *
+ */
+ public static final Hashtable RFC1779Symbols = new Hashtable();
+
+ /**
+ * look up table translating common symbols into their OIDS.
+ */
+ public static final Hashtable DefaultLookUp = new Hashtable();
+
+ /**
+ * look up table translating OID values into their common symbols
+ * @deprecated use DefaultSymbols
+ */
+ public static final Hashtable OIDLookUp = DefaultSymbols;
+
+ /**
+ * look up table translating string values into their OIDS -
+ * @deprecated use DefaultLookUp
+ */
+ public static final Hashtable SymbolLookUp = DefaultLookUp;
+
+ private static final Boolean TRUE = new Boolean(true); // for J2ME compatibility
+ private static final Boolean FALSE = new Boolean(false);
+
+ static
+ {
+ DefaultSymbols.put(C, "C");
+ DefaultSymbols.put(O, "O");
+ DefaultSymbols.put(T, "T");
+ DefaultSymbols.put(OU, "OU");
+ DefaultSymbols.put(CN, "CN");
+ DefaultSymbols.put(L, "L");
+ DefaultSymbols.put(ST, "ST");
+ DefaultSymbols.put(SN, "SERIALNUMBER");
+ DefaultSymbols.put(EmailAddress, "E");
+ DefaultSymbols.put(DC, "DC");
+ DefaultSymbols.put(UID, "UID");
+ DefaultSymbols.put(STREET, "STREET");
+ DefaultSymbols.put(SURNAME, "SURNAME");
+ DefaultSymbols.put(GIVENNAME, "GIVENNAME");
+ DefaultSymbols.put(INITIALS, "INITIALS");
+ DefaultSymbols.put(GENERATION, "GENERATION");
+ DefaultSymbols.put(UnstructuredAddress, "unstructuredAddress");
+ DefaultSymbols.put(UnstructuredName, "unstructuredName");
+ DefaultSymbols.put(UNIQUE_IDENTIFIER, "UniqueIdentifier");
+ DefaultSymbols.put(DN_QUALIFIER, "DN");
+ DefaultSymbols.put(PSEUDONYM, "Pseudonym");
+ DefaultSymbols.put(POSTAL_ADDRESS, "PostalAddress");
+ DefaultSymbols.put(NAME_AT_BIRTH, "NameAtBirth");
+ DefaultSymbols.put(COUNTRY_OF_CITIZENSHIP, "CountryOfCitizenship");
+ DefaultSymbols.put(COUNTRY_OF_RESIDENCE, "CountryOfResidence");
+ DefaultSymbols.put(GENDER, "Gender");
+ DefaultSymbols.put(PLACE_OF_BIRTH, "PlaceOfBirth");
+ DefaultSymbols.put(DATE_OF_BIRTH, "DateOfBirth");
+ DefaultSymbols.put(POSTAL_CODE, "PostalCode");
+ DefaultSymbols.put(BUSINESS_CATEGORY, "BusinessCategory");
+ DefaultSymbols.put(TELEPHONE_NUMBER, "TelephoneNumber");
+ DefaultSymbols.put(NAME, "Name");
+
+ RFC2253Symbols.put(C, "C");
+ RFC2253Symbols.put(O, "O");
+ RFC2253Symbols.put(OU, "OU");
+ RFC2253Symbols.put(CN, "CN");
+ RFC2253Symbols.put(L, "L");
+ RFC2253Symbols.put(ST, "ST");
+ RFC2253Symbols.put(STREET, "STREET");
+ RFC2253Symbols.put(DC, "DC");
+ RFC2253Symbols.put(UID, "UID");
+
+ RFC1779Symbols.put(C, "C");
+ RFC1779Symbols.put(O, "O");
+ RFC1779Symbols.put(OU, "OU");
+ RFC1779Symbols.put(CN, "CN");
+ RFC1779Symbols.put(L, "L");
+ RFC1779Symbols.put(ST, "ST");
+ RFC1779Symbols.put(STREET, "STREET");
+
+ DefaultLookUp.put("c", C);
+ DefaultLookUp.put("o", O);
+ DefaultLookUp.put("t", T);
+ DefaultLookUp.put("ou", OU);
+ DefaultLookUp.put("cn", CN);
+ DefaultLookUp.put("l", L);
+ DefaultLookUp.put("st", ST);
+ DefaultLookUp.put("sn", SN);
+ DefaultLookUp.put("serialnumber", SN);
+ DefaultLookUp.put("street", STREET);
+ DefaultLookUp.put("emailaddress", E);
+ DefaultLookUp.put("dc", DC);
+ DefaultLookUp.put("e", E);
+ DefaultLookUp.put("uid", UID);
+ DefaultLookUp.put("surname", SURNAME);
+ DefaultLookUp.put("givenname", GIVENNAME);
+ DefaultLookUp.put("initials", INITIALS);
+ DefaultLookUp.put("generation", GENERATION);
+ DefaultLookUp.put("unstructuredaddress", UnstructuredAddress);
+ DefaultLookUp.put("unstructuredname", UnstructuredName);
+ DefaultLookUp.put("uniqueidentifier", UNIQUE_IDENTIFIER);
+ DefaultLookUp.put("dn", DN_QUALIFIER);
+ DefaultLookUp.put("pseudonym", PSEUDONYM);
+ DefaultLookUp.put("postaladdress", POSTAL_ADDRESS);
+ DefaultLookUp.put("nameofbirth", NAME_AT_BIRTH);
+ DefaultLookUp.put("countryofcitizenship", COUNTRY_OF_CITIZENSHIP);
+ DefaultLookUp.put("countryofresidence", COUNTRY_OF_RESIDENCE);
+ DefaultLookUp.put("gender", GENDER);
+ DefaultLookUp.put("placeofbirth", PLACE_OF_BIRTH);
+ DefaultLookUp.put("dateofbirth", DATE_OF_BIRTH);
+ DefaultLookUp.put("postalcode", POSTAL_CODE);
+ DefaultLookUp.put("businesscategory", BUSINESS_CATEGORY);
+ DefaultLookUp.put("telephonenumber", TELEPHONE_NUMBER);
+ DefaultLookUp.put("name", NAME);
+ }
+
+ private X509NameEntryConverter converter = null;
+ private Vector ordering = new Vector();
+ private Vector values = new Vector();
+ private Vector added = new Vector();
+
+ private ASN1Sequence seq;
+
+ private boolean isHashCodeCalculated;
+ private int hashCodeValue;
+
+ /**
+ * Return a X509Name based on the passed in tagged object.
+ *
+ * @param obj tag object holding name.
+ * @param explicit true if explicitly tagged false otherwise.
+ * @return the X509Name
+ */
+ public static X509Name getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static X509Name getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof X509Name)
+ {
+ return (X509Name)obj;
+ }
+ else if (obj instanceof X500Name)
+ {
+ return new X509Name(ASN1Sequence.getInstance(((X500Name)obj).toASN1Primitive()));
+ }
+ else if (obj != null)
+ {
+ return new X509Name(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ protected X509Name()
+ {
+ // constructure use by new X500 Name class
+ }
+ /**
+ * Constructor from ASN1Sequence
+ *
+ * the principal will be a list of constructed sets, each containing an (OID, String) pair.
+ * @deprecated use X500Name.getInstance()
+ */
+ public X509Name(
+ ASN1Sequence seq)
+ {
+ this.seq = seq;
+
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ ASN1Set set = ASN1Set.getInstance(((ASN1Encodable)e.nextElement()).toASN1Primitive());
+
+ for (int i = 0; i < set.size(); i++)
+ {
+ ASN1Sequence s = ASN1Sequence.getInstance(set.getObjectAt(i).toASN1Primitive());
+
+ if (s.size() != 2)
+ {
+ throw new IllegalArgumentException("badly sized pair");
+ }
+
+ ordering.addElement(ASN1ObjectIdentifier.getInstance(s.getObjectAt(0)));
+
+ ASN1Encodable value = s.getObjectAt(1);
+ if (value instanceof ASN1String && !(value instanceof DERUniversalString))
+ {
+ String v = ((ASN1String)value).getString();
+ if (v.length() > 0 && v.charAt(0) == '#')
+ {
+ values.addElement("\\" + v);
+ }
+ else
+ {
+ values.addElement(v);
+ }
+ }
+ else
+ {
+ try
+ {
+ values.addElement("#" + bytesToString(Hex.encode(value.toASN1Primitive().getEncoded(ASN1Encoding.DER))));
+ }
+ catch (IOException e1)
+ {
+ throw new IllegalArgumentException("cannot encode value");
+ }
+ }
+ added.addElement((i != 0) ? TRUE : FALSE); // to allow earlier JDK compatibility
+ }
+ }
+ }
+
+ /**
+ * constructor from a table of attributes.
+ * <p>
+ * it's is assumed the table contains OID/String pairs, and the contents
+ * of the table are copied into an internal table as part of the
+ * construction process.
+ * <p>
+ * <b>Note:</b> if the name you are trying to generate should be
+ * following a specific ordering, you should use the constructor
+ * with the ordering specified below.
+ * @deprecated use an ordered constructor! The hashtable ordering is rarely correct
+ */
+ public X509Name(
+ Hashtable attributes)
+ {
+ this(null, attributes);
+ }
+
+ /**
+ * Constructor from a table of attributes with ordering.
+ * <p>
+ * it's is assumed the table contains OID/String pairs, and the contents
+ * of the table are copied into an internal table as part of the
+ * construction process. The ordering vector should contain the OIDs
+ * in the order they are meant to be encoded or printed in toString.
+ */
+ public X509Name(
+ Vector ordering,
+ Hashtable attributes)
+ {
+ this(ordering, attributes, new X509DefaultEntryConverter());
+ }
+
+ /**
+ * Constructor from a table of attributes with ordering.
+ * <p>
+ * it's is assumed the table contains OID/String pairs, and the contents
+ * of the table are copied into an internal table as part of the
+ * construction process. The ordering vector should contain the OIDs
+ * in the order they are meant to be encoded or printed in toString.
+ * <p>
+ * The passed in converter will be used to convert the strings into their
+ * ASN.1 counterparts.
+ * @deprecated use X500Name, X500NameBuilder
+ */
+ public X509Name(
+ Vector ordering,
+ Hashtable attributes,
+ X509NameEntryConverter converter)
+ {
+ this.converter = converter;
+
+ if (ordering != null)
+ {
+ for (int i = 0; i != ordering.size(); i++)
+ {
+ this.ordering.addElement(ordering.elementAt(i));
+ this.added.addElement(FALSE);
+ }
+ }
+ else
+ {
+ Enumeration e = attributes.keys();
+
+ while (e.hasMoreElements())
+ {
+ this.ordering.addElement(e.nextElement());
+ this.added.addElement(FALSE);
+ }
+ }
+
+ for (int i = 0; i != this.ordering.size(); i++)
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)this.ordering.elementAt(i);
+
+ if (attributes.get(oid) == null)
+ {
+ throw new IllegalArgumentException("No attribute for object id - " + oid.getId() + " - passed to distinguished name");
+ }
+
+ this.values.addElement(attributes.get(oid)); // copy the hash table
+ }
+ }
+
+ /**
+ * Takes two vectors one of the oids and the other of the values.
+ * @deprecated use X500Name, X500NameBuilder
+ */
+ public X509Name(
+ Vector oids,
+ Vector values)
+ {
+ this(oids, values, new X509DefaultEntryConverter());
+ }
+
+ /**
+ * Takes two vectors one of the oids and the other of the values.
+ * <p>
+ * The passed in converter will be used to convert the strings into their
+ * ASN.1 counterparts.
+ * @deprecated use X500Name, X500NameBuilder
+ */
+ public X509Name(
+ Vector oids,
+ Vector values,
+ X509NameEntryConverter converter)
+ {
+ this.converter = converter;
+
+ if (oids.size() != values.size())
+ {
+ throw new IllegalArgumentException("oids vector must be same length as values.");
+ }
+
+ for (int i = 0; i < oids.size(); i++)
+ {
+ this.ordering.addElement(oids.elementAt(i));
+ this.values.addElement(values.elementAt(i));
+ this.added.addElement(FALSE);
+ }
+ }
+
+// private Boolean isEncoded(String s)
+// {
+// if (s.charAt(0) == '#')
+// {
+// return TRUE;
+// }
+//
+// return FALSE;
+// }
+
+ /**
+ * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
+ * some such, converting it into an ordered set of name attributes.
+ * @deprecated use X500Name, X500NameBuilder
+ */
+ public X509Name(
+ String dirName)
+ {
+ this(DefaultReverse, DefaultLookUp, dirName);
+ }
+
+ /**
+ * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
+ * some such, converting it into an ordered set of name attributes with each
+ * string value being converted to its associated ASN.1 type using the passed
+ * in converter.
+ * @deprecated use X500Name, X500NameBuilder
+ */
+ public X509Name(
+ String dirName,
+ X509NameEntryConverter converter)
+ {
+ this(DefaultReverse, DefaultLookUp, dirName, converter);
+ }
+
+ /**
+ * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
+ * some such, converting it into an ordered set of name attributes. If reverse
+ * is true, create the encoded version of the sequence starting from the
+ * last element in the string.
+ * @deprecated use X500Name, X500NameBuilder
+ */
+ public X509Name(
+ boolean reverse,
+ String dirName)
+ {
+ this(reverse, DefaultLookUp, dirName);
+ }
+
+ /**
+ * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
+ * some such, converting it into an ordered set of name attributes with each
+ * string value being converted to its associated ASN.1 type using the passed
+ * in converter. If reverse is true the ASN.1 sequence representing the DN will
+ * be built by starting at the end of the string, rather than the start.
+ * @deprecated use X500Name, X500NameBuilder
+ */
+ public X509Name(
+ boolean reverse,
+ String dirName,
+ X509NameEntryConverter converter)
+ {
+ this(reverse, DefaultLookUp, dirName, converter);
+ }
+
+ /**
+ * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
+ * some such, converting it into an ordered set of name attributes. lookUp
+ * should provide a table of lookups, indexed by lowercase only strings and
+ * yielding a ASN1ObjectIdentifier, other than that OID. and numeric oids
+ * will be processed automatically.
+ * <br>
+ * If reverse is true, create the encoded version of the sequence
+ * starting from the last element in the string.
+ * @param reverse true if we should start scanning from the end (RFC 2553).
+ * @param lookUp table of names and their oids.
+ * @param dirName the X.500 string to be parsed.
+ * @deprecated use X500Name, X500NameBuilder
+ */
+ public X509Name(
+ boolean reverse,
+ Hashtable lookUp,
+ String dirName)
+ {
+ this(reverse, lookUp, dirName, new X509DefaultEntryConverter());
+ }
+
+ private ASN1ObjectIdentifier decodeOID(
+ String name,
+ Hashtable lookUp)
+ {
+ name = name.trim();
+ if (Strings.toUpperCase(name).startsWith("OID."))
+ {
+ return new ASN1ObjectIdentifier(name.substring(4));
+ }
+ else if (name.charAt(0) >= '0' && name.charAt(0) <= '9')
+ {
+ return new ASN1ObjectIdentifier(name);
+ }
+
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)lookUp.get(Strings.toLowerCase(name));
+ if (oid == null)
+ {
+ throw new IllegalArgumentException("Unknown object id - " + name + " - passed to distinguished name");
+ }
+
+ return oid;
+ }
+
+ private String unescape(String elt)
+ {
+ if (elt.length() == 0 || (elt.indexOf('\\') < 0 && elt.indexOf('"') < 0))
+ {
+ return elt.trim();
+ }
+
+ char[] elts = elt.toCharArray();
+ boolean escaped = false;
+ boolean quoted = false;
+ StringBuffer buf = new StringBuffer(elt.length());
+ int start = 0;
+
+ // if it's an escaped hash string and not an actual encoding in string form
+ // we need to leave it escaped.
+ if (elts[0] == '\\')
+ {
+ if (elts[1] == '#')
+ {
+ start = 2;
+ buf.append("\\#");
+ }
+ }
+
+ boolean nonWhiteSpaceEncountered = false;
+ int lastEscaped = 0;
+
+ for (int i = start; i != elts.length; i++)
+ {
+ char c = elts[i];
+
+ if (c != ' ')
+ {
+ nonWhiteSpaceEncountered = true;
+ }
+
+ if (c == '"')
+ {
+ if (!escaped)
+ {
+ quoted = !quoted;
+ }
+ else
+ {
+ buf.append(c);
+ }
+ escaped = false;
+ }
+ else if (c == '\\' && !(escaped || quoted))
+ {
+ escaped = true;
+ lastEscaped = buf.length();
+ }
+ else
+ {
+ if (c == ' ' && !escaped && !nonWhiteSpaceEncountered)
+ {
+ continue;
+ }
+ buf.append(c);
+ escaped = false;
+ }
+ }
+
+ if (buf.length() > 0)
+ {
+ while (buf.charAt(buf.length() - 1) == ' ' && lastEscaped != (buf.length() - 1))
+ {
+ buf.setLength(buf.length() - 1);
+ }
+ }
+
+ return buf.toString();
+ }
+
+ /**
+ * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
+ * some such, converting it into an ordered set of name attributes. lookUp
+ * should provide a table of lookups, indexed by lowercase only strings and
+ * yielding a ASN1ObjectIdentifier, other than that OID. and numeric oids
+ * will be processed automatically. The passed in converter is used to convert the
+ * string values to the right of each equals sign to their ASN.1 counterparts.
+ * <br>
+ * @param reverse true if we should start scanning from the end, false otherwise.
+ * @param lookUp table of names and oids.
+ * @param dirName the string dirName
+ * @param converter the converter to convert string values into their ASN.1 equivalents
+ */
+ public X509Name(
+ boolean reverse,
+ Hashtable lookUp,
+ String dirName,
+ X509NameEntryConverter converter)
+ {
+ this.converter = converter;
+ X509NameTokenizer nTok = new X509NameTokenizer(dirName);
+
+ while (nTok.hasMoreTokens())
+ {
+ String token = nTok.nextToken();
+
+ if (token.indexOf('+') > 0)
+ {
+ X509NameTokenizer pTok = new X509NameTokenizer(token, '+');
+
+ addEntry(lookUp, pTok.nextToken(), FALSE);
+
+ while (pTok.hasMoreTokens())
+ {
+ addEntry(lookUp, pTok.nextToken(), TRUE);
+ }
+ }
+ else
+ {
+ addEntry(lookUp, token, FALSE);
+ }
+ }
+
+ if (reverse)
+ {
+ Vector o = new Vector();
+ Vector v = new Vector();
+ Vector a = new Vector();
+
+ int count = 1;
+
+ for (int i = 0; i < this.ordering.size(); i++)
+ {
+ if (((Boolean)this.added.elementAt(i)).booleanValue())
+ {
+ o.insertElementAt(this.ordering.elementAt(i), count);
+ v.insertElementAt(this.values.elementAt(i), count);
+ a.insertElementAt(this.added.elementAt(i), count);
+ count++;
+ }
+ else
+ {
+ o.insertElementAt(this.ordering.elementAt(i), 0);
+ v.insertElementAt(this.values.elementAt(i), 0);
+ a.insertElementAt(this.added.elementAt(i), 0);
+ count = 1;
+ }
+ }
+
+ this.ordering = o;
+ this.values = v;
+ this.added = a;
+ }
+ }
+
+ private void addEntry(Hashtable lookUp, String token, Boolean isAdded)
+ {
+ X509NameTokenizer vTok;
+ String name;
+ String value;ASN1ObjectIdentifier oid;
+ vTok = new X509NameTokenizer(token, '=');
+
+ name = vTok.nextToken();
+
+ if (!vTok.hasMoreTokens())
+ {
+ throw new IllegalArgumentException("badly formatted directory string");
+ }
+
+ value = vTok.nextToken();
+
+ oid = decodeOID(name, lookUp);
+
+ this.ordering.addElement(oid);
+ this.values.addElement(unescape(value));
+ this.added.addElement(isAdded);
+ }
+
+ /**
+ * return a vector of the oids in the name, in the order they were found.
+ */
+ public Vector getOIDs()
+ {
+ Vector v = new Vector();
+
+ for (int i = 0; i != ordering.size(); i++)
+ {
+ v.addElement(ordering.elementAt(i));
+ }
+
+ return v;
+ }
+
+ /**
+ * return a vector of the values found in the name, in the order they
+ * were found.
+ */
+ public Vector getValues()
+ {
+ Vector v = new Vector();
+
+ for (int i = 0; i != values.size(); i++)
+ {
+ v.addElement(values.elementAt(i));
+ }
+
+ return v;
+ }
+
+ /**
+ * return a vector of the values found in the name, in the order they
+ * were found, with the DN label corresponding to passed in oid.
+ */
+ public Vector getValues(
+ ASN1ObjectIdentifier oid)
+ {
+ Vector v = new Vector();
+
+ for (int i = 0; i != values.size(); i++)
+ {
+ if (ordering.elementAt(i).equals(oid))
+ {
+ String val = (String)values.elementAt(i);
+
+ if (val.length() > 2 && val.charAt(0) == '\\' && val.charAt(1) == '#')
+ {
+ v.addElement(val.substring(1));
+ }
+ else
+ {
+ v.addElement(val);
+ }
+ }
+ }
+
+ return v;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ if (seq == null)
+ {
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+ ASN1EncodableVector sVec = new ASN1EncodableVector();
+ ASN1ObjectIdentifier lstOid = null;
+
+ for (int i = 0; i != ordering.size(); i++)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
+
+ v.add(oid);
+
+ String str = (String)values.elementAt(i);
+
+ v.add(converter.getConvertedValue(oid, str));
+
+ if (lstOid == null
+ || ((Boolean)this.added.elementAt(i)).booleanValue())
+ {
+ sVec.add(new DERSequence(v));
+ }
+ else
+ {
+ vec.add(new DERSet(sVec));
+ sVec = new ASN1EncodableVector();
+
+ sVec.add(new DERSequence(v));
+ }
+
+ lstOid = oid;
+ }
+
+ vec.add(new DERSet(sVec));
+
+ seq = new DERSequence(vec);
+ }
+
+ return seq;
+ }
+
+ /**
+ * @param inOrder if true the order of both X509 names must be the same,
+ * as well as the values associated with each element.
+ */
+ public boolean equals(Object obj, boolean inOrder)
+ {
+ if (!inOrder)
+ {
+ return this.equals(obj);
+ }
+
+ if (obj == this)
+ {
+ return true;
+ }
+
+ if (!(obj instanceof X509Name || obj instanceof ASN1Sequence))
+ {
+ return false;
+ }
+
+ ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive();
+
+ if (this.toASN1Primitive().equals(derO))
+ {
+ return true;
+ }
+
+ X509Name other;
+
+ try
+ {
+ other = X509Name.getInstance(obj);
+ }
+ catch (IllegalArgumentException e)
+ {
+ return false;
+ }
+
+ int orderingSize = ordering.size();
+
+ if (orderingSize != other.ordering.size())
+ {
+ return false;
+ }
+
+ for (int i = 0; i < orderingSize; i++)
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
+ ASN1ObjectIdentifier oOid = (ASN1ObjectIdentifier)other.ordering.elementAt(i);
+
+ if (oid.equals(oOid))
+ {
+ String value = (String)values.elementAt(i);
+ String oValue = (String)other.values.elementAt(i);
+
+ if (!equivalentStrings(value, oValue))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public int hashCode()
+ {
+ if (isHashCodeCalculated)
+ {
+ return hashCodeValue;
+ }
+
+ isHashCodeCalculated = true;
+
+ // this needs to be order independent, like equals
+ for (int i = 0; i != ordering.size(); i += 1)
+ {
+ String value = (String)values.elementAt(i);
+
+ value = canonicalize(value);
+ value = stripInternalSpaces(value);
+
+ hashCodeValue ^= ordering.elementAt(i).hashCode();
+ hashCodeValue ^= value.hashCode();
+ }
+
+ return hashCodeValue;
+ }
+
+ /**
+ * test for equality - note: case is ignored.
+ */
+ public boolean equals(Object obj)
+ {
+ if (obj == this)
+ {
+ return true;
+ }
+
+ if (!(obj instanceof X509Name || obj instanceof ASN1Sequence))
+ {
+ return false;
+ }
+
+ ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive();
+
+ if (this.toASN1Primitive().equals(derO))
+ {
+ return true;
+ }
+
+ X509Name other;
+
+ try
+ {
+ other = X509Name.getInstance(obj);
+ }
+ catch (IllegalArgumentException e)
+ {
+ return false;
+ }
+
+ int orderingSize = ordering.size();
+
+ if (orderingSize != other.ordering.size())
+ {
+ return false;
+ }
+
+ boolean[] indexes = new boolean[orderingSize];
+ int start, end, delta;
+
+ if (ordering.elementAt(0).equals(other.ordering.elementAt(0))) // guess forward
+ {
+ start = 0;
+ end = orderingSize;
+ delta = 1;
+ }
+ else // guess reversed - most common problem
+ {
+ start = orderingSize - 1;
+ end = -1;
+ delta = -1;
+ }
+
+ for (int i = start; i != end; i += delta)
+ {
+ boolean found = false;
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
+ String value = (String)values.elementAt(i);
+
+ for (int j = 0; j < orderingSize; j++)
+ {
+ if (indexes[j])
+ {
+ continue;
+ }
+
+ ASN1ObjectIdentifier oOid = (ASN1ObjectIdentifier)other.ordering.elementAt(j);
+
+ if (oid.equals(oOid))
+ {
+ String oValue = (String)other.values.elementAt(j);
+
+ if (equivalentStrings(value, oValue))
+ {
+ indexes[j] = true;
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean equivalentStrings(String s1, String s2)
+ {
+ String value = canonicalize(s1);
+ String oValue = canonicalize(s2);
+
+ if (!value.equals(oValue))
+ {
+ value = stripInternalSpaces(value);
+ oValue = stripInternalSpaces(oValue);
+
+ if (!value.equals(oValue))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private String canonicalize(String s)
+ {
+ String value = Strings.toLowerCase(s.trim());
+
+ if (value.length() > 0 && value.charAt(0) == '#')
+ {
+ ASN1Primitive obj = decodeObject(value);
+
+ if (obj instanceof ASN1String)
+ {
+ value = Strings.toLowerCase(((ASN1String)obj).getString().trim());
+ }
+ }
+
+ return value;
+ }
+
+ private ASN1Primitive decodeObject(String oValue)
+ {
+ try
+ {
+ return ASN1Primitive.fromByteArray(Hex.decode(oValue.substring(1)));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("unknown encoding in name: " + e);
+ }
+ }
+
+ private String stripInternalSpaces(
+ String str)
+ {
+ StringBuffer res = new StringBuffer();
+
+ if (str.length() != 0)
+ {
+ char c1 = str.charAt(0);
+
+ res.append(c1);
+
+ for (int k = 1; k < str.length(); k++)
+ {
+ char c2 = str.charAt(k);
+ if (!(c1 == ' ' && c2 == ' '))
+ {
+ res.append(c2);
+ }
+ c1 = c2;
+ }
+ }
+
+ return res.toString();
+ }
+
+ private void appendValue(
+ StringBuffer buf,
+ Hashtable oidSymbols,
+ ASN1ObjectIdentifier oid,
+ String value)
+ {
+ String sym = (String)oidSymbols.get(oid);
+
+ if (sym != null)
+ {
+ buf.append(sym);
+ }
+ else
+ {
+ buf.append(oid.getId());
+ }
+
+ buf.append('=');
+
+ int index = buf.length();
+ int start = index;
+
+ buf.append(value);
+
+ int end = buf.length();
+
+ if (value.length() >= 2 && value.charAt(0) == '\\' && value.charAt(1) == '#')
+ {
+ index += 2;
+ }
+
+ while (index != end)
+ {
+ if ((buf.charAt(index) == ',')
+ || (buf.charAt(index) == '"')
+ || (buf.charAt(index) == '\\')
+ || (buf.charAt(index) == '+')
+ || (buf.charAt(index) == '=')
+ || (buf.charAt(index) == '<')
+ || (buf.charAt(index) == '>')
+ || (buf.charAt(index) == ';'))
+ {
+ buf.insert(index, "\\");
+ index++;
+ end++;
+ }
+
+ index++;
+ }
+
+ while (buf.charAt(start) == ' ')
+ {
+ buf.insert(start, "\\");
+ start += 2;
+ }
+
+ int endBuf = buf.length() - 1;
+
+ while (endBuf >= 0 && buf.charAt(endBuf) == ' ')
+ {
+ buf.insert(endBuf, '\\');
+ endBuf--;
+ }
+ }
+
+ /**
+ * convert the structure to a string - if reverse is true the
+ * oids and values are listed out starting with the last element
+ * in the sequence (ala RFC 2253), otherwise the string will begin
+ * with the first element of the structure. If no string definition
+ * for the oid is found in oidSymbols the string value of the oid is
+ * added. Two standard symbol tables are provided DefaultSymbols, and
+ * RFC2253Symbols as part of this class.
+ *
+ * @param reverse if true start at the end of the sequence and work back.
+ * @param oidSymbols look up table strings for oids.
+ */
+ public String toString(
+ boolean reverse,
+ Hashtable oidSymbols)
+ {
+ StringBuffer buf = new StringBuffer();
+ Vector components = new Vector();
+ boolean first = true;
+
+ StringBuffer ava = null;
+
+ for (int i = 0; i < ordering.size(); i++)
+ {
+ if (((Boolean)added.elementAt(i)).booleanValue())
+ {
+ ava.append('+');
+ appendValue(ava, oidSymbols,
+ (ASN1ObjectIdentifier)ordering.elementAt(i),
+ (String)values.elementAt(i));
+ }
+ else
+ {
+ ava = new StringBuffer();
+ appendValue(ava, oidSymbols,
+ (ASN1ObjectIdentifier)ordering.elementAt(i),
+ (String)values.elementAt(i));
+ components.addElement(ava);
+ }
+ }
+
+ if (reverse)
+ {
+ for (int i = components.size() - 1; i >= 0; i--)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ buf.append(',');
+ }
+
+ buf.append(components.elementAt(i).toString());
+ }
+ }
+ else
+ {
+ for (int i = 0; i < components.size(); i++)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ buf.append(',');
+ }
+
+ buf.append(components.elementAt(i).toString());
+ }
+ }
+
+ return buf.toString();
+ }
+
+ private String bytesToString(
+ byte[] data)
+ {
+ char[] cs = new char[data.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ cs[i] = (char)(data[i] & 0xff);
+ }
+
+ return new String(cs);
+ }
+
+ public String toString()
+ {
+ return toString(DefaultReverse, DefaultSymbols);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/X509NameEntryConverter.java b/core/src/main/java/org/bouncycastle/asn1/x509/X509NameEntryConverter.java
new file mode 100644
index 00000000..5d919e1b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/X509NameEntryConverter.java
@@ -0,0 +1,113 @@
+package org.bouncycastle.asn1.x509;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DERPrintableString;
+import org.bouncycastle.util.Strings;
+
+/**
+ * It turns out that the number of standard ways the fields in a DN should be
+ * encoded into their ASN.1 counterparts is rapidly approaching the
+ * number of machines on the internet. By default the X509Name class
+ * will produce UTF8Strings in line with the current recommendations (RFC 3280).
+ * <p>
+ * An example of an encoder look like below:
+ * <pre>
+ * public class X509DirEntryConverter
+ * extends X509NameEntryConverter
+ * {
+ * public ASN1Primitive getConvertedValue(
+ * ASN1ObjectIdentifier oid,
+ * String value)
+ * {
+ * if (str.length() != 0 && str.charAt(0) == '#')
+ * {
+ * return convertHexEncoded(str, 1);
+ * }
+ * if (oid.equals(EmailAddress))
+ * {
+ * return new DERIA5String(str);
+ * }
+ * else if (canBePrintable(str))
+ * {
+ * return new DERPrintableString(str);
+ * }
+ * else if (canBeUTF8(str))
+ * {
+ * return new DERUTF8String(str);
+ * }
+ * else
+ * {
+ * return new DERBMPString(str);
+ * }
+ * }
+ * }
+ */
+public abstract class X509NameEntryConverter
+{
+ /**
+ * Convert an inline encoded hex string rendition of an ASN.1
+ * object back into its corresponding ASN.1 object.
+ *
+ * @param str the hex encoded object
+ * @param off the index at which the encoding starts
+ * @return the decoded object
+ */
+ protected ASN1Primitive convertHexEncoded(
+ String str,
+ int off)
+ throws IOException
+ {
+ str = Strings.toLowerCase(str);
+ byte[] data = new byte[(str.length() - off) / 2];
+ for (int index = 0; index != data.length; index++)
+ {
+ char left = str.charAt((index * 2) + off);
+ char right = str.charAt((index * 2) + off + 1);
+
+ if (left < 'a')
+ {
+ data[index] = (byte)((left - '0') << 4);
+ }
+ else
+ {
+ data[index] = (byte)((left - 'a' + 10) << 4);
+ }
+ if (right < 'a')
+ {
+ data[index] |= (byte)(right - '0');
+ }
+ else
+ {
+ data[index] |= (byte)(right - 'a' + 10);
+ }
+ }
+
+ ASN1InputStream aIn = new ASN1InputStream(data);
+
+ return aIn.readObject();
+ }
+
+ /**
+ * return true if the passed in String can be represented without
+ * loss as a PrintableString, false otherwise.
+ */
+ protected boolean canBePrintable(
+ String str)
+ {
+ return DERPrintableString.isPrintableString(str);
+ }
+
+ /**
+ * Convert the passed in String value into the appropriate ASN.1
+ * encoded object.
+ *
+ * @param oid the oid associated with the value in the DN.
+ * @param value the value of the particular DN component.
+ * @return the ASN.1 equivalent for the value.
+ */
+ public abstract ASN1Primitive getConvertedValue(ASN1ObjectIdentifier oid, String value);
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/X509NameTokenizer.java b/core/src/main/java/org/bouncycastle/asn1/x509/X509NameTokenizer.java
new file mode 100644
index 00000000..7f99235b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/X509NameTokenizer.java
@@ -0,0 +1,91 @@
+package org.bouncycastle.asn1.x509;
+
+/**
+ * class for breaking up an X500 Name into it's component tokens, ala
+ * java.util.StringTokenizer. We need this class as some of the
+ * lightweight Java environment don't support classes like
+ * StringTokenizer.
+ * @deprecated use X500NameTokenizer
+ */
+public class X509NameTokenizer
+{
+ private String value;
+ private int index;
+ private char separator;
+ private StringBuffer buf = new StringBuffer();
+
+ public X509NameTokenizer(
+ String oid)
+ {
+ this(oid, ',');
+ }
+
+ public X509NameTokenizer(
+ String oid,
+ char separator)
+ {
+ this.value = oid;
+ this.index = -1;
+ this.separator = separator;
+ }
+
+ public boolean hasMoreTokens()
+ {
+ return (index != value.length());
+ }
+
+ public String nextToken()
+ {
+ if (index == value.length())
+ {
+ return null;
+ }
+
+ int end = index + 1;
+ boolean quoted = false;
+ boolean escaped = false;
+
+ buf.setLength(0);
+
+ while (end != value.length())
+ {
+ char c = value.charAt(end);
+
+ if (c == '"')
+ {
+ if (!escaped)
+ {
+ quoted = !quoted;
+ }
+ buf.append(c);
+ escaped = false;
+ }
+ else
+ {
+ if (escaped || quoted)
+ {
+ buf.append(c);
+ escaped = false;
+ }
+ else if (c == '\\')
+ {
+ buf.append(c);
+ escaped = true;
+ }
+ else if (c == separator)
+ {
+ break;
+ }
+ else
+ {
+ buf.append(c);
+ }
+ }
+ end++;
+ }
+
+ index = end;
+
+ return buf.toString();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
new file mode 100644
index 00000000..ed4dd328
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
@@ -0,0 +1,67 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface X509ObjectIdentifiers
+{
+ //
+ // base id
+ //
+ static final String id = "2.5.4";
+
+ static final ASN1ObjectIdentifier commonName = new ASN1ObjectIdentifier(id + ".3");
+ static final ASN1ObjectIdentifier countryName = new ASN1ObjectIdentifier(id + ".6");
+ static final ASN1ObjectIdentifier localityName = new ASN1ObjectIdentifier(id + ".7");
+ static final ASN1ObjectIdentifier stateOrProvinceName = new ASN1ObjectIdentifier(id + ".8");
+ static final ASN1ObjectIdentifier organization = new ASN1ObjectIdentifier(id + ".10");
+ static final ASN1ObjectIdentifier organizationalUnitName = new ASN1ObjectIdentifier(id + ".11");
+
+ static final ASN1ObjectIdentifier id_at_telephoneNumber = new ASN1ObjectIdentifier("2.5.4.20");
+ static final ASN1ObjectIdentifier id_at_name = new ASN1ObjectIdentifier(id + ".41");
+
+ // id-SHA1 OBJECT IDENTIFIER ::=
+ // {iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 } //
+ static final ASN1ObjectIdentifier id_SHA1 = new ASN1ObjectIdentifier("1.3.14.3.2.26");
+
+ //
+ // ripemd160 OBJECT IDENTIFIER ::=
+ // {iso(1) identified-organization(3) TeleTrust(36) algorithm(3) hashAlgorithm(2) RIPEMD-160(1)}
+ //
+ static final ASN1ObjectIdentifier ripemd160 = new ASN1ObjectIdentifier("1.3.36.3.2.1");
+
+ //
+ // ripemd160WithRSAEncryption OBJECT IDENTIFIER ::=
+ // {iso(1) identified-organization(3) TeleTrust(36) algorithm(3) signatureAlgorithm(3) rsaSignature(1) rsaSignatureWithripemd160(2) }
+ //
+ static final ASN1ObjectIdentifier ripemd160WithRSAEncryption = new ASN1ObjectIdentifier("1.3.36.3.3.1.2");
+
+
+ static final ASN1ObjectIdentifier id_ea_rsa = new ASN1ObjectIdentifier("2.5.8.1.1");
+
+ // id-pkix
+ static final ASN1ObjectIdentifier id_pkix = new ASN1ObjectIdentifier("1.3.6.1.5.5.7");
+
+ //
+ // private internet extensions
+ //
+ static final ASN1ObjectIdentifier id_pe = new ASN1ObjectIdentifier(id_pkix + ".1");
+
+ //
+ // ISO ARC for standard certificate and CRL extensions
+ //
+ static final ASN1ObjectIdentifier id_ce = new ASN1ObjectIdentifier("2.5.29");
+
+ //
+ // authority information access
+ //
+ static final ASN1ObjectIdentifier id_ad = new ASN1ObjectIdentifier(id_pkix + ".48");
+ static final ASN1ObjectIdentifier id_ad_caIssuers = new ASN1ObjectIdentifier(id_ad + ".2");
+ static final ASN1ObjectIdentifier id_ad_ocsp = new ASN1ObjectIdentifier(id_ad + ".1");
+
+ //
+ // OID for ocsp and crl uri in AuthorityInformationAccess extension
+ //
+ static final ASN1ObjectIdentifier ocspAccessMethod = id_ad_ocsp;
+ static final ASN1ObjectIdentifier crlAccessMethod = id_ad_caIssuers;
+}
+
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/qualified/BiometricData.java b/core/src/main/java/org/bouncycastle/asn1/x509/qualified/BiometricData.java
new file mode 100644
index 00000000..32fa451d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/qualified/BiometricData.java
@@ -0,0 +1,122 @@
+package org.bouncycastle.asn1.x509.qualified;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * The BiometricData object.
+ * <pre>
+ * BiometricData ::= SEQUENCE {
+ * typeOfBiometricData TypeOfBiometricData,
+ * hashAlgorithm AlgorithmIdentifier,
+ * biometricDataHash OCTET STRING,
+ * sourceDataUri IA5String OPTIONAL }
+ * </pre>
+ */
+public class BiometricData
+ extends ASN1Object
+{
+ private TypeOfBiometricData typeOfBiometricData;
+ private AlgorithmIdentifier hashAlgorithm;
+ private ASN1OctetString biometricDataHash;
+ private DERIA5String sourceDataUri;
+
+ public static BiometricData getInstance(
+ Object obj)
+ {
+ if (obj instanceof BiometricData)
+ {
+ return (BiometricData)obj;
+ }
+
+ if (obj != null)
+ {
+ return new BiometricData(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private BiometricData(ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ // typeOfBiometricData
+ typeOfBiometricData = TypeOfBiometricData.getInstance(e.nextElement());
+ // hashAlgorithm
+ hashAlgorithm = AlgorithmIdentifier.getInstance(e.nextElement());
+ // biometricDataHash
+ biometricDataHash = ASN1OctetString.getInstance(e.nextElement());
+ // sourceDataUri
+ if (e.hasMoreElements())
+ {
+ sourceDataUri = DERIA5String.getInstance(e.nextElement());
+ }
+ }
+
+ public BiometricData(
+ TypeOfBiometricData typeOfBiometricData,
+ AlgorithmIdentifier hashAlgorithm,
+ ASN1OctetString biometricDataHash,
+ DERIA5String sourceDataUri)
+ {
+ this.typeOfBiometricData = typeOfBiometricData;
+ this.hashAlgorithm = hashAlgorithm;
+ this.biometricDataHash = biometricDataHash;
+ this.sourceDataUri = sourceDataUri;
+ }
+
+ public BiometricData(
+ TypeOfBiometricData typeOfBiometricData,
+ AlgorithmIdentifier hashAlgorithm,
+ ASN1OctetString biometricDataHash)
+ {
+ this.typeOfBiometricData = typeOfBiometricData;
+ this.hashAlgorithm = hashAlgorithm;
+ this.biometricDataHash = biometricDataHash;
+ this.sourceDataUri = null;
+ }
+
+ public TypeOfBiometricData getTypeOfBiometricData()
+ {
+ return typeOfBiometricData;
+ }
+
+ public AlgorithmIdentifier getHashAlgorithm()
+ {
+ return hashAlgorithm;
+ }
+
+ public ASN1OctetString getBiometricDataHash()
+ {
+ return biometricDataHash;
+ }
+
+ public DERIA5String getSourceDataUri()
+ {
+ return sourceDataUri;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector seq = new ASN1EncodableVector();
+ seq.add(typeOfBiometricData);
+ seq.add(hashAlgorithm);
+ seq.add(biometricDataHash);
+
+ if (sourceDataUri != null)
+ {
+ seq.add(sourceDataUri);
+ }
+
+ return new DERSequence(seq);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/qualified/ETSIQCObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/x509/qualified/ETSIQCObjectIdentifiers.java
new file mode 100644
index 00000000..19ef12ba
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/qualified/ETSIQCObjectIdentifiers.java
@@ -0,0 +1,16 @@
+package org.bouncycastle.asn1.x509.qualified;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface ETSIQCObjectIdentifiers
+{
+ //
+ // base id
+ //
+ static final ASN1ObjectIdentifier id_etsi_qcs = new ASN1ObjectIdentifier("0.4.0.1862.1");
+
+ static final ASN1ObjectIdentifier id_etsi_qcs_QcCompliance = id_etsi_qcs.branch("1");
+ static final ASN1ObjectIdentifier id_etsi_qcs_LimiteValue = id_etsi_qcs.branch("2");
+ static final ASN1ObjectIdentifier id_etsi_qcs_RetentionPeriod = id_etsi_qcs.branch("3");
+ static final ASN1ObjectIdentifier id_etsi_qcs_QcSSCD = id_etsi_qcs.branch("4");
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/qualified/Iso4217CurrencyCode.java b/core/src/main/java/org/bouncycastle/asn1/x509/qualified/Iso4217CurrencyCode.java
new file mode 100644
index 00000000..b6cfb629
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/qualified/Iso4217CurrencyCode.java
@@ -0,0 +1,93 @@
+package org.bouncycastle.asn1.x509.qualified;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DERPrintableString;
+
+/**
+ * The Iso4217CurrencyCode object.
+ * <pre>
+ * Iso4217CurrencyCode ::= CHOICE {
+ * alphabetic PrintableString (SIZE 3), --Recommended
+ * numeric INTEGER (1..999) }
+ * -- Alphabetic or numeric currency code as defined in ISO 4217
+ * -- It is recommended that the Alphabetic form is used
+ * </pre>
+ */
+public class Iso4217CurrencyCode
+ extends ASN1Object
+ implements ASN1Choice
+{
+ final int ALPHABETIC_MAXSIZE = 3;
+ final int NUMERIC_MINSIZE = 1;
+ final int NUMERIC_MAXSIZE = 999;
+
+ ASN1Encodable obj;
+ int numeric;
+
+ public static Iso4217CurrencyCode getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof Iso4217CurrencyCode)
+ {
+ return (Iso4217CurrencyCode)obj;
+ }
+
+ if (obj instanceof ASN1Integer)
+ {
+ ASN1Integer numericobj = ASN1Integer.getInstance(obj);
+ int numeric = numericobj.getValue().intValue();
+ return new Iso4217CurrencyCode(numeric);
+ }
+ else
+ if (obj instanceof DERPrintableString)
+ {
+ DERPrintableString alphabetic = DERPrintableString.getInstance(obj);
+ return new Iso4217CurrencyCode(alphabetic.getString());
+ }
+ throw new IllegalArgumentException("unknown object in getInstance");
+ }
+
+ public Iso4217CurrencyCode(
+ int numeric)
+ {
+ if (numeric > NUMERIC_MAXSIZE || numeric < NUMERIC_MINSIZE)
+ {
+ throw new IllegalArgumentException("wrong size in numeric code : not in (" +NUMERIC_MINSIZE +".."+ NUMERIC_MAXSIZE +")");
+ }
+ obj = new ASN1Integer(numeric);
+ }
+
+ public Iso4217CurrencyCode(
+ String alphabetic)
+ {
+ if (alphabetic.length() > ALPHABETIC_MAXSIZE)
+ {
+ throw new IllegalArgumentException("wrong size in alphabetic code : max size is " + ALPHABETIC_MAXSIZE);
+ }
+ obj = new DERPrintableString(alphabetic);
+ }
+
+ public boolean isAlphabetic()
+ {
+ return obj instanceof DERPrintableString;
+ }
+
+ public String getAlphabetic()
+ {
+ return ((DERPrintableString)obj).getString();
+ }
+
+ public int getNumeric()
+ {
+ return ((ASN1Integer)obj).getValue().intValue();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return obj.toASN1Primitive();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/qualified/MonetaryValue.java b/core/src/main/java/org/bouncycastle/asn1/x509/qualified/MonetaryValue.java
new file mode 100644
index 00000000..1ec2dcdb
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/qualified/MonetaryValue.java
@@ -0,0 +1,92 @@
+package org.bouncycastle.asn1.x509.qualified;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * The MonetaryValue object.
+ * <pre>
+ * MonetaryValue ::= SEQUENCE {
+ * currency Iso4217CurrencyCode,
+ * amount INTEGER,
+ * exponent INTEGER }
+ * -- value = amount * 10^exponent
+ * </pre>
+ */
+public class MonetaryValue
+ extends ASN1Object
+{
+ private Iso4217CurrencyCode currency;
+ private ASN1Integer amount;
+ private ASN1Integer exponent;
+
+ public static MonetaryValue getInstance(
+ Object obj)
+ {
+ if (obj instanceof MonetaryValue)
+ {
+ return (MonetaryValue)obj;
+ }
+
+ if (obj != null)
+ {
+ return new MonetaryValue(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private MonetaryValue(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+ // currency
+ currency = Iso4217CurrencyCode.getInstance(e.nextElement());
+ // hashAlgorithm
+ amount = ASN1Integer.getInstance(e.nextElement());
+ // exponent
+ exponent = ASN1Integer.getInstance(e.nextElement());
+ }
+
+ public MonetaryValue(
+ Iso4217CurrencyCode currency,
+ int amount,
+ int exponent)
+ {
+ this.currency = currency;
+ this.amount = new ASN1Integer(amount);
+ this.exponent = new ASN1Integer(exponent);
+ }
+
+ public Iso4217CurrencyCode getCurrency()
+ {
+ return currency;
+ }
+
+ public BigInteger getAmount()
+ {
+ return amount.getValue();
+ }
+
+ public BigInteger getExponent()
+ {
+ return exponent.getValue();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector seq = new ASN1EncodableVector();
+ seq.add(currency);
+ seq.add(amount);
+ seq.add(exponent);
+
+ return new DERSequence(seq);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/qualified/QCStatement.java b/core/src/main/java/org/bouncycastle/asn1/x509/qualified/QCStatement.java
new file mode 100644
index 00000000..82b01e71
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/qualified/QCStatement.java
@@ -0,0 +1,95 @@
+package org.bouncycastle.asn1.x509.qualified;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * The QCStatement object.
+ * <pre>
+ * QCStatement ::= SEQUENCE {
+ * statementId OBJECT IDENTIFIER,
+ * statementInfo ANY DEFINED BY statementId OPTIONAL}
+ * </pre>
+ */
+
+public class QCStatement
+ extends ASN1Object
+ implements ETSIQCObjectIdentifiers, RFC3739QCObjectIdentifiers
+{
+ ASN1ObjectIdentifier qcStatementId;
+ ASN1Encodable qcStatementInfo;
+
+ public static QCStatement getInstance(
+ Object obj)
+ {
+ if (obj instanceof QCStatement)
+ {
+ return (QCStatement)obj;
+ }
+ if (obj != null)
+ {
+ return new QCStatement(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private QCStatement(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ // qcStatementId
+ qcStatementId = ASN1ObjectIdentifier.getInstance(e.nextElement());
+ // qcstatementInfo
+ if (e.hasMoreElements())
+ {
+ qcStatementInfo = (ASN1Encodable) e.nextElement();
+ }
+ }
+
+ public QCStatement(
+ ASN1ObjectIdentifier qcStatementId)
+ {
+ this.qcStatementId = qcStatementId;
+ this.qcStatementInfo = null;
+ }
+
+ public QCStatement(
+ ASN1ObjectIdentifier qcStatementId,
+ ASN1Encodable qcStatementInfo)
+ {
+ this.qcStatementId = qcStatementId;
+ this.qcStatementInfo = qcStatementInfo;
+ }
+
+ public ASN1ObjectIdentifier getStatementId()
+ {
+ return qcStatementId;
+ }
+
+ public ASN1Encodable getStatementInfo()
+ {
+ return qcStatementInfo;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector seq = new ASN1EncodableVector();
+ seq.add(qcStatementId);
+
+ if (qcStatementInfo != null)
+ {
+ seq.add(qcStatementInfo);
+ }
+
+ return new DERSequence(seq);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/qualified/RFC3739QCObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/x509/qualified/RFC3739QCObjectIdentifiers.java
new file mode 100644
index 00000000..ecb5cce5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/qualified/RFC3739QCObjectIdentifiers.java
@@ -0,0 +1,14 @@
+package org.bouncycastle.asn1.x509.qualified;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface RFC3739QCObjectIdentifiers
+{
+ //
+ // base id
+ //
+ static final ASN1ObjectIdentifier id_qcs = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.11");
+
+ static final ASN1ObjectIdentifier id_qcs_pkixQCSyntax_v1 = id_qcs.branch("1");
+ static final ASN1ObjectIdentifier id_qcs_pkixQCSyntax_v2 = id_qcs.branch("2");
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/qualified/SemanticsInformation.java b/core/src/main/java/org/bouncycastle/asn1/x509/qualified/SemanticsInformation.java
new file mode 100644
index 00000000..43d8d585
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/qualified/SemanticsInformation.java
@@ -0,0 +1,131 @@
+package org.bouncycastle.asn1.x509.qualified;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.GeneralName;
+
+/**
+ * The SemanticsInformation object.
+ * <pre>
+ * SemanticsInformation ::= SEQUENCE {
+ * semanticsIdentifier OBJECT IDENTIFIER OPTIONAL,
+ * nameRegistrationAuthorities NameRegistrationAuthorities
+ * OPTIONAL }
+ * (WITH COMPONENTS {..., semanticsIdentifier PRESENT}|
+ * WITH COMPONENTS {..., nameRegistrationAuthorities PRESENT})
+ *
+ * NameRegistrationAuthorities ::= SEQUENCE SIZE (1..MAX) OF
+ * GeneralName
+ * </pre>
+ */
+public class SemanticsInformation
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier semanticsIdentifier;
+ private GeneralName[] nameRegistrationAuthorities;
+
+ public static SemanticsInformation getInstance(Object obj)
+ {
+ if (obj instanceof SemanticsInformation)
+ {
+ return (SemanticsInformation)obj;
+ }
+
+ if (obj != null)
+ {
+ return new SemanticsInformation(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private SemanticsInformation(ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+ if (seq.size() < 1)
+ {
+ throw new IllegalArgumentException("no objects in SemanticsInformation");
+ }
+
+ Object object = e.nextElement();
+ if (object instanceof ASN1ObjectIdentifier)
+ {
+ semanticsIdentifier = ASN1ObjectIdentifier.getInstance(object);
+ if (e.hasMoreElements())
+ {
+ object = e.nextElement();
+ }
+ else
+ {
+ object = null;
+ }
+ }
+
+ if (object != null)
+ {
+ ASN1Sequence generalNameSeq = ASN1Sequence.getInstance(object);
+ nameRegistrationAuthorities = new GeneralName[generalNameSeq.size()];
+ for (int i= 0; i < generalNameSeq.size(); i++)
+ {
+ nameRegistrationAuthorities[i] = GeneralName.getInstance(generalNameSeq.getObjectAt(i));
+ }
+ }
+ }
+
+ public SemanticsInformation(
+ ASN1ObjectIdentifier semanticsIdentifier,
+ GeneralName[] generalNames)
+ {
+ this.semanticsIdentifier = semanticsIdentifier;
+ this.nameRegistrationAuthorities = generalNames;
+ }
+
+ public SemanticsInformation(ASN1ObjectIdentifier semanticsIdentifier)
+ {
+ this.semanticsIdentifier = semanticsIdentifier;
+ this.nameRegistrationAuthorities = null;
+ }
+
+ public SemanticsInformation(GeneralName[] generalNames)
+ {
+ this.semanticsIdentifier = null;
+ this.nameRegistrationAuthorities = generalNames;
+ }
+
+ public ASN1ObjectIdentifier getSemanticsIdentifier()
+ {
+ return semanticsIdentifier;
+ }
+
+ public GeneralName[] getNameRegistrationAuthorities()
+ {
+ return nameRegistrationAuthorities;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector seq = new ASN1EncodableVector();
+
+ if (this.semanticsIdentifier != null)
+ {
+ seq.add(semanticsIdentifier);
+ }
+ if (this.nameRegistrationAuthorities != null)
+ {
+ ASN1EncodableVector seqname = new ASN1EncodableVector();
+ for (int i = 0; i < nameRegistrationAuthorities.length; i++)
+ {
+ seqname.add(nameRegistrationAuthorities[i]);
+ }
+ seq.add(new DERSequence(seqname));
+ }
+
+ return new DERSequence(seq);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/qualified/TypeOfBiometricData.java b/core/src/main/java/org/bouncycastle/asn1/x509/qualified/TypeOfBiometricData.java
new file mode 100644
index 00000000..01b254eb
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/qualified/TypeOfBiometricData.java
@@ -0,0 +1,90 @@
+package org.bouncycastle.asn1.x509.qualified;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+
+/**
+ * The TypeOfBiometricData object.
+ * <pre>
+ * TypeOfBiometricData ::= CHOICE {
+ * predefinedBiometricType PredefinedBiometricType,
+ * biometricDataOid OBJECT IDENTIFIER }
+ *
+ * PredefinedBiometricType ::= INTEGER {
+ * picture(0),handwritten-signature(1)}
+ * (picture|handwritten-signature)
+ * </pre>
+ */
+public class TypeOfBiometricData
+ extends ASN1Object
+ implements ASN1Choice
+{
+ public static final int PICTURE = 0;
+ public static final int HANDWRITTEN_SIGNATURE = 1;
+
+ ASN1Encodable obj;
+
+ public static TypeOfBiometricData getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof TypeOfBiometricData)
+ {
+ return (TypeOfBiometricData)obj;
+ }
+
+ if (obj instanceof ASN1Integer)
+ {
+ ASN1Integer predefinedBiometricTypeObj = ASN1Integer.getInstance(obj);
+ int predefinedBiometricType = predefinedBiometricTypeObj.getValue().intValue();
+
+ return new TypeOfBiometricData(predefinedBiometricType);
+ }
+ else if (obj instanceof ASN1ObjectIdentifier)
+ {
+ ASN1ObjectIdentifier BiometricDataID = ASN1ObjectIdentifier.getInstance(obj);
+ return new TypeOfBiometricData(BiometricDataID);
+ }
+
+ throw new IllegalArgumentException("unknown object in getInstance");
+ }
+
+ public TypeOfBiometricData(int predefinedBiometricType)
+ {
+ if (predefinedBiometricType == PICTURE || predefinedBiometricType == HANDWRITTEN_SIGNATURE)
+ {
+ obj = new ASN1Integer(predefinedBiometricType);
+ }
+ else
+ {
+ throw new IllegalArgumentException("unknow PredefinedBiometricType : " + predefinedBiometricType);
+ }
+ }
+
+ public TypeOfBiometricData(ASN1ObjectIdentifier BiometricDataID)
+ {
+ obj = BiometricDataID;
+ }
+
+ public boolean isPredefined()
+ {
+ return obj instanceof ASN1Integer;
+ }
+
+ public int getPredefinedBiometricType()
+ {
+ return ((ASN1Integer)obj).getValue().intValue();
+ }
+
+ public ASN1ObjectIdentifier getBiometricDataOid()
+ {
+ return (ASN1ObjectIdentifier)obj;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return obj.toASN1Primitive();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/sigi/NameOrPseudonym.java b/core/src/main/java/org/bouncycastle/asn1/x509/sigi/NameOrPseudonym.java
new file mode 100644
index 00000000..304f1d46
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/sigi/NameOrPseudonym.java
@@ -0,0 +1,191 @@
+package org.bouncycastle.asn1.x509.sigi;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1String;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x500.DirectoryString;
+
+/**
+ * Structure for a name or pseudonym.
+ *
+ * <pre>
+ * NameOrPseudonym ::= CHOICE {
+ * surAndGivenName SEQUENCE {
+ * surName DirectoryString,
+ * givenName SEQUENCE OF DirectoryString
+ * },
+ * pseudonym DirectoryString
+ * }
+ * </pre>
+ *
+ * @see org.bouncycastle.asn1.x509.sigi.PersonalData
+ *
+ */
+public class NameOrPseudonym
+ extends ASN1Object
+ implements ASN1Choice
+{
+ private DirectoryString pseudonym;
+
+ private DirectoryString surname;
+
+ private ASN1Sequence givenName;
+
+ public static NameOrPseudonym getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof NameOrPseudonym)
+ {
+ return (NameOrPseudonym)obj;
+ }
+
+ if (obj instanceof ASN1String)
+ {
+ return new NameOrPseudonym(DirectoryString.getInstance(obj));
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new NameOrPseudonym((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: "
+ + obj.getClass().getName());
+ }
+
+ /**
+ * Constructor from DirectoryString.
+ * <p/>
+ * The sequence is of type NameOrPseudonym:
+ * <p/>
+ * <pre>
+ * NameOrPseudonym ::= CHOICE {
+ * surAndGivenName SEQUENCE {
+ * surName DirectoryString,
+ * givenName SEQUENCE OF DirectoryString
+ * },
+ * pseudonym DirectoryString
+ * }
+ * </pre>
+ * @param pseudonym pseudonym value to use.
+ */
+ public NameOrPseudonym(DirectoryString pseudonym)
+ {
+ this.pseudonym = pseudonym;
+ }
+
+ /**
+ * Constructor from ASN1Sequence.
+ * <p/>
+ * The sequence is of type NameOrPseudonym:
+ * <p/>
+ * <pre>
+ * NameOrPseudonym ::= CHOICE {
+ * surAndGivenName SEQUENCE {
+ * surName DirectoryString,
+ * givenName SEQUENCE OF DirectoryString
+ * },
+ * pseudonym DirectoryString
+ * }
+ * </pre>
+ *
+ * @param seq The ASN.1 sequence.
+ */
+ private NameOrPseudonym(ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ if (!(seq.getObjectAt(0) instanceof ASN1String))
+ {
+ throw new IllegalArgumentException("Bad object encountered: "
+ + seq.getObjectAt(0).getClass());
+ }
+
+ surname = DirectoryString.getInstance(seq.getObjectAt(0));
+ givenName = ASN1Sequence.getInstance(seq.getObjectAt(1));
+ }
+
+ /**
+ * Constructor from a given details.
+ *
+ * @param pseudonym The pseudonym.
+ */
+ public NameOrPseudonym(String pseudonym)
+ {
+ this(new DirectoryString(pseudonym));
+ }
+
+ /**
+ * Constructor from a given details.
+ *
+ * @param surname The surname.
+ * @param givenName A sequence of directory strings making up the givenName
+ */
+ public NameOrPseudonym(DirectoryString surname, ASN1Sequence givenName)
+ {
+ this.surname = surname;
+ this.givenName = givenName;
+ }
+
+ public DirectoryString getPseudonym()
+ {
+ return pseudonym;
+ }
+
+ public DirectoryString getSurname()
+ {
+ return surname;
+ }
+
+ public DirectoryString[] getGivenName()
+ {
+ DirectoryString[] items = new DirectoryString[givenName.size()];
+ int count = 0;
+ for (Enumeration e = givenName.getObjects(); e.hasMoreElements();)
+ {
+ items[count++] = DirectoryString.getInstance(e.nextElement());
+ }
+ return items;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <p/>
+ * Returns:
+ * <p/>
+ * <pre>
+ * NameOrPseudonym ::= CHOICE {
+ * surAndGivenName SEQUENCE {
+ * surName DirectoryString,
+ * givenName SEQUENCE OF DirectoryString
+ * },
+ * pseudonym DirectoryString
+ * }
+ * </pre>
+ *
+ * @return a DERObject
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ if (pseudonym != null)
+ {
+ return pseudonym.toASN1Primitive();
+ }
+ else
+ {
+ ASN1EncodableVector vec1 = new ASN1EncodableVector();
+ vec1.add(surname);
+ vec1.add(givenName);
+ return new DERSequence(vec1);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/sigi/PersonalData.java b/core/src/main/java/org/bouncycastle/asn1/x509/sigi/PersonalData.java
new file mode 100644
index 00000000..0b732480
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/sigi/PersonalData.java
@@ -0,0 +1,214 @@
+package org.bouncycastle.asn1.x509.sigi;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERPrintableString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x500.DirectoryString;
+
+/**
+ * Contains personal data for the otherName field in the subjectAltNames
+ * extension.
+ * <p/>
+ * <pre>
+ * PersonalData ::= SEQUENCE {
+ * nameOrPseudonym NameOrPseudonym,
+ * nameDistinguisher [0] INTEGER OPTIONAL,
+ * dateOfBirth [1] GeneralizedTime OPTIONAL,
+ * placeOfBirth [2] DirectoryString OPTIONAL,
+ * gender [3] PrintableString OPTIONAL,
+ * postalAddress [4] DirectoryString OPTIONAL
+ * }
+ * </pre>
+ *
+ * @see org.bouncycastle.asn1.x509.sigi.NameOrPseudonym
+ * @see org.bouncycastle.asn1.x509.sigi.SigIObjectIdentifiers
+ */
+public class PersonalData
+ extends ASN1Object
+{
+ private NameOrPseudonym nameOrPseudonym;
+ private BigInteger nameDistinguisher;
+ private ASN1GeneralizedTime dateOfBirth;
+ private DirectoryString placeOfBirth;
+ private String gender;
+ private DirectoryString postalAddress;
+
+ public static PersonalData getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof PersonalData)
+ {
+ return (PersonalData)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new PersonalData((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * Constructor from ASN1Sequence.
+ * <p/>
+ * The sequence is of type NameOrPseudonym:
+ * <p/>
+ * <pre>
+ * PersonalData ::= SEQUENCE {
+ * nameOrPseudonym NameOrPseudonym,
+ * nameDistinguisher [0] INTEGER OPTIONAL,
+ * dateOfBirth [1] GeneralizedTime OPTIONAL,
+ * placeOfBirth [2] DirectoryString OPTIONAL,
+ * gender [3] PrintableString OPTIONAL,
+ * postalAddress [4] DirectoryString OPTIONAL
+ * }
+ * </pre>
+ *
+ * @param seq The ASN.1 sequence.
+ */
+ private PersonalData(ASN1Sequence seq)
+ {
+ if (seq.size() < 1)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ Enumeration e = seq.getObjects();
+
+ nameOrPseudonym = NameOrPseudonym.getInstance(e.nextElement());
+
+ while (e.hasMoreElements())
+ {
+ ASN1TaggedObject o = ASN1TaggedObject.getInstance(e.nextElement());
+ int tag = o.getTagNo();
+ switch (tag)
+ {
+ case 0:
+ nameDistinguisher = ASN1Integer.getInstance(o, false).getValue();
+ break;
+ case 1:
+ dateOfBirth = ASN1GeneralizedTime.getInstance(o, false);
+ break;
+ case 2:
+ placeOfBirth = DirectoryString.getInstance(o, true);
+ break;
+ case 3:
+ gender = DERPrintableString.getInstance(o, false).getString();
+ break;
+ case 4:
+ postalAddress = DirectoryString.getInstance(o, true);
+ break;
+ default:
+ throw new IllegalArgumentException("Bad tag number: " + o.getTagNo());
+ }
+ }
+ }
+
+ /**
+ * Constructor from a given details.
+ *
+ * @param nameOrPseudonym Name or pseudonym.
+ * @param nameDistinguisher Name distinguisher.
+ * @param dateOfBirth Date of birth.
+ * @param placeOfBirth Place of birth.
+ * @param gender Gender.
+ * @param postalAddress Postal Address.
+ */
+ public PersonalData(NameOrPseudonym nameOrPseudonym,
+ BigInteger nameDistinguisher, ASN1GeneralizedTime dateOfBirth,
+ DirectoryString placeOfBirth, String gender, DirectoryString postalAddress)
+ {
+ this.nameOrPseudonym = nameOrPseudonym;
+ this.dateOfBirth = dateOfBirth;
+ this.gender = gender;
+ this.nameDistinguisher = nameDistinguisher;
+ this.postalAddress = postalAddress;
+ this.placeOfBirth = placeOfBirth;
+ }
+
+ public NameOrPseudonym getNameOrPseudonym()
+ {
+ return nameOrPseudonym;
+ }
+
+ public BigInteger getNameDistinguisher()
+ {
+ return nameDistinguisher;
+ }
+
+ public ASN1GeneralizedTime getDateOfBirth()
+ {
+ return dateOfBirth;
+ }
+
+ public DirectoryString getPlaceOfBirth()
+ {
+ return placeOfBirth;
+ }
+
+ public String getGender()
+ {
+ return gender;
+ }
+
+ public DirectoryString getPostalAddress()
+ {
+ return postalAddress;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <p/>
+ * Returns:
+ * <p/>
+ * <pre>
+ * PersonalData ::= SEQUENCE {
+ * nameOrPseudonym NameOrPseudonym,
+ * nameDistinguisher [0] INTEGER OPTIONAL,
+ * dateOfBirth [1] GeneralizedTime OPTIONAL,
+ * placeOfBirth [2] DirectoryString OPTIONAL,
+ * gender [3] PrintableString OPTIONAL,
+ * postalAddress [4] DirectoryString OPTIONAL
+ * }
+ * </pre>
+ *
+ * @return a DERObject
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+ vec.add(nameOrPseudonym);
+ if (nameDistinguisher != null)
+ {
+ vec.add(new DERTaggedObject(false, 0, new ASN1Integer(nameDistinguisher)));
+ }
+ if (dateOfBirth != null)
+ {
+ vec.add(new DERTaggedObject(false, 1, dateOfBirth));
+ }
+ if (placeOfBirth != null)
+ {
+ vec.add(new DERTaggedObject(true, 2, placeOfBirth));
+ }
+ if (gender != null)
+ {
+ vec.add(new DERTaggedObject(false, 3, new DERPrintableString(gender, true)));
+ }
+ if (postalAddress != null)
+ {
+ vec.add(new DERTaggedObject(true, 4, postalAddress));
+ }
+ return new DERSequence(vec);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/sigi/SigIObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/x509/sigi/SigIObjectIdentifiers.java
new file mode 100644
index 00000000..8cac124b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x509/sigi/SigIObjectIdentifiers.java
@@ -0,0 +1,45 @@
+package org.bouncycastle.asn1.x509.sigi;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+/**
+ * Object Identifiers of SigI specifciation (German Signature Law
+ * Interoperability specification).
+ */
+public interface SigIObjectIdentifiers
+{
+ public final static ASN1ObjectIdentifier id_sigi = new ASN1ObjectIdentifier("1.3.36.8");
+
+ /**
+ * Key purpose IDs for German SigI (Signature Interoperability
+ * Specification)
+ */
+ public final static ASN1ObjectIdentifier id_sigi_kp = new ASN1ObjectIdentifier(id_sigi + ".2");
+
+ /**
+ * Certificate policy IDs for German SigI (Signature Interoperability
+ * Specification)
+ */
+ public final static ASN1ObjectIdentifier id_sigi_cp = new ASN1ObjectIdentifier(id_sigi + ".1");
+
+ /**
+ * Other Name IDs for German SigI (Signature Interoperability Specification)
+ */
+ public final static ASN1ObjectIdentifier id_sigi_on = new ASN1ObjectIdentifier(id_sigi + ".4");
+
+ /**
+ * To be used for for the generation of directory service certificates.
+ */
+ public static final ASN1ObjectIdentifier id_sigi_kp_directoryService = new ASN1ObjectIdentifier(id_sigi_kp + ".1");
+
+ /**
+ * ID for PersonalData
+ */
+ public static final ASN1ObjectIdentifier id_sigi_on_personalData = new ASN1ObjectIdentifier(id_sigi_on + ".1");
+
+ /**
+ * Certificate is conform to german signature law.
+ */
+ public static final ASN1ObjectIdentifier id_sigi_cp_sigconform = new ASN1ObjectIdentifier(id_sigi_cp + ".1");
+
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x9/DHDomainParameters.java b/core/src/main/java/org/bouncycastle/asn1/x9/DHDomainParameters.java
new file mode 100644
index 00000000..6a97a48e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x9/DHDomainParameters.java
@@ -0,0 +1,139 @@
+package org.bouncycastle.asn1.x9;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+public class DHDomainParameters
+ extends ASN1Object
+{
+ private ASN1Integer p, g, q, j;
+ private DHValidationParms validationParms;
+
+ public static DHDomainParameters getInstance(ASN1TaggedObject obj, boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static DHDomainParameters getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof DHDomainParameters)
+ {
+ return (DHDomainParameters)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new DHDomainParameters((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid DHDomainParameters: "
+ + obj.getClass().getName());
+ }
+
+ public DHDomainParameters(ASN1Integer p, ASN1Integer g, ASN1Integer q, ASN1Integer j,
+ DHValidationParms validationParms)
+ {
+ if (p == null)
+ {
+ throw new IllegalArgumentException("'p' cannot be null");
+ }
+ if (g == null)
+ {
+ throw new IllegalArgumentException("'g' cannot be null");
+ }
+ if (q == null)
+ {
+ throw new IllegalArgumentException("'q' cannot be null");
+ }
+
+ this.p = p;
+ this.g = g;
+ this.q = q;
+ this.j = j;
+ this.validationParms = validationParms;
+ }
+
+ private DHDomainParameters(ASN1Sequence seq)
+ {
+ if (seq.size() < 3 || seq.size() > 5)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ Enumeration e = seq.getObjects();
+ this.p = ASN1Integer.getInstance(e.nextElement());
+ this.g = ASN1Integer.getInstance(e.nextElement());
+ this.q = ASN1Integer.getInstance(e.nextElement());
+
+ ASN1Encodable next = getNext(e);
+
+ if (next != null && next instanceof ASN1Integer)
+ {
+ this.j = ASN1Integer.getInstance(next);
+ next = getNext(e);
+ }
+
+ if (next != null)
+ {
+ this.validationParms = DHValidationParms.getInstance(next.toASN1Primitive());
+ }
+ }
+
+ private static ASN1Encodable getNext(Enumeration e)
+ {
+ return e.hasMoreElements() ? (ASN1Encodable)e.nextElement() : null;
+ }
+
+ public ASN1Integer getP()
+ {
+ return this.p;
+ }
+
+ public ASN1Integer getG()
+ {
+ return this.g;
+ }
+
+ public ASN1Integer getQ()
+ {
+ return this.q;
+ }
+
+ public ASN1Integer getJ()
+ {
+ return this.j;
+ }
+
+ public DHValidationParms getValidationParms()
+ {
+ return this.validationParms;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(this.p);
+ v.add(this.g);
+ v.add(this.q);
+
+ if (this.j != null)
+ {
+ v.add(this.j);
+ }
+
+ if (this.validationParms != null)
+ {
+ v.add(this.validationParms);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x9/DHPublicKey.java b/core/src/main/java/org/bouncycastle/asn1/x9/DHPublicKey.java
new file mode 100644
index 00000000..7c6d217e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x9/DHPublicKey.java
@@ -0,0 +1,52 @@
+package org.bouncycastle.asn1.x9;
+
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+
+public class DHPublicKey
+ extends ASN1Object
+{
+ private ASN1Integer y;
+
+ public static DHPublicKey getInstance(ASN1TaggedObject obj, boolean explicit)
+ {
+ return getInstance(ASN1Integer.getInstance(obj, explicit));
+ }
+
+ public static DHPublicKey getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof DHPublicKey)
+ {
+ return (DHPublicKey)obj;
+ }
+
+ if (obj instanceof ASN1Integer)
+ {
+ return new DHPublicKey((ASN1Integer)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid DHPublicKey: " + obj.getClass().getName());
+ }
+
+ public DHPublicKey(ASN1Integer y)
+ {
+ if (y == null)
+ {
+ throw new IllegalArgumentException("'y' cannot be null");
+ }
+
+ this.y = y;
+ }
+
+ public ASN1Integer getY()
+ {
+ return this.y;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return this.y;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x9/DHValidationParms.java b/core/src/main/java/org/bouncycastle/asn1/x9/DHValidationParms.java
new file mode 100644
index 00000000..78b09796
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x9/DHValidationParms.java
@@ -0,0 +1,80 @@
+package org.bouncycastle.asn1.x9;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+
+public class DHValidationParms extends ASN1Object
+{
+ private DERBitString seed;
+ private ASN1Integer pgenCounter;
+
+ public static DHValidationParms getInstance(ASN1TaggedObject obj, boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static DHValidationParms getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof DHDomainParameters)
+ {
+ return (DHValidationParms)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new DHValidationParms((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid DHValidationParms: " + obj.getClass().getName());
+ }
+
+ public DHValidationParms(DERBitString seed, ASN1Integer pgenCounter)
+ {
+ if (seed == null)
+ {
+ throw new IllegalArgumentException("'seed' cannot be null");
+ }
+ if (pgenCounter == null)
+ {
+ throw new IllegalArgumentException("'pgenCounter' cannot be null");
+ }
+
+ this.seed = seed;
+ this.pgenCounter = pgenCounter;
+ }
+
+ private DHValidationParms(ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ this.seed = DERBitString.getInstance(seq.getObjectAt(0));
+ this.pgenCounter = ASN1Integer.getInstance(seq.getObjectAt(1));
+ }
+
+ public DERBitString getSeed()
+ {
+ return this.seed;
+ }
+
+ public ASN1Integer getPgenCounter()
+ {
+ return this.pgenCounter;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(this.seed);
+ v.add(this.pgenCounter);
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x9/ECNamedCurveTable.java b/core/src/main/java/org/bouncycastle/asn1/x9/ECNamedCurveTable.java
new file mode 100644
index 00000000..fb545c2b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x9/ECNamedCurveTable.java
@@ -0,0 +1,97 @@
+package org.bouncycastle.asn1.x9;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.nist.NISTNamedCurves;
+import org.bouncycastle.asn1.sec.SECNamedCurves;
+import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves;
+
+/**
+ * A general class that reads all X9.62 style EC curve tables.
+ */
+public class ECNamedCurveTable
+{
+ /**
+ * return a X9ECParameters object representing the passed in named
+ * curve. The routine returns null if the curve is not present.
+ *
+ * @param name the name of the curve requested
+ * @return an X9ECParameters object or null if the curve is not available.
+ */
+ public static X9ECParameters getByName(
+ String name)
+ {
+ X9ECParameters ecP = X962NamedCurves.getByName(name);
+
+ if (ecP == null)
+ {
+ ecP = SECNamedCurves.getByName(name);
+ }
+
+ if (ecP == null)
+ {
+ ecP = TeleTrusTNamedCurves.getByName(name);
+ }
+
+ if (ecP == null)
+ {
+ ecP = NISTNamedCurves.getByName(name);
+ }
+
+ return ecP;
+ }
+
+ /**
+ * return a X9ECParameters object representing the passed in named
+ * curve.
+ *
+ * @param oid the object id of the curve requested
+ * @return an X9ECParameters object or null if the curve is not available.
+ */
+ public static X9ECParameters getByOID(
+ ASN1ObjectIdentifier oid)
+ {
+ X9ECParameters ecP = X962NamedCurves.getByOID(oid);
+
+ if (ecP == null)
+ {
+ ecP = SECNamedCurves.getByOID(oid);
+ }
+
+ if (ecP == null)
+ {
+ ecP = TeleTrusTNamedCurves.getByOID(oid);
+ }
+
+ return ecP;
+ }
+
+ /**
+ * return an enumeration of the names of the available curves.
+ *
+ * @return an enumeration of the names of the available curves.
+ */
+ public static Enumeration getNames()
+ {
+ Vector v = new Vector();
+
+ addEnumeration(v, X962NamedCurves.getNames());
+ addEnumeration(v, SECNamedCurves.getNames());
+ addEnumeration(v, NISTNamedCurves.getNames());
+ addEnumeration(v, TeleTrusTNamedCurves.getNames());
+
+ return v.elements();
+ }
+
+ private static void addEnumeration(
+ Vector v,
+ Enumeration e)
+ {
+ while (e.hasMoreElements())
+ {
+ v.addElement(e.nextElement());
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x9/KeySpecificInfo.java b/core/src/main/java/org/bouncycastle/asn1/x9/KeySpecificInfo.java
new file mode 100644
index 00000000..092716fe
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x9/KeySpecificInfo.java
@@ -0,0 +1,68 @@
+package org.bouncycastle.asn1.x9;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * ASN.1 def for Diffie-Hellman key exchange KeySpecificInfo structure. See
+ * RFC 2631, or X9.42, for further details.
+ */
+public class KeySpecificInfo
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier algorithm;
+ private ASN1OctetString counter;
+
+ public KeySpecificInfo(
+ ASN1ObjectIdentifier algorithm,
+ ASN1OctetString counter)
+ {
+ this.algorithm = algorithm;
+ this.counter = counter;
+ }
+
+ public KeySpecificInfo(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ algorithm = (ASN1ObjectIdentifier)e.nextElement();
+ counter = (ASN1OctetString)e.nextElement();
+ }
+
+ public ASN1ObjectIdentifier getAlgorithm()
+ {
+ return algorithm;
+ }
+
+ public ASN1OctetString getCounter()
+ {
+ return counter;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * KeySpecificInfo ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * counter OCTET STRING SIZE (4..4)
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(algorithm);
+ v.add(counter);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x9/OtherInfo.java b/core/src/main/java/org/bouncycastle/asn1/x9/OtherInfo.java
new file mode 100644
index 00000000..0959244a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x9/OtherInfo.java
@@ -0,0 +1,96 @@
+package org.bouncycastle.asn1.x9;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * ANS.1 def for Diffie-Hellman key exchange OtherInfo structure. See
+ * RFC 2631, or X9.42, for further details.
+ */
+public class OtherInfo
+ extends ASN1Object
+{
+ private KeySpecificInfo keyInfo;
+ private ASN1OctetString partyAInfo;
+ private ASN1OctetString suppPubInfo;
+
+ public OtherInfo(
+ KeySpecificInfo keyInfo,
+ ASN1OctetString partyAInfo,
+ ASN1OctetString suppPubInfo)
+ {
+ this.keyInfo = keyInfo;
+ this.partyAInfo = partyAInfo;
+ this.suppPubInfo = suppPubInfo;
+ }
+
+ public OtherInfo(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ keyInfo = new KeySpecificInfo((ASN1Sequence)e.nextElement());
+
+ while (e.hasMoreElements())
+ {
+ DERTaggedObject o = (DERTaggedObject)e.nextElement();
+
+ if (o.getTagNo() == 0)
+ {
+ partyAInfo = (ASN1OctetString)o.getObject();
+ }
+ else if (o.getTagNo() == 2)
+ {
+ suppPubInfo = (ASN1OctetString)o.getObject();
+ }
+ }
+ }
+
+ public KeySpecificInfo getKeyInfo()
+ {
+ return keyInfo;
+ }
+
+ public ASN1OctetString getPartyAInfo()
+ {
+ return partyAInfo;
+ }
+
+ public ASN1OctetString getSuppPubInfo()
+ {
+ return suppPubInfo;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * OtherInfo ::= SEQUENCE {
+ * keyInfo KeySpecificInfo,
+ * partyAInfo [0] OCTET STRING OPTIONAL,
+ * suppPubInfo [2] OCTET STRING
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(keyInfo);
+
+ if (partyAInfo != null)
+ {
+ v.add(new DERTaggedObject(0, partyAInfo));
+ }
+
+ v.add(new DERTaggedObject(2, suppPubInfo));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x9/X962NamedCurves.java b/core/src/main/java/org/bouncycastle/asn1/x9/X962NamedCurves.java
new file mode 100644
index 00000000..764017e7
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x9/X962NamedCurves.java
@@ -0,0 +1,621 @@
+package org.bouncycastle.asn1.x9;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Hex;
+
+
+/**
+ * table of the current named curves defined in X.962 EC-DSA.
+ */
+public class X962NamedCurves
+{
+ static X9ECParametersHolder prime192v1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ ECCurve cFp192v1 = new ECCurve.Fp(
+ new BigInteger("6277101735386680763835789423207666416083908700390324961279"),
+ new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16),
+ new BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16));
+
+ return new X9ECParameters(
+ cFp192v1,
+ cFp192v1.decodePoint(
+ Hex.decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012")),
+ new BigInteger("ffffffffffffffffffffffff99def836146bc9b1b4d22831", 16),
+ BigInteger.valueOf(1),
+ Hex.decode("3045AE6FC8422f64ED579528D38120EAE12196D5"));
+ }
+ };
+
+ static X9ECParametersHolder prime192v2 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ ECCurve cFp192v2 = new ECCurve.Fp(
+ new BigInteger("6277101735386680763835789423207666416083908700390324961279"),
+ new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16),
+ new BigInteger("cc22d6dfb95c6b25e49c0d6364a4e5980c393aa21668d953", 16));
+
+ return new X9ECParameters(
+ cFp192v2,
+ cFp192v2.decodePoint(
+ Hex.decode("03eea2bae7e1497842f2de7769cfe9c989c072ad696f48034a")),
+ new BigInteger("fffffffffffffffffffffffe5fb1a724dc80418648d8dd31", 16),
+ BigInteger.valueOf(1),
+ Hex.decode("31a92ee2029fd10d901b113e990710f0d21ac6b6"));
+ }
+ };
+
+ static X9ECParametersHolder prime192v3 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ ECCurve cFp192v3 = new ECCurve.Fp(
+ new BigInteger("6277101735386680763835789423207666416083908700390324961279"),
+ new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16),
+ new BigInteger("22123dc2395a05caa7423daeccc94760a7d462256bd56916", 16));
+
+ return new X9ECParameters(
+ cFp192v3,
+ cFp192v3.decodePoint(
+ Hex.decode("027d29778100c65a1da1783716588dce2b8b4aee8e228f1896")),
+ new BigInteger("ffffffffffffffffffffffff7a62d031c83f4294f640ec13", 16),
+ BigInteger.valueOf(1),
+ Hex.decode("c469684435deb378c4b65ca9591e2a5763059a2e"));
+ }
+ };
+
+ static X9ECParametersHolder prime239v1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ ECCurve cFp239v1 = new ECCurve.Fp(
+ new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"),
+ new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16),
+ new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16));
+
+ return new X9ECParameters(
+ cFp239v1,
+ cFp239v1.decodePoint(
+ Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")),
+ new BigInteger("7fffffffffffffffffffffff7fffff9e5e9a9f5d9071fbd1522688909d0b", 16),
+ BigInteger.valueOf(1),
+ Hex.decode("e43bb460f0b80cc0c0b075798e948060f8321b7d"));
+ }
+ };
+
+ static X9ECParametersHolder prime239v2 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ ECCurve cFp239v2 = new ECCurve.Fp(
+ new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"),
+ new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16),
+ new BigInteger("617fab6832576cbbfed50d99f0249c3fee58b94ba0038c7ae84c8c832f2c", 16));
+
+ return new X9ECParameters(
+ cFp239v2,
+ cFp239v2.decodePoint(
+ Hex.decode("0238af09d98727705120c921bb5e9e26296a3cdcf2f35757a0eafd87b830e7")),
+ new BigInteger("7fffffffffffffffffffffff800000cfa7e8594377d414c03821bc582063", 16),
+ BigInteger.valueOf(1),
+ Hex.decode("e8b4011604095303ca3b8099982be09fcb9ae616"));
+ }
+ };
+
+ static X9ECParametersHolder prime239v3 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ ECCurve cFp239v3 = new ECCurve.Fp(
+ new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"),
+ new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16),
+ new BigInteger("255705fa2a306654b1f4cb03d6a750a30c250102d4988717d9ba15ab6d3e", 16));
+
+ return new X9ECParameters(
+ cFp239v3,
+ cFp239v3.decodePoint(
+ Hex.decode("036768ae8e18bb92cfcf005c949aa2c6d94853d0e660bbf854b1c9505fe95a")),
+ new BigInteger("7fffffffffffffffffffffff7fffff975deb41b3a6057c3c432146526551", 16),
+ BigInteger.valueOf(1),
+ Hex.decode("7d7374168ffe3471b60a857686a19475d3bfa2ff"));
+ }
+ };
+
+ static X9ECParametersHolder prime256v1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ ECCurve cFp256v1 = new ECCurve.Fp(
+ new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853951"),
+ new BigInteger("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 16),
+ new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16));
+
+ return new X9ECParameters(
+ cFp256v1,
+ cFp256v1.decodePoint(
+ Hex.decode("036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296")),
+ new BigInteger("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16),
+ BigInteger.valueOf(1),
+ Hex.decode("c49d360886e704936a6678e1139d26b7819f7e90"));
+ }
+ };
+
+ /*
+ * F2m Curves
+ */
+ static X9ECParametersHolder c2pnb163v1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ BigInteger c2m163v1n = new BigInteger("0400000000000000000001E60FC8821CC74DAEAFC1", 16);
+ BigInteger c2m163v1h = BigInteger.valueOf(2);
+
+ ECCurve c2m163v1 = new ECCurve.F2m(
+ 163,
+ 1, 2, 8,
+ new BigInteger("072546B5435234A422E0789675F432C89435DE5242", 16),
+ new BigInteger("00C9517D06D5240D3CFF38C74B20B6CD4D6F9DD4D9", 16),
+ c2m163v1n, c2m163v1h);
+
+ return new X9ECParameters(
+ c2m163v1,
+ c2m163v1.decodePoint(
+ Hex.decode("0307AF69989546103D79329FCC3D74880F33BBE803CB")),
+ c2m163v1n, c2m163v1h,
+ Hex.decode("D2C0FB15760860DEF1EEF4D696E6768756151754"));
+ }
+ };
+
+ static X9ECParametersHolder c2pnb163v2 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ BigInteger c2m163v2n = new BigInteger("03FFFFFFFFFFFFFFFFFFFDF64DE1151ADBB78F10A7", 16);
+ BigInteger c2m163v2h = BigInteger.valueOf(2);
+
+ ECCurve c2m163v2 = new ECCurve.F2m(
+ 163,
+ 1, 2, 8,
+ new BigInteger("0108B39E77C4B108BED981ED0E890E117C511CF072", 16),
+ new BigInteger("0667ACEB38AF4E488C407433FFAE4F1C811638DF20", 16),
+ c2m163v2n, c2m163v2h);
+
+ return new X9ECParameters(
+ c2m163v2,
+ c2m163v2.decodePoint(
+ Hex.decode("030024266E4EB5106D0A964D92C4860E2671DB9B6CC5")),
+ c2m163v2n, c2m163v2h,
+ null);
+ }
+ };
+
+ static X9ECParametersHolder c2pnb163v3 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ BigInteger c2m163v3n = new BigInteger("03FFFFFFFFFFFFFFFFFFFE1AEE140F110AFF961309", 16);
+ BigInteger c2m163v3h = BigInteger.valueOf(2);
+
+ ECCurve c2m163v3 = new ECCurve.F2m(
+ 163,
+ 1, 2, 8,
+ new BigInteger("07A526C63D3E25A256A007699F5447E32AE456B50E", 16),
+ new BigInteger("03F7061798EB99E238FD6F1BF95B48FEEB4854252B", 16),
+ c2m163v3n, c2m163v3h);
+
+ return new X9ECParameters(
+ c2m163v3,
+ c2m163v3.decodePoint(
+ Hex.decode("0202F9F87B7C574D0BDECF8A22E6524775F98CDEBDCB")),
+ c2m163v3n, c2m163v3h,
+ null);
+ }
+ };
+
+ static X9ECParametersHolder c2pnb176w1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ BigInteger c2m176w1n = new BigInteger("010092537397ECA4F6145799D62B0A19CE06FE26AD", 16);
+ BigInteger c2m176w1h = BigInteger.valueOf(0xFF6E);
+
+ ECCurve c2m176w1 = new ECCurve.F2m(
+ 176,
+ 1, 2, 43,
+ new BigInteger("00E4E6DB2995065C407D9D39B8D0967B96704BA8E9C90B", 16),
+ new BigInteger("005DDA470ABE6414DE8EC133AE28E9BBD7FCEC0AE0FFF2", 16),
+ c2m176w1n, c2m176w1h);
+
+ return new X9ECParameters(
+ c2m176w1,
+ c2m176w1.decodePoint(
+ Hex.decode("038D16C2866798B600F9F08BB4A8E860F3298CE04A5798")),
+ c2m176w1n, c2m176w1h,
+ null);
+ }
+ };
+
+ static X9ECParametersHolder c2tnb191v1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ BigInteger c2m191v1n = new BigInteger("40000000000000000000000004A20E90C39067C893BBB9A5", 16);
+ BigInteger c2m191v1h = BigInteger.valueOf(2);
+
+ ECCurve c2m191v1 = new ECCurve.F2m(
+ 191,
+ 9,
+ new BigInteger("2866537B676752636A68F56554E12640276B649EF7526267", 16),
+ new BigInteger("2E45EF571F00786F67B0081B9495A3D95462F5DE0AA185EC", 16),
+ c2m191v1n, c2m191v1h);
+
+ return new X9ECParameters(
+ c2m191v1,
+ c2m191v1.decodePoint(
+ Hex.decode("0236B3DAF8A23206F9C4F299D7B21A9C369137F2C84AE1AA0D")),
+ c2m191v1n, c2m191v1h,
+ Hex.decode("4E13CA542744D696E67687561517552F279A8C84"));
+ }
+ };
+
+ static X9ECParametersHolder c2tnb191v2 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ BigInteger c2m191v2n = new BigInteger("20000000000000000000000050508CB89F652824E06B8173", 16);
+ BigInteger c2m191v2h = BigInteger.valueOf(4);
+
+ ECCurve c2m191v2 = new ECCurve.F2m(
+ 191,
+ 9,
+ new BigInteger("401028774D7777C7B7666D1366EA432071274F89FF01E718", 16),
+ new BigInteger("0620048D28BCBD03B6249C99182B7C8CD19700C362C46A01", 16),
+ c2m191v2n, c2m191v2h);
+
+ return new X9ECParameters(
+ c2m191v2,
+ c2m191v2.decodePoint(
+ Hex.decode("023809B2B7CC1B28CC5A87926AAD83FD28789E81E2C9E3BF10")),
+ c2m191v2n, c2m191v2h,
+ null);
+ }
+ };
+
+ static X9ECParametersHolder c2tnb191v3 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ BigInteger c2m191v3n = new BigInteger("155555555555555555555555610C0B196812BFB6288A3EA3", 16);
+ BigInteger c2m191v3h = BigInteger.valueOf(6);
+
+ ECCurve c2m191v3 = new ECCurve.F2m(
+ 191,
+ 9,
+ new BigInteger("6C01074756099122221056911C77D77E77A777E7E7E77FCB", 16),
+ new BigInteger("71FE1AF926CF847989EFEF8DB459F66394D90F32AD3F15E8", 16),
+ c2m191v3n, c2m191v3h);
+
+ return new X9ECParameters(
+ c2m191v3,
+ c2m191v3.decodePoint(
+ Hex.decode("03375D4CE24FDE434489DE8746E71786015009E66E38A926DD")),
+ c2m191v3n, c2m191v3h,
+ null);
+ }
+ };
+
+ static X9ECParametersHolder c2pnb208w1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ BigInteger c2m208w1n = new BigInteger("0101BAF95C9723C57B6C21DA2EFF2D5ED588BDD5717E212F9D", 16);
+ BigInteger c2m208w1h = BigInteger.valueOf(0xFE48);
+
+ ECCurve c2m208w1 = new ECCurve.F2m(
+ 208,
+ 1, 2, 83,
+ new BigInteger("0", 16),
+ new BigInteger("00C8619ED45A62E6212E1160349E2BFA844439FAFC2A3FD1638F9E", 16),
+ c2m208w1n, c2m208w1h);
+
+ return new X9ECParameters(
+ c2m208w1,
+ c2m208w1.decodePoint(
+ Hex.decode("0289FDFBE4ABE193DF9559ECF07AC0CE78554E2784EB8C1ED1A57A")),
+ c2m208w1n, c2m208w1h,
+ null);
+ }
+ };
+
+ static X9ECParametersHolder c2tnb239v1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ BigInteger c2m239v1n = new BigInteger("2000000000000000000000000000000F4D42FFE1492A4993F1CAD666E447", 16);
+ BigInteger c2m239v1h = BigInteger.valueOf(4);
+
+ ECCurve c2m239v1 = new ECCurve.F2m(
+ 239,
+ 36,
+ new BigInteger("32010857077C5431123A46B808906756F543423E8D27877578125778AC76", 16),
+ new BigInteger("790408F2EEDAF392B012EDEFB3392F30F4327C0CA3F31FC383C422AA8C16", 16),
+ c2m239v1n, c2m239v1h);
+
+ return new X9ECParameters(
+ c2m239v1,
+ c2m239v1.decodePoint(
+ Hex.decode("0257927098FA932E7C0A96D3FD5B706EF7E5F5C156E16B7E7C86038552E91D")),
+ c2m239v1n, c2m239v1h,
+ null);
+ }
+ };
+
+ static X9ECParametersHolder c2tnb239v2 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ BigInteger c2m239v2n = new BigInteger("1555555555555555555555555555553C6F2885259C31E3FCDF154624522D", 16);
+ BigInteger c2m239v2h = BigInteger.valueOf(6);
+
+ ECCurve c2m239v2 = new ECCurve.F2m(
+ 239,
+ 36,
+ new BigInteger("4230017757A767FAE42398569B746325D45313AF0766266479B75654E65F", 16),
+ new BigInteger("5037EA654196CFF0CD82B2C14A2FCF2E3FF8775285B545722F03EACDB74B", 16),
+ c2m239v2n, c2m239v2h);
+
+ return new X9ECParameters(
+ c2m239v2,
+ c2m239v2.decodePoint(
+ Hex.decode("0228F9D04E900069C8DC47A08534FE76D2B900B7D7EF31F5709F200C4CA205")),
+ c2m239v2n, c2m239v2h,
+ null);
+ }
+ };
+
+ static X9ECParametersHolder c2tnb239v3 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ BigInteger c2m239v3n = new BigInteger("0CCCCCCCCCCCCCCCCCCCCCCCCCCCCCAC4912D2D9DF903EF9888B8A0E4CFF", 16);
+ BigInteger c2m239v3h = BigInteger.valueOf(10);
+
+ ECCurve c2m239v3 = new ECCurve.F2m(
+ 239,
+ 36,
+ new BigInteger("01238774666A67766D6676F778E676B66999176666E687666D8766C66A9F", 16),
+ new BigInteger("6A941977BA9F6A435199ACFC51067ED587F519C5ECB541B8E44111DE1D40", 16),
+ c2m239v3n, c2m239v3h);
+
+ return new X9ECParameters(
+ c2m239v3,
+ c2m239v3.decodePoint(
+ Hex.decode("0370F6E9D04D289C4E89913CE3530BFDE903977D42B146D539BF1BDE4E9C92")),
+ c2m239v3n, c2m239v3h,
+ null);
+ }
+ };
+
+ static X9ECParametersHolder c2pnb272w1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ BigInteger c2m272w1n = new BigInteger("0100FAF51354E0E39E4892DF6E319C72C8161603FA45AA7B998A167B8F1E629521", 16);
+ BigInteger c2m272w1h = BigInteger.valueOf(0xFF06);
+
+ ECCurve c2m272w1 = new ECCurve.F2m(
+ 272,
+ 1, 3, 56,
+ new BigInteger("0091A091F03B5FBA4AB2CCF49C4EDD220FB028712D42BE752B2C40094DBACDB586FB20", 16),
+ new BigInteger("7167EFC92BB2E3CE7C8AAAFF34E12A9C557003D7C73A6FAF003F99F6CC8482E540F7", 16),
+ c2m272w1n, c2m272w1h);
+
+ return new X9ECParameters(
+ c2m272w1,
+ c2m272w1.decodePoint(
+ Hex.decode("026108BABB2CEEBCF787058A056CBE0CFE622D7723A289E08A07AE13EF0D10D171DD8D")),
+ c2m272w1n, c2m272w1h,
+ null);
+ }
+ };
+
+ static X9ECParametersHolder c2pnb304w1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ BigInteger c2m304w1n = new BigInteger("0101D556572AABAC800101D556572AABAC8001022D5C91DD173F8FB561DA6899164443051D", 16);
+ BigInteger c2m304w1h = BigInteger.valueOf(0xFE2E);
+
+ ECCurve c2m304w1 = new ECCurve.F2m(
+ 304,
+ 1, 2, 11,
+ new BigInteger("00FD0D693149A118F651E6DCE6802085377E5F882D1B510B44160074C1288078365A0396C8E681", 16),
+ new BigInteger("00BDDB97E555A50A908E43B01C798EA5DAA6788F1EA2794EFCF57166B8C14039601E55827340BE", 16),
+ c2m304w1n, c2m304w1h);
+
+ return new X9ECParameters(
+ c2m304w1,
+ c2m304w1.decodePoint(
+ Hex.decode("02197B07845E9BE2D96ADB0F5F3C7F2CFFBD7A3EB8B6FEC35C7FD67F26DDF6285A644F740A2614")),
+ c2m304w1n, c2m304w1h,
+ null);
+ }
+ };
+
+ static X9ECParametersHolder c2tnb359v1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ BigInteger c2m359v1n = new BigInteger("01AF286BCA1AF286BCA1AF286BCA1AF286BCA1AF286BC9FB8F6B85C556892C20A7EB964FE7719E74F490758D3B", 16);
+ BigInteger c2m359v1h = BigInteger.valueOf(0x4C);
+
+ ECCurve c2m359v1 = new ECCurve.F2m(
+ 359,
+ 68,
+ new BigInteger("5667676A654B20754F356EA92017D946567C46675556F19556A04616B567D223A5E05656FB549016A96656A557", 16),
+ new BigInteger("2472E2D0197C49363F1FE7F5B6DB075D52B6947D135D8CA445805D39BC345626089687742B6329E70680231988", 16),
+ c2m359v1n, c2m359v1h);
+
+ return new X9ECParameters(
+ c2m359v1,
+ c2m359v1.decodePoint(
+ Hex.decode("033C258EF3047767E7EDE0F1FDAA79DAEE3841366A132E163ACED4ED2401DF9C6BDCDE98E8E707C07A2239B1B097")),
+ c2m359v1n, c2m359v1h,
+ null);
+ }
+ };
+
+ static X9ECParametersHolder c2pnb368w1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ BigInteger c2m368w1n = new BigInteger("010090512DA9AF72B08349D98A5DD4C7B0532ECA51CE03E2D10F3B7AC579BD87E909AE40A6F131E9CFCE5BD967", 16);
+ BigInteger c2m368w1h = BigInteger.valueOf(0xFF70);
+
+ ECCurve c2m368w1 = new ECCurve.F2m(
+ 368,
+ 1, 2, 85,
+ new BigInteger("00E0D2EE25095206F5E2A4F9ED229F1F256E79A0E2B455970D8D0D865BD94778C576D62F0AB7519CCD2A1A906AE30D", 16),
+ new BigInteger("00FC1217D4320A90452C760A58EDCD30C8DD069B3C34453837A34ED50CB54917E1C2112D84D164F444F8F74786046A", 16),
+ c2m368w1n, c2m368w1h);
+
+ return new X9ECParameters(
+ c2m368w1,
+ c2m368w1.decodePoint(
+ Hex.decode("021085E2755381DCCCE3C1557AFA10C2F0C0C2825646C5B34A394CBCFA8BC16B22E7E789E927BE216F02E1FB136A5F")),
+ c2m368w1n, c2m368w1h,
+ null);
+ }
+ };
+
+ static X9ECParametersHolder c2tnb431r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ BigInteger c2m431r1n = new BigInteger("0340340340340340340340340340340340340340340340340340340323C313FAB50589703B5EC68D3587FEC60D161CC149C1AD4A91", 16);
+ BigInteger c2m431r1h = BigInteger.valueOf(0x2760);
+
+ ECCurve c2m431r1 = new ECCurve.F2m(
+ 431,
+ 120,
+ new BigInteger("1A827EF00DD6FC0E234CAF046C6A5D8A85395B236CC4AD2CF32A0CADBDC9DDF620B0EB9906D0957F6C6FEACD615468DF104DE296CD8F", 16),
+ new BigInteger("10D9B4A3D9047D8B154359ABFB1B7F5485B04CEB868237DDC9DEDA982A679A5A919B626D4E50A8DD731B107A9962381FB5D807BF2618", 16),
+ c2m431r1n, c2m431r1h);
+
+ return new X9ECParameters(
+ c2m431r1,
+ c2m431r1.decodePoint(
+ Hex.decode("02120FC05D3C67A99DE161D2F4092622FECA701BE4F50F4758714E8A87BBF2A658EF8C21E7C5EFE965361F6C2999C0C247B0DBD70CE6B7")),
+ c2m431r1n, c2m431r1h,
+ null);
+ }
+ };
+
+ static final Hashtable objIds = new Hashtable();
+ static final Hashtable curves = new Hashtable();
+ static final Hashtable names = new Hashtable();
+
+ static void defineCurve(String name, ASN1ObjectIdentifier oid, X9ECParametersHolder holder)
+ {
+ objIds.put(name, oid);
+ names.put(oid, name);
+ curves.put(oid, holder);
+ }
+
+ static
+ {
+ defineCurve("prime192v1", X9ObjectIdentifiers.prime192v1, prime192v1);
+ defineCurve("prime192v2", X9ObjectIdentifiers.prime192v2, prime192v2);
+ defineCurve("prime192v3", X9ObjectIdentifiers.prime192v3, prime192v3);
+ defineCurve("prime239v1", X9ObjectIdentifiers.prime239v1, prime239v1);
+ defineCurve("prime239v2", X9ObjectIdentifiers.prime239v2, prime239v2);
+ defineCurve("prime239v3", X9ObjectIdentifiers.prime239v3, prime239v3);
+ defineCurve("prime256v1", X9ObjectIdentifiers.prime256v1, prime256v1);
+ defineCurve("c2pnb163v1", X9ObjectIdentifiers.c2pnb163v1, c2pnb163v1);
+ defineCurve("c2pnb163v2", X9ObjectIdentifiers.c2pnb163v2, c2pnb163v2);
+ defineCurve("c2pnb163v3", X9ObjectIdentifiers.c2pnb163v3, c2pnb163v3);
+ defineCurve("c2pnb176w1", X9ObjectIdentifiers.c2pnb176w1, c2pnb176w1);
+ defineCurve("c2tnb191v1", X9ObjectIdentifiers.c2tnb191v1, c2tnb191v1);
+ defineCurve("c2tnb191v2", X9ObjectIdentifiers.c2tnb191v2, c2tnb191v2);
+ defineCurve("c2tnb191v3", X9ObjectIdentifiers.c2tnb191v3, c2tnb191v3);
+ defineCurve("c2pnb208w1", X9ObjectIdentifiers.c2pnb208w1, c2pnb208w1);
+ defineCurve("c2tnb239v1", X9ObjectIdentifiers.c2tnb239v1, c2tnb239v1);
+ defineCurve("c2tnb239v2", X9ObjectIdentifiers.c2tnb239v2, c2tnb239v2);
+ defineCurve("c2tnb239v3", X9ObjectIdentifiers.c2tnb239v3, c2tnb239v3);
+ defineCurve("c2pnb272w1", X9ObjectIdentifiers.c2pnb272w1, c2pnb272w1);
+ defineCurve("c2pnb304w1", X9ObjectIdentifiers.c2pnb304w1, c2pnb304w1);
+ defineCurve("c2tnb359v1", X9ObjectIdentifiers.c2tnb359v1, c2tnb359v1);
+ defineCurve("c2pnb368w1", X9ObjectIdentifiers.c2pnb368w1, c2pnb368w1);
+ defineCurve("c2tnb431r1", X9ObjectIdentifiers.c2tnb431r1, c2tnb431r1);
+ }
+
+ public static X9ECParameters getByName(
+ String name)
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)objIds.get(Strings.toLowerCase(name));
+
+ if (oid != null)
+ {
+ return getByOID(oid);
+ }
+
+ return null;
+ }
+
+ /**
+ * return the X9ECParameters object for the named curve represented by
+ * the passed in object identifier. Null if the curve isn't present.
+ *
+ * @param oid an object identifier representing a named curve, if present.
+ */
+ public static X9ECParameters getByOID(
+ ASN1ObjectIdentifier oid)
+ {
+ X9ECParametersHolder holder = (X9ECParametersHolder)curves.get(oid);
+
+ if (holder != null)
+ {
+ return holder.getParameters();
+ }
+
+ return null;
+ }
+
+ /**
+ * return the object identifier signified by the passed in name. Null
+ * if there is no object identifier associated with name.
+ *
+ * @return the object identifier associated with name, if present.
+ */
+ public static ASN1ObjectIdentifier getOID(
+ String name)
+ {
+ return (ASN1ObjectIdentifier)objIds.get(Strings.toLowerCase(name));
+ }
+
+ /**
+ * return the named curve name represented by the given object identifier.
+ */
+ public static String getName(
+ ASN1ObjectIdentifier oid)
+ {
+ return (String)names.get(oid);
+ }
+
+ /**
+ * returns an enumeration containing the name strings for curves
+ * contained in this structure.
+ */
+ public static Enumeration getNames()
+ {
+ return objIds.keys();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x9/X962Parameters.java b/core/src/main/java/org/bouncycastle/asn1/x9/X962Parameters.java
new file mode 100644
index 00000000..1c395d20
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x9/X962Parameters.java
@@ -0,0 +1,86 @@
+package org.bouncycastle.asn1.x9;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Null;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+
+public class X962Parameters
+ extends ASN1Object
+ implements ASN1Choice
+{
+ private ASN1Primitive params = null;
+
+ public static X962Parameters getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof X962Parameters)
+ {
+ return (X962Parameters)obj;
+ }
+
+ if (obj instanceof ASN1Primitive)
+ {
+ return new X962Parameters((ASN1Primitive)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in getInstance()");
+ }
+
+ public static X962Parameters getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject()); // must be explicitly tagged
+ }
+
+ public X962Parameters(
+ X9ECParameters ecParameters)
+ {
+ this.params = ecParameters.toASN1Primitive();
+ }
+
+ public X962Parameters(
+ ASN1ObjectIdentifier namedCurve)
+ {
+ this.params = namedCurve;
+ }
+
+ public X962Parameters(
+ ASN1Primitive obj)
+ {
+ this.params = obj;
+ }
+
+ public boolean isNamedCurve()
+ {
+ return (params instanceof ASN1ObjectIdentifier);
+ }
+
+ public boolean isImplicitlyCA()
+ {
+ return (params instanceof ASN1Null);
+ }
+
+ public ASN1Primitive getParameters()
+ {
+ return params;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * Parameters ::= CHOICE {
+ * ecParameters ECParameters,
+ * namedCurve CURVES.&id({CurveNames}),
+ * implicitlyCA NULL
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return (ASN1Primitive)params;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x9/X9Curve.java b/core/src/main/java/org/bouncycastle/asn1/x9/X9Curve.java
new file mode 100644
index 00000000..f233657e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x9/X9Curve.java
@@ -0,0 +1,161 @@
+package org.bouncycastle.asn1.x9;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.math.ec.ECCurve;
+
+/**
+ * ASN.1 def for Elliptic-Curve Curve structure. See
+ * X9.62, for further details.
+ */
+public class X9Curve
+ extends ASN1Object
+ implements X9ObjectIdentifiers
+{
+ private ECCurve curve;
+ private byte[] seed;
+ private ASN1ObjectIdentifier fieldIdentifier = null;
+
+ public X9Curve(
+ ECCurve curve)
+ {
+ this.curve = curve;
+ this.seed = null;
+ setFieldIdentifier();
+ }
+
+ public X9Curve(
+ ECCurve curve,
+ byte[] seed)
+ {
+ this.curve = curve;
+ this.seed = seed;
+ setFieldIdentifier();
+ }
+
+ public X9Curve(
+ X9FieldID fieldID,
+ ASN1Sequence seq)
+ {
+ fieldIdentifier = fieldID.getIdentifier();
+ if (fieldIdentifier.equals(prime_field))
+ {
+ BigInteger p = ((ASN1Integer)fieldID.getParameters()).getValue();
+ X9FieldElement x9A = new X9FieldElement(p, (ASN1OctetString)seq.getObjectAt(0));
+ X9FieldElement x9B = new X9FieldElement(p, (ASN1OctetString)seq.getObjectAt(1));
+ curve = new ECCurve.Fp(p, x9A.getValue().toBigInteger(), x9B.getValue().toBigInteger());
+ }
+ else if (fieldIdentifier.equals(characteristic_two_field))
+ {
+ // Characteristic two field
+ ASN1Sequence parameters = ASN1Sequence.getInstance(fieldID.getParameters());
+ int m = ((ASN1Integer)parameters.getObjectAt(0)).getValue().
+ intValue();
+ ASN1ObjectIdentifier representation
+ = (ASN1ObjectIdentifier)parameters.getObjectAt(1);
+
+ int k1 = 0;
+ int k2 = 0;
+ int k3 = 0;
+
+ if (representation.equals(tpBasis))
+ {
+ // Trinomial basis representation
+ k1 = ASN1Integer.getInstance(parameters.getObjectAt(2)).getValue().intValue();
+ }
+ else if (representation.equals(ppBasis))
+ {
+ // Pentanomial basis representation
+ ASN1Sequence pentanomial = ASN1Sequence.getInstance(parameters.getObjectAt(2));
+ k1 = ASN1Integer.getInstance(pentanomial.getObjectAt(0)).getValue().intValue();
+ k2 = ASN1Integer.getInstance(pentanomial.getObjectAt(1)).getValue().intValue();
+ k3 = ASN1Integer.getInstance(pentanomial.getObjectAt(2)).getValue().intValue();
+ }
+ else
+ {
+ throw new IllegalArgumentException("This type of EC basis is not implemented");
+ }
+ X9FieldElement x9A = new X9FieldElement(m, k1, k2, k3, (ASN1OctetString)seq.getObjectAt(0));
+ X9FieldElement x9B = new X9FieldElement(m, k1, k2, k3, (ASN1OctetString)seq.getObjectAt(1));
+ // TODO Is it possible to get the order (n) and cofactor(h) too?
+ curve = new ECCurve.F2m(m, k1, k2, k3, x9A.getValue().toBigInteger(), x9B.getValue().toBigInteger());
+ }
+ else
+ {
+ throw new IllegalArgumentException("This type of ECCurve is not implemented");
+ }
+
+ if (seq.size() == 3)
+ {
+ seed = ((DERBitString)seq.getObjectAt(2)).getBytes();
+ }
+ }
+
+ private void setFieldIdentifier()
+ {
+ if (curve instanceof ECCurve.Fp)
+ {
+ fieldIdentifier = prime_field;
+ }
+ else if (curve instanceof ECCurve.F2m)
+ {
+ fieldIdentifier = characteristic_two_field;
+ }
+ else
+ {
+ throw new IllegalArgumentException("This type of ECCurve is not implemented");
+ }
+ }
+
+ public ECCurve getCurve()
+ {
+ return curve;
+ }
+
+ public byte[] getSeed()
+ {
+ return seed;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * Curve ::= SEQUENCE {
+ * a FieldElement,
+ * b FieldElement,
+ * seed BIT STRING OPTIONAL
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (fieldIdentifier.equals(prime_field))
+ {
+ v.add(new X9FieldElement(curve.getA()).toASN1Primitive());
+ v.add(new X9FieldElement(curve.getB()).toASN1Primitive());
+ }
+ else if (fieldIdentifier.equals(characteristic_two_field))
+ {
+ v.add(new X9FieldElement(curve.getA()).toASN1Primitive());
+ v.add(new X9FieldElement(curve.getB()).toASN1Primitive());
+ }
+
+ if (seed != null)
+ {
+ v.add(new DERBitString(seed));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java b/core/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java
new file mode 100644
index 00000000..e0590890
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java
@@ -0,0 +1,176 @@
+package org.bouncycastle.asn1.x9;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECPoint;
+
+/**
+ * ASN.1 def for Elliptic-Curve ECParameters structure. See
+ * X9.62, for further details.
+ */
+public class X9ECParameters
+ extends ASN1Object
+ implements X9ObjectIdentifiers
+{
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ private X9FieldID fieldID;
+ private ECCurve curve;
+ private ECPoint g;
+ private BigInteger n;
+ private BigInteger h;
+ private byte[] seed;
+
+ private X9ECParameters(
+ ASN1Sequence seq)
+ {
+ if (!(seq.getObjectAt(0) instanceof ASN1Integer)
+ || !((ASN1Integer)seq.getObjectAt(0)).getValue().equals(ONE))
+ {
+ throw new IllegalArgumentException("bad version in X9ECParameters");
+ }
+
+ X9Curve x9c = new X9Curve(
+ new X9FieldID((ASN1Sequence)seq.getObjectAt(1)),
+ (ASN1Sequence)seq.getObjectAt(2));
+
+ this.curve = x9c.getCurve();
+ this.g = new X9ECPoint(curve, (ASN1OctetString)seq.getObjectAt(3)).getPoint();
+ this.n = ((ASN1Integer)seq.getObjectAt(4)).getValue();
+ this.seed = x9c.getSeed();
+
+ if (seq.size() == 6)
+ {
+ this.h = ((ASN1Integer)seq.getObjectAt(5)).getValue();
+ }
+ }
+
+ public static X9ECParameters getInstance(Object obj)
+ {
+ if (obj instanceof X9ECParameters)
+ {
+ return (X9ECParameters)obj;
+ }
+
+ if (obj != null)
+ {
+ return new X9ECParameters(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public X9ECParameters(
+ ECCurve curve,
+ ECPoint g,
+ BigInteger n)
+ {
+ this(curve, g, n, ONE, null);
+ }
+
+ public X9ECParameters(
+ ECCurve curve,
+ ECPoint g,
+ BigInteger n,
+ BigInteger h)
+ {
+ this(curve, g, n, h, null);
+ }
+
+ public X9ECParameters(
+ ECCurve curve,
+ ECPoint g,
+ BigInteger n,
+ BigInteger h,
+ byte[] seed)
+ {
+ this.curve = curve;
+ this.g = g;
+ this.n = n;
+ this.h = h;
+ this.seed = seed;
+
+ if (curve instanceof ECCurve.Fp)
+ {
+ this.fieldID = new X9FieldID(((ECCurve.Fp)curve).getQ());
+ }
+ else
+ {
+ if (curve instanceof ECCurve.F2m)
+ {
+ ECCurve.F2m curveF2m = (ECCurve.F2m)curve;
+ this.fieldID = new X9FieldID(curveF2m.getM(), curveF2m.getK1(),
+ curveF2m.getK2(), curveF2m.getK3());
+ }
+ }
+ }
+
+ public ECCurve getCurve()
+ {
+ return curve;
+ }
+
+ public ECPoint getG()
+ {
+ return g;
+ }
+
+ public BigInteger getN()
+ {
+ return n;
+ }
+
+ public BigInteger getH()
+ {
+ if (h == null)
+ {
+ return ONE; // TODO - this should be calculated, it will cause issues with custom curves.
+ }
+
+ return h;
+ }
+
+ public byte[] getSeed()
+ {
+ return seed;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * ECParameters ::= SEQUENCE {
+ * version INTEGER { ecpVer1(1) } (ecpVer1),
+ * fieldID FieldID {{FieldTypes}},
+ * curve X9Curve,
+ * base X9ECPoint,
+ * order INTEGER,
+ * cofactor INTEGER OPTIONAL
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new ASN1Integer(1));
+ v.add(fieldID);
+ v.add(new X9Curve(curve, seed));
+ v.add(new X9ECPoint(g));
+ v.add(new ASN1Integer(n));
+
+ if (h != null)
+ {
+ v.add(new ASN1Integer(h));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x9/X9ECParametersHolder.java b/core/src/main/java/org/bouncycastle/asn1/x9/X9ECParametersHolder.java
new file mode 100644
index 00000000..47361f89
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x9/X9ECParametersHolder.java
@@ -0,0 +1,18 @@
+package org.bouncycastle.asn1.x9;
+
+public abstract class X9ECParametersHolder
+{
+ private X9ECParameters params;
+
+ public X9ECParameters getParameters()
+ {
+ if (params == null)
+ {
+ params = createParameters();
+ }
+
+ return params;
+ }
+
+ protected abstract X9ECParameters createParameters();
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x9/X9ECPoint.java b/core/src/main/java/org/bouncycastle/asn1/x9/X9ECPoint.java
new file mode 100644
index 00000000..a4acb6e9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x9/X9ECPoint.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.asn1.x9;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECPoint;
+
+/**
+ * class for describing an ECPoint as a DER object.
+ */
+public class X9ECPoint
+ extends ASN1Object
+{
+ ECPoint p;
+
+ public X9ECPoint(
+ ECPoint p)
+ {
+ this.p = p;
+ }
+
+ public X9ECPoint(
+ ECCurve c,
+ ASN1OctetString s)
+ {
+ this.p = c.decodePoint(s.getOctets());
+ }
+
+ public ECPoint getPoint()
+ {
+ return p;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * ECPoint ::= OCTET STRING
+ * </pre>
+ * <p>
+ * Octet string produced using ECPoint.getEncoded().
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return new DEROctetString(p.getEncoded());
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x9/X9FieldElement.java b/core/src/main/java/org/bouncycastle/asn1/x9/X9FieldElement.java
new file mode 100644
index 00000000..13fe7721
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x9/X9FieldElement.java
@@ -0,0 +1,64 @@
+package org.bouncycastle.asn1.x9;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.math.ec.ECFieldElement;
+
+/**
+ * class for processing an FieldElement as a DER object.
+ */
+public class X9FieldElement
+ extends ASN1Object
+{
+ protected ECFieldElement f;
+
+ private static X9IntegerConverter converter = new X9IntegerConverter();
+
+ public X9FieldElement(ECFieldElement f)
+ {
+ this.f = f;
+ }
+
+ public X9FieldElement(BigInteger p, ASN1OctetString s)
+ {
+ this(new ECFieldElement.Fp(p, new BigInteger(1, s.getOctets())));
+ }
+
+ public X9FieldElement(int m, int k1, int k2, int k3, ASN1OctetString s)
+ {
+ this(new ECFieldElement.F2m(m, k1, k2, k3, new BigInteger(1, s.getOctets())));
+ }
+
+ public ECFieldElement getValue()
+ {
+ return f;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * FieldElement ::= OCTET STRING
+ * </pre>
+ * <p>
+ * <ol>
+ * <li> if <i>q</i> is an odd prime then the field element is
+ * processed as an Integer and converted to an octet string
+ * according to x 9.62 4.3.1.</li>
+ * <li> if <i>q</i> is 2<sup>m</sup> then the bit string
+ * contained in the field element is converted into an octet
+ * string with the same ordering padded at the front if necessary.
+ * </li>
+ * </ol>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ int byteCount = converter.getByteLength(f);
+ byte[] paddedBigInteger = converter.integerToBytes(f.toBigInteger(), byteCount);
+
+ return new DEROctetString(paddedBigInteger);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x9/X9FieldID.java b/core/src/main/java/org/bouncycastle/asn1/x9/X9FieldID.java
new file mode 100644
index 00000000..30598e2e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x9/X9FieldID.java
@@ -0,0 +1,109 @@
+package org.bouncycastle.asn1.x9;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * ASN.1 def for Elliptic-Curve Field ID structure. See
+ * X9.62, for further details.
+ */
+public class X9FieldID
+ extends ASN1Object
+ implements X9ObjectIdentifiers
+{
+ private ASN1ObjectIdentifier id;
+ private ASN1Primitive parameters;
+
+ /**
+ * Constructor for elliptic curves over prime fields
+ * <code>F<sub>2</sub></code>.
+ * @param primeP The prime <code>p</code> defining the prime field.
+ */
+ public X9FieldID(BigInteger primeP)
+ {
+ this.id = prime_field;
+ this.parameters = new ASN1Integer(primeP);
+ }
+
+ /**
+ * Constructor for elliptic curves over binary fields
+ * <code>F<sub>2<sup>m</sup></sub></code>.
+ * @param m The exponent <code>m</code> of
+ * <code>F<sub>2<sup>m</sup></sub></code>.
+ * @param k1 The integer <code>k1</code> where <code>x<sup>m</sup> +
+ * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+ * represents the reduction polynomial <code>f(z)</code>.
+ * @param k2 The integer <code>k2</code> where <code>x<sup>m</sup> +
+ * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+ * represents the reduction polynomial <code>f(z)</code>.
+ * @param k3 The integer <code>k3</code> where <code>x<sup>m</sup> +
+ * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+ * represents the reduction polynomial <code>f(z)</code>..
+ */
+ public X9FieldID(int m, int k1, int k2, int k3)
+ {
+ this.id = characteristic_two_field;
+ ASN1EncodableVector fieldIdParams = new ASN1EncodableVector();
+ fieldIdParams.add(new ASN1Integer(m));
+
+ if (k2 == 0)
+ {
+ fieldIdParams.add(tpBasis);
+ fieldIdParams.add(new ASN1Integer(k1));
+ }
+ else
+ {
+ fieldIdParams.add(ppBasis);
+ ASN1EncodableVector pentanomialParams = new ASN1EncodableVector();
+ pentanomialParams.add(new ASN1Integer(k1));
+ pentanomialParams.add(new ASN1Integer(k2));
+ pentanomialParams.add(new ASN1Integer(k3));
+ fieldIdParams.add(new DERSequence(pentanomialParams));
+ }
+
+ this.parameters = new DERSequence(fieldIdParams);
+ }
+
+ public X9FieldID(
+ ASN1Sequence seq)
+ {
+ this.id = (ASN1ObjectIdentifier)seq.getObjectAt(0);
+ this.parameters = (ASN1Primitive)seq.getObjectAt(1);
+ }
+
+ public ASN1ObjectIdentifier getIdentifier()
+ {
+ return id;
+ }
+
+ public ASN1Primitive getParameters()
+ {
+ return parameters;
+ }
+
+ /**
+ * Produce a DER encoding of the following structure.
+ * <pre>
+ * FieldID ::= SEQUENCE {
+ * fieldType FIELD-ID.&amp;id({IOSet}),
+ * parameters FIELD-ID.&amp;Type({IOSet}{&#64;fieldType})
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(this.id);
+ v.add(this.parameters);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x9/X9IntegerConverter.java b/core/src/main/java/org/bouncycastle/asn1/x9/X9IntegerConverter.java
new file mode 100644
index 00000000..16a803cc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x9/X9IntegerConverter.java
@@ -0,0 +1,47 @@
+package org.bouncycastle.asn1.x9;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+
+public class X9IntegerConverter
+{
+ public int getByteLength(
+ ECCurve c)
+ {
+ return (c.getFieldSize() + 7) / 8;
+ }
+
+ public int getByteLength(
+ ECFieldElement fe)
+ {
+ return (fe.getFieldSize() + 7) / 8;
+ }
+
+ public byte[] integerToBytes(
+ BigInteger s,
+ int qLength)
+ {
+ byte[] bytes = s.toByteArray();
+
+ if (qLength < bytes.length)
+ {
+ byte[] tmp = new byte[qLength];
+
+ System.arraycopy(bytes, bytes.length - tmp.length, tmp, 0, tmp.length);
+
+ return tmp;
+ }
+ else if (qLength > bytes.length)
+ {
+ byte[] tmp = new byte[qLength];
+
+ System.arraycopy(bytes, 0, tmp, tmp.length - bytes.length, bytes.length);
+
+ return tmp;
+ }
+
+ return bytes;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java
new file mode 100644
index 00000000..f005cfa8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java
@@ -0,0 +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");
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/AsymmetricBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/AsymmetricBlockCipher.java
new file mode 100644
index 00000000..565effcc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/AsymmetricBlockCipher.java
@@ -0,0 +1,45 @@
+package org.bouncycastle.crypto;
+
+
+/**
+ * base interface that a public/private key block cipher needs
+ * to conform to.
+ */
+public interface AsymmetricBlockCipher
+{
+ /**
+ * initialise the cipher.
+ *
+ * @param forEncryption if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param param the key and other data required by the cipher.
+ */
+ public void init(boolean forEncryption, CipherParameters param);
+
+ /**
+ * returns the largest size an input block can be.
+ *
+ * @return maximum size for an input block.
+ */
+ public int getInputBlockSize();
+
+ /**
+ * returns the maximum size of the block produced by this cipher.
+ *
+ * @return maximum size of the output block produced by the cipher.
+ */
+ public int getOutputBlockSize();
+
+ /**
+ * process the block of len bytes stored in in from offset inOff.
+ *
+ * @param in the input data
+ * @param inOff offset into the in array where the data starts
+ * @param len the length of the block to be processed.
+ * @return the resulting byte array of the encryption/decryption process.
+ * @exception InvalidCipherTextException data decrypts improperly.
+ * @exception DataLengthException the input data is too large for the cipher.
+ */
+ public byte[] processBlock(byte[] in, int inOff, int len)
+ throws InvalidCipherTextException;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/AsymmetricCipherKeyPair.java b/core/src/main/java/org/bouncycastle/crypto/AsymmetricCipherKeyPair.java
new file mode 100644
index 00000000..ddee7019
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/AsymmetricCipherKeyPair.java
@@ -0,0 +1,61 @@
+package org.bouncycastle.crypto;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+
+/**
+ * a holding class for public/private parameter pairs.
+ */
+public class AsymmetricCipherKeyPair
+{
+ private AsymmetricKeyParameter publicParam;
+ private AsymmetricKeyParameter privateParam;
+
+ /**
+ * basic constructor.
+ *
+ * @param publicParam a public key parameters object.
+ * @param privateParam the corresponding private key parameters.
+ */
+ public AsymmetricCipherKeyPair(
+ AsymmetricKeyParameter publicParam,
+ AsymmetricKeyParameter privateParam)
+ {
+ this.publicParam = publicParam;
+ this.privateParam = privateParam;
+ }
+
+ /**
+ * basic constructor.
+ *
+ * @param publicParam a public key parameters object.
+ * @param privateParam the corresponding private key parameters.
+ * @deprecated use AsymmetricKeyParameter
+ */
+ public AsymmetricCipherKeyPair(
+ CipherParameters publicParam,
+ CipherParameters privateParam)
+ {
+ this.publicParam = (AsymmetricKeyParameter)publicParam;
+ this.privateParam = (AsymmetricKeyParameter)privateParam;
+ }
+
+ /**
+ * return the public key parameters.
+ *
+ * @return the public key parameters.
+ */
+ public AsymmetricKeyParameter getPublic()
+ {
+ return publicParam;
+ }
+
+ /**
+ * return the private key parameters.
+ *
+ * @return the private key parameters.
+ */
+ public AsymmetricKeyParameter getPrivate()
+ {
+ return privateParam;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/AsymmetricCipherKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/crypto/AsymmetricCipherKeyPairGenerator.java
new file mode 100644
index 00000000..919db199
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/AsymmetricCipherKeyPairGenerator.java
@@ -0,0 +1,22 @@
+package org.bouncycastle.crypto;
+
+/**
+ * interface that a public/private key pair generator should conform to.
+ */
+public interface AsymmetricCipherKeyPairGenerator
+{
+ /**
+ * intialise the key pair generator.
+ *
+ * @param param the parameters the key pair is to be initialised with.
+ */
+ public void init(KeyGenerationParameters param);
+
+ /**
+ * return an AsymmetricCipherKeyPair containing the generated keys.
+ *
+ * @return an AsymmetricCipherKeyPair containing the generated keys.
+ */
+ public AsymmetricCipherKeyPair generateKeyPair();
+}
+
diff --git a/core/src/main/java/org/bouncycastle/crypto/BasicAgreement.java b/core/src/main/java/org/bouncycastle/crypto/BasicAgreement.java
new file mode 100644
index 00000000..8e5ff0da
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/BasicAgreement.java
@@ -0,0 +1,26 @@
+package org.bouncycastle.crypto;
+
+import java.math.BigInteger;
+
+/**
+ * The basic interface that basic Diffie-Hellman implementations
+ * conforms to.
+ */
+public interface BasicAgreement
+{
+ /**
+ * initialise the agreement engine.
+ */
+ void init(CipherParameters param);
+
+ /**
+ * return the field size for the agreement algorithm in bytes.
+ */
+ int getFieldSize();
+
+ /**
+ * given a public key from a given party calculate the next
+ * message in the agreement sequence.
+ */
+ BigInteger calculateAgreement(CipherParameters pubKey);
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/BlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/BlockCipher.java
new file mode 100644
index 00000000..3cfa25a0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/BlockCipher.java
@@ -0,0 +1,56 @@
+package org.bouncycastle.crypto;
+
+
+/**
+ * Block cipher engines are expected to conform to this interface.
+ */
+public interface BlockCipher
+{
+ /**
+ * Initialise the cipher.
+ *
+ * @param forEncryption if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param params the key and other data required by the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException;
+
+ /**
+ * Return the name of the algorithm the cipher implements.
+ *
+ * @return the name of the algorithm the cipher implements.
+ */
+ public String getAlgorithmName();
+
+ /**
+ * Return the block size for this cipher (in bytes).
+ *
+ * @return the block size for this cipher in bytes.
+ */
+ public int getBlockSize();
+
+ /**
+ * Process one block of input from the array in and write it to
+ * the out array.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+ throws DataLengthException, IllegalStateException;
+
+ /**
+ * Reset the cipher. After resetting the cipher is in the same state
+ * as it was after the last init (if there was one).
+ */
+ public void reset();
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/BufferedAsymmetricBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/BufferedAsymmetricBlockCipher.java
new file mode 100644
index 00000000..1bf7ce3e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/BufferedAsymmetricBlockCipher.java
@@ -0,0 +1,171 @@
+package org.bouncycastle.crypto;
+
+/**
+ * a buffer wrapper for an asymmetric block cipher, allowing input
+ * to be accumulated in a piecemeal fashion until final processing.
+ */
+public class BufferedAsymmetricBlockCipher
+{
+ protected byte[] buf;
+ protected int bufOff;
+
+ private final AsymmetricBlockCipher cipher;
+
+ /**
+ * base constructor.
+ *
+ * @param cipher the cipher this buffering object wraps.
+ */
+ public BufferedAsymmetricBlockCipher(
+ AsymmetricBlockCipher cipher)
+ {
+ this.cipher = cipher;
+ }
+
+ /**
+ * return the underlying cipher for the buffer.
+ *
+ * @return the underlying cipher for the buffer.
+ */
+ public AsymmetricBlockCipher getUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+ /**
+ * return the amount of data sitting in the buffer.
+ *
+ * @return the amount of data sitting in the buffer.
+ */
+ public int getBufferPosition()
+ {
+ return bufOff;
+ }
+
+ /**
+ * initialise the buffer and the underlying cipher.
+ *
+ * @param forEncryption if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param params the key and other data required by the cipher.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ reset();
+
+ cipher.init(forEncryption, params);
+
+ //
+ // we allow for an extra byte where people are using their own padding
+ // mechanisms on a raw cipher.
+ //
+ buf = new byte[cipher.getInputBlockSize() + (forEncryption ? 1 : 0)];
+ bufOff = 0;
+ }
+
+ /**
+ * returns the largest size an input block can be.
+ *
+ * @return maximum size for an input block.
+ */
+ public int getInputBlockSize()
+ {
+ return cipher.getInputBlockSize();
+ }
+
+ /**
+ * returns the maximum size of the block produced by this cipher.
+ *
+ * @return maximum size of the output block produced by the cipher.
+ */
+ public int getOutputBlockSize()
+ {
+ return cipher.getOutputBlockSize();
+ }
+
+ /**
+ * add another byte for processing.
+ *
+ * @param in the input byte.
+ */
+ public void processByte(
+ byte in)
+ {
+ if (bufOff >= buf.length)
+ {
+ throw new DataLengthException("attempt to process message too long for cipher");
+ }
+
+ buf[bufOff++] = in;
+ }
+
+ /**
+ * add len bytes to the buffer for processing.
+ *
+ * @param in the input data
+ * @param inOff offset into the in array where the data starts
+ * @param len the length of the block to be processed.
+ */
+ public void processBytes(
+ byte[] in,
+ int inOff,
+ int len)
+ {
+ if (len == 0)
+ {
+ return;
+ }
+
+ if (len < 0)
+ {
+ throw new IllegalArgumentException("Can't have a negative input length!");
+ }
+
+ if (bufOff + len > buf.length)
+ {
+ throw new DataLengthException("attempt to process message too long for cipher");
+ }
+
+ System.arraycopy(in, inOff, buf, bufOff, len);
+ bufOff += len;
+ }
+
+ /**
+ * process the contents of the buffer using the underlying
+ * cipher.
+ *
+ * @return the result of the encryption/decryption process on the
+ * buffer.
+ * @exception InvalidCipherTextException if we are given a garbage block.
+ */
+ public byte[] doFinal()
+ throws InvalidCipherTextException
+ {
+ byte[] out = cipher.processBlock(buf, 0, bufOff);
+
+ reset();
+
+ return out;
+ }
+
+ /**
+ * Reset the buffer and the underlying cipher.
+ */
+ public void reset()
+ {
+ /*
+ * clean the buffer.
+ */
+ if (buf != null)
+ {
+ for (int i = 0; i < buf.length; i++)
+ {
+ buf[i] = 0;
+ }
+ }
+
+ bufOff = 0;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java
new file mode 100644
index 00000000..bdb694d4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java
@@ -0,0 +1,313 @@
+package org.bouncycastle.crypto;
+
+
+/**
+ * A wrapper class that allows block ciphers to be used to process data in
+ * a piecemeal fashion. The BufferedBlockCipher outputs a block only when the
+ * buffer is full and more data is being added, or on a doFinal.
+ * <p>
+ * Note: in the case where the underlying cipher is either a CFB cipher or an
+ * OFB one the last block may not be a multiple of the block size.
+ */
+public class BufferedBlockCipher
+{
+ protected byte[] buf;
+ protected int bufOff;
+
+ protected boolean forEncryption;
+ protected BlockCipher cipher;
+
+ protected boolean partialBlockOkay;
+ protected boolean pgpCFB;
+
+ /**
+ * constructor for subclasses
+ */
+ protected BufferedBlockCipher()
+ {
+ }
+
+ /**
+ * Create a buffered block cipher without padding.
+ *
+ * @param cipher the underlying block cipher this buffering object wraps.
+ */
+ public BufferedBlockCipher(
+ BlockCipher cipher)
+ {
+ this.cipher = cipher;
+
+ buf = new byte[cipher.getBlockSize()];
+ bufOff = 0;
+
+ //
+ // check if we can handle partial blocks on doFinal.
+ //
+ String name = cipher.getAlgorithmName();
+ int idx = name.indexOf('/') + 1;
+
+ pgpCFB = (idx > 0 && name.startsWith("PGP", idx));
+
+ if (pgpCFB)
+ {
+ partialBlockOkay = true;
+ }
+ else
+ {
+ partialBlockOkay = (idx > 0 && (name.startsWith("CFB", idx) || name.startsWith("OFB", idx) || name.startsWith("OpenPGP", idx) || name.startsWith("SIC", idx) || name.startsWith("GCTR", idx)));
+ }
+ }
+
+ /**
+ * return the cipher this object wraps.
+ *
+ * @return the cipher this object wraps.
+ */
+ public BlockCipher getUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+ /**
+ * initialise the cipher.
+ *
+ * @param forEncryption if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param params the key and other data required by the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ throws IllegalArgumentException
+ {
+ this.forEncryption = forEncryption;
+
+ reset();
+
+ cipher.init(forEncryption, params);
+ }
+
+ /**
+ * return the blocksize for the underlying cipher.
+ *
+ * @return the blocksize for the underlying cipher.
+ */
+ public int getBlockSize()
+ {
+ return cipher.getBlockSize();
+ }
+
+ /**
+ * return the size of the output buffer required for an update
+ * an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to update
+ * with len bytes of input.
+ */
+ public int getUpdateOutputSize(
+ int len)
+ {
+ int total = len + bufOff;
+ int leftOver;
+
+ if (pgpCFB)
+ {
+ leftOver = total % buf.length - (cipher.getBlockSize() + 2);
+ }
+ else
+ {
+ leftOver = total % buf.length;
+ }
+
+ return total - leftOver;
+ }
+
+ /**
+ * return the size of the output buffer required for an update plus a
+ * doFinal with an input of 'length' bytes.
+ *
+ * @param length the length of the input.
+ * @return the space required to accommodate a call to update and doFinal
+ * with 'length' bytes of input.
+ */
+ public int getOutputSize(
+ int length)
+ {
+ // Note: Can assume partialBlockOkay is true for purposes of this calculation
+ return length + bufOff;
+ }
+
+ /**
+ * process a single byte, producing an output block if neccessary.
+ *
+ * @param in the input byte.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ */
+ public int processByte(
+ byte in,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ int resultLen = 0;
+
+ buf[bufOff++] = in;
+
+ if (bufOff == buf.length)
+ {
+ resultLen = cipher.processBlock(buf, 0, out, outOff);
+ bufOff = 0;
+ }
+
+ return resultLen;
+ }
+
+ /**
+ * process an array of bytes, producing output if necessary.
+ *
+ * @param in the input byte array.
+ * @param inOff the offset at which the input data starts.
+ * @param len the number of bytes to be copied out of the input array.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ */
+ public int processBytes(
+ byte[] in,
+ int inOff,
+ int len,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if (len < 0)
+ {
+ throw new IllegalArgumentException("Can't have a negative input length!");
+ }
+
+ int blockSize = getBlockSize();
+ int length = getUpdateOutputSize(len);
+
+ if (length > 0)
+ {
+ if ((outOff + length) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+ }
+
+ int resultLen = 0;
+ int gapLen = buf.length - bufOff;
+
+ if (len > gapLen)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+ resultLen += cipher.processBlock(buf, 0, out, outOff);
+
+ bufOff = 0;
+ len -= gapLen;
+ inOff += gapLen;
+
+ while (len > buf.length)
+ {
+ resultLen += cipher.processBlock(in, inOff, out, outOff + resultLen);
+
+ len -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ System.arraycopy(in, inOff, buf, bufOff, len);
+
+ bufOff += len;
+
+ if (bufOff == buf.length)
+ {
+ resultLen += cipher.processBlock(buf, 0, out, outOff + resultLen);
+ bufOff = 0;
+ }
+
+ return resultLen;
+ }
+
+ /**
+ * Process the last block in the buffer.
+ *
+ * @param out the array the block currently being held is copied into.
+ * @param outOff the offset at which the copying starts.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there is insufficient space in out for
+ * the output, or the input is not block size aligned and should be.
+ * @exception IllegalStateException if the underlying cipher is not
+ * initialised.
+ * @exception InvalidCipherTextException if padding is expected and not found.
+ * @exception DataLengthException if the input is not block size
+ * aligned.
+ */
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException, InvalidCipherTextException
+ {
+ try
+ {
+ int resultLen = 0;
+
+ if (outOff + bufOff > out.length)
+ {
+ throw new OutputLengthException("output buffer too short for doFinal()");
+ }
+
+ if (bufOff != 0)
+ {
+ if (!partialBlockOkay)
+ {
+ throw new DataLengthException("data not block size aligned");
+ }
+
+ cipher.processBlock(buf, 0, buf, 0);
+ resultLen = bufOff;
+ bufOff = 0;
+ System.arraycopy(buf, 0, out, outOff, resultLen);
+ }
+
+ return resultLen;
+ }
+ finally
+ {
+ reset();
+ }
+ }
+
+ /**
+ * Reset the buffer and cipher. After resetting the object is in the same
+ * state as it was after the last init (if there was one).
+ */
+ public void reset()
+ {
+ //
+ // clean the buffer.
+ //
+ for (int i = 0; i < buf.length; i++)
+ {
+ buf[i] = 0;
+ }
+
+ bufOff = 0;
+
+ //
+ // reset the underlying cipher.
+ //
+ cipher.reset();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/CipherKeyGenerator.java b/core/src/main/java/org/bouncycastle/crypto/CipherKeyGenerator.java
new file mode 100644
index 00000000..451f8e8f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/CipherKeyGenerator.java
@@ -0,0 +1,38 @@
+package org.bouncycastle.crypto;
+
+import java.security.SecureRandom;
+
+/**
+ * The base class for symmetric, or secret, cipher key generators.
+ */
+public class CipherKeyGenerator
+{
+ protected SecureRandom random;
+ protected int strength;
+
+ /**
+ * initialise the key generator.
+ *
+ * @param param the parameters to be used for key generation
+ */
+ public void init(
+ KeyGenerationParameters param)
+ {
+ this.random = param.getRandom();
+ this.strength = (param.getStrength() + 7) / 8;
+ }
+
+ /**
+ * generate a secret key.
+ *
+ * @return a byte array containing the key value.
+ */
+ public byte[] generateKey()
+ {
+ byte[] key = new byte[strength];
+
+ random.nextBytes(key);
+
+ return key;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/CipherParameters.java b/core/src/main/java/org/bouncycastle/crypto/CipherParameters.java
new file mode 100644
index 00000000..5be87304
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/CipherParameters.java
@@ -0,0 +1,8 @@
+package org.bouncycastle.crypto;
+
+/**
+ * all parameter classes implement this.
+ */
+public interface CipherParameters
+{
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/Commitment.java b/core/src/main/java/org/bouncycastle/crypto/Commitment.java
new file mode 100644
index 00000000..f1dc05a3
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/Commitment.java
@@ -0,0 +1,42 @@
+package org.bouncycastle.crypto;
+
+/**
+ * General holding class for a commitment.
+ */
+public class Commitment
+{
+ private final byte[] secret;
+ private final byte[] commitment;
+
+ /**
+ * Base constructor.
+ *
+ * @param secret an encoding of the secret required to reveal the commitment.
+ * @param commitment an encoding of the sealed commitment.
+ */
+ public Commitment(byte[] secret, byte[] commitment)
+ {
+ this.secret = secret;
+ this.commitment = commitment;
+ }
+
+ /**
+ * The secret required to reveal the commitment.
+ *
+ * @return an encoding of the secret associated with the commitment.
+ */
+ public byte[] getSecret()
+ {
+ return secret;
+ }
+
+ /**
+ * The sealed commitment.
+ *
+ * @return an encoding of the sealed commitment.
+ */
+ public byte[] getCommitment()
+ {
+ return commitment;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/Committer.java b/core/src/main/java/org/bouncycastle/crypto/Committer.java
new file mode 100644
index 00000000..5c93e5d1
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/Committer.java
@@ -0,0 +1,24 @@
+package org.bouncycastle.crypto;
+
+/**
+ * General interface fdr classes that produce and validate commitments.
+ */
+public interface Committer
+{
+ /**
+ * Generate a commitment for the passed in message.
+ *
+ * @param message the message to be committed to,
+ * @return a Commitment
+ */
+ Commitment commit(byte[] message);
+
+ /**
+ * Return true if the passed in commitment represents a commitment to the passed in maessage.
+ *
+ * @param commitment a commitment previously generated.
+ * @param message the message that was expected to have been committed to.
+ * @return true if commitment matches message, false otherwise.
+ */
+ boolean isRevealed(Commitment commitment, byte[] message);
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/CryptoException.java b/core/src/main/java/org/bouncycastle/crypto/CryptoException.java
new file mode 100644
index 00000000..352c5569
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/CryptoException.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.crypto;
+
+/**
+ * the foundation class for the hard exceptions thrown by the crypto packages.
+ */
+public class CryptoException
+ extends Exception
+{
+ private Throwable cause;
+
+ /**
+ * base constructor.
+ */
+ public CryptoException()
+ {
+ }
+
+ /**
+ * create a CryptoException with the given message.
+ *
+ * @param message the message to be carried with the exception.
+ */
+ public CryptoException(
+ String message)
+ {
+ super(message);
+ }
+
+ /**
+ * Create a CryptoException with the given message and underlying cause.
+ *
+ * @param message message describing exception.
+ * @param cause the throwable that was the underlying cause.
+ */
+ public CryptoException(
+ String message,
+ Throwable cause)
+ {
+ super(message);
+
+ this.cause = cause;
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/DSA.java b/core/src/main/java/org/bouncycastle/crypto/DSA.java
new file mode 100644
index 00000000..1f584762
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/DSA.java
@@ -0,0 +1,36 @@
+package org.bouncycastle.crypto;
+
+import java.math.BigInteger;
+
+/**
+ * interface for classes implementing algorithms modeled similar to the Digital Signature Alorithm.
+ */
+public interface DSA
+{
+ /**
+ * initialise the signer for signature generation or signature
+ * verification.
+ *
+ * @param forSigning true if we are generating a signature, false
+ * otherwise.
+ * @param param key parameters for signature generation.
+ */
+ public void init(boolean forSigning, CipherParameters param);
+
+ /**
+ * sign the passed in message (usually the output of a hash function).
+ *
+ * @param message the message to be signed.
+ * @return two big integers representing the r and s values respectively.
+ */
+ public BigInteger[] generateSignature(byte[] message);
+
+ /**
+ * verify the message message against the signature values r and s.
+ *
+ * @param message the message that was supposed to have been signed.
+ * @param r the r signature value.
+ * @param s the s signature value.
+ */
+ public boolean verifySignature(byte[] message, BigInteger r, BigInteger s);
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/DataLengthException.java b/core/src/main/java/org/bouncycastle/crypto/DataLengthException.java
new file mode 100644
index 00000000..fbf047cf
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/DataLengthException.java
@@ -0,0 +1,29 @@
+package org.bouncycastle.crypto;
+
+/**
+ * this exception is thrown if a buffer that is meant to have output
+ * copied into it turns out to be too short, or if we've been given
+ * insufficient input. In general this exception will get thrown rather
+ * than an ArrayOutOfBounds exception.
+ */
+public class DataLengthException
+ extends RuntimeCryptoException
+{
+ /**
+ * base constructor.
+ */
+ public DataLengthException()
+ {
+ }
+
+ /**
+ * create a DataLengthException with the given message.
+ *
+ * @param message the message to be carried with the exception.
+ */
+ public DataLengthException(
+ String message)
+ {
+ super(message);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/DerivationFunction.java b/core/src/main/java/org/bouncycastle/crypto/DerivationFunction.java
new file mode 100644
index 00000000..ef6e29eb
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/DerivationFunction.java
@@ -0,0 +1,17 @@
+package org.bouncycastle.crypto;
+
+/**
+ * base interface for general purpose byte derivation functions.
+ */
+public interface DerivationFunction
+{
+ public void init(DerivationParameters param);
+
+ /**
+ * return the message digest used as the basis for the function
+ */
+ public Digest getDigest();
+
+ public int generateBytes(byte[] out, int outOff, int len)
+ throws DataLengthException, IllegalArgumentException;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/DerivationParameters.java b/core/src/main/java/org/bouncycastle/crypto/DerivationParameters.java
new file mode 100644
index 00000000..e11eb867
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/DerivationParameters.java
@@ -0,0 +1,8 @@
+package org.bouncycastle.crypto;
+
+/**
+ * Parameters for key/byte stream derivation classes
+ */
+public interface DerivationParameters
+{
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/Digest.java b/core/src/main/java/org/bouncycastle/crypto/Digest.java
new file mode 100644
index 00000000..f44fad0d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/Digest.java
@@ -0,0 +1,51 @@
+package org.bouncycastle.crypto;
+
+/**
+ * interface that a message digest conforms to.
+ */
+public interface Digest
+{
+ /**
+ * return the algorithm name
+ *
+ * @return the algorithm name
+ */
+ public String getAlgorithmName();
+
+ /**
+ * return the size, in bytes, of the digest produced by this message digest.
+ *
+ * @return the size, in bytes, of the digest produced by this message digest.
+ */
+ public int getDigestSize();
+
+ /**
+ * update the message digest with a single byte.
+ *
+ * @param in the input byte to be entered.
+ */
+ public void update(byte in);
+
+ /**
+ * update the message digest with a block of bytes.
+ *
+ * @param in the byte array containing the data.
+ * @param inOff the offset into the byte array where the data starts.
+ * @param len the length of the data.
+ */
+ public void update(byte[] in, int inOff, int len);
+
+ /**
+ * close the digest, producing the final digest value. The doFinal
+ * call leaves the digest reset.
+ *
+ * @param out the array the digest is to be copied into.
+ * @param outOff the offset into the out array the digest is to start at.
+ */
+ public int doFinal(byte[] out, int outOff);
+
+ /**
+ * reset the digest back to it's initial state.
+ */
+ public void reset();
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/EphemeralKeyPair.java b/core/src/main/java/org/bouncycastle/crypto/EphemeralKeyPair.java
new file mode 100644
index 00000000..f16812f9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/EphemeralKeyPair.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.crypto;
+
+public class EphemeralKeyPair
+{
+ private AsymmetricCipherKeyPair keyPair;
+ private KeyEncoder publicKeyEncoder;
+
+ public EphemeralKeyPair(AsymmetricCipherKeyPair keyPair, KeyEncoder publicKeyEncoder)
+ {
+ this.keyPair = keyPair;
+ this.publicKeyEncoder = publicKeyEncoder;
+ }
+
+ public AsymmetricCipherKeyPair getKeyPair()
+ {
+ return keyPair;
+ }
+
+ public byte[] getEncodedPublicKey()
+ {
+ return publicKeyEncoder.getEncoded(keyPair.getPublic());
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/ExtendedDigest.java b/core/src/main/java/org/bouncycastle/crypto/ExtendedDigest.java
new file mode 100644
index 00000000..c5e9e8b0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/ExtendedDigest.java
@@ -0,0 +1,13 @@
+package org.bouncycastle.crypto;
+
+public interface ExtendedDigest
+ extends Digest
+{
+ /**
+ * Return the size in bytes of the internal buffer the digest applies it's compression
+ * function to.
+ *
+ * @return byte length of the digests internal buffer.
+ */
+ public int getByteLength();
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/InvalidCipherTextException.java b/core/src/main/java/org/bouncycastle/crypto/InvalidCipherTextException.java
new file mode 100644
index 00000000..21c150d9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/InvalidCipherTextException.java
@@ -0,0 +1,40 @@
+package org.bouncycastle.crypto;
+
+/**
+ * this exception is thrown whenever we find something we don't expect in a
+ * message.
+ */
+public class InvalidCipherTextException
+ extends CryptoException
+{
+ /**
+ * base constructor.
+ */
+ public InvalidCipherTextException()
+ {
+ }
+
+ /**
+ * create a InvalidCipherTextException with the given message.
+ *
+ * @param message the message to be carried with the exception.
+ */
+ public InvalidCipherTextException(
+ String message)
+ {
+ super(message);
+ }
+
+ /**
+ * create a InvalidCipherTextException with the given message.
+ *
+ * @param message the message to be carried with the exception.
+ * @param cause the root cause of the exception.
+ */
+ public InvalidCipherTextException(
+ String message,
+ Throwable cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/KeyEncapsulation.java b/core/src/main/java/org/bouncycastle/crypto/KeyEncapsulation.java
new file mode 100755
index 00000000..16744573
--- /dev/null
+++ b/core/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/core/src/main/java/org/bouncycastle/crypto/KeyEncoder.java b/core/src/main/java/org/bouncycastle/crypto/KeyEncoder.java
new file mode 100644
index 00000000..92ded9cd
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/KeyEncoder.java
@@ -0,0 +1,8 @@
+package org.bouncycastle.crypto;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+
+public interface KeyEncoder
+{
+ byte[] getEncoded(AsymmetricKeyParameter keyParameter);
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/KeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/crypto/KeyGenerationParameters.java
new file mode 100644
index 00000000..9a63522f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/KeyGenerationParameters.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.crypto;
+
+import java.security.SecureRandom;
+
+/**
+ * The base class for parameters to key generators.
+ */
+public class KeyGenerationParameters
+{
+ private SecureRandom random;
+ private int strength;
+
+ /**
+ * initialise the generator with a source of randomness
+ * and a strength (in bits).
+ *
+ * @param random the random byte source.
+ * @param strength the size, in bits, of the keys we want to produce.
+ */
+ public KeyGenerationParameters(
+ SecureRandom random,
+ int strength)
+ {
+ this.random = random;
+ this.strength = strength;
+ }
+
+ /**
+ * return the random source associated with this
+ * generator.
+ *
+ * @return the generators random source.
+ */
+ public SecureRandom getRandom()
+ {
+ return random;
+ }
+
+ /**
+ * return the bit strength for keys produced by this generator,
+ *
+ * @return the strength of the keys this generator produces (in bits).
+ */
+ public int getStrength()
+ {
+ return strength;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/KeyParser.java b/core/src/main/java/org/bouncycastle/crypto/KeyParser.java
new file mode 100644
index 00000000..60ce29de
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/KeyParser.java
@@ -0,0 +1,12 @@
+package org.bouncycastle.crypto;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+
+public interface KeyParser
+{
+ AsymmetricKeyParameter readKey(InputStream stream)
+ throws IOException;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/Mac.java b/core/src/main/java/org/bouncycastle/crypto/Mac.java
new file mode 100644
index 00000000..c00cd58c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/Mac.java
@@ -0,0 +1,71 @@
+package org.bouncycastle.crypto;
+
+
+/**
+ * The base interface for implementations of message authentication codes (MACs).
+ */
+public interface Mac
+{
+ /**
+ * Initialise the MAC.
+ *
+ * @param params the key and other data required by the MAC.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(CipherParameters params)
+ throws IllegalArgumentException;
+
+ /**
+ * Return the name of the algorithm the MAC implements.
+ *
+ * @return the name of the algorithm the MAC implements.
+ */
+ public String getAlgorithmName();
+
+ /**
+ * Return the block size for this MAC (in bytes).
+ *
+ * @return the block size for this MAC in bytes.
+ */
+ public int getMacSize();
+
+ /**
+ * add a single byte to the mac for processing.
+ *
+ * @param in the byte to be processed.
+ * @exception IllegalStateException if the MAC is not initialised.
+ */
+ public void update(byte in)
+ throws IllegalStateException;
+
+ /**
+ * @param in the array containing the input.
+ * @param inOff the index in the array the data begins at.
+ * @param len the length of the input starting at inOff.
+ * @exception IllegalStateException if the MAC is not initialised.
+ * @exception DataLengthException if there isn't enough data in in.
+ */
+ public void update(byte[] in, int inOff, int len)
+ throws DataLengthException, IllegalStateException;
+
+ /**
+ * Compute the final stage of the MAC writing the output to the out
+ * parameter.
+ * <p>
+ * doFinal leaves the MAC in the same state it was after the last init.
+ *
+ * @param out the array the MAC is to be output to.
+ * @param outOff the offset into the out buffer the output is to start at.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception IllegalStateException if the MAC is not initialised.
+ */
+ public int doFinal(byte[] out, int outOff)
+ throws DataLengthException, IllegalStateException;
+
+ /**
+ * Reset the MAC. At the end of resetting the MAC should be in the
+ * in the same state it was after the last init (if there was one).
+ */
+ public void reset();
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/MaxBytesExceededException.java b/core/src/main/java/org/bouncycastle/crypto/MaxBytesExceededException.java
new file mode 100644
index 00000000..bfa15442
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/MaxBytesExceededException.java
@@ -0,0 +1,27 @@
+package org.bouncycastle.crypto;
+
+/**
+ * this exception is thrown whenever a cipher requires a change of key, iv
+ * or similar after x amount of bytes enciphered
+ */
+public class MaxBytesExceededException
+ extends RuntimeCryptoException
+{
+ /**
+ * base constructor.
+ */
+ public MaxBytesExceededException()
+ {
+ }
+
+ /**
+ * create an with the given message.
+ *
+ * @param message the message to be carried with the exception.
+ */
+ public MaxBytesExceededException(
+ String message)
+ {
+ super(message);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/OutputLengthException.java b/core/src/main/java/org/bouncycastle/crypto/OutputLengthException.java
new file mode 100644
index 00000000..62811a2b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/OutputLengthException.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.crypto;
+
+public class OutputLengthException
+ extends DataLengthException
+{
+ public OutputLengthException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/PBEParametersGenerator.java b/core/src/main/java/org/bouncycastle/crypto/PBEParametersGenerator.java
new file mode 100644
index 00000000..18cc648a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/PBEParametersGenerator.java
@@ -0,0 +1,171 @@
+package org.bouncycastle.crypto;
+
+import org.bouncycastle.util.Strings;
+
+/**
+ * super class for all Password Based Encryption (PBE) parameter generator classes.
+ */
+public abstract class PBEParametersGenerator
+{
+ protected byte[] password;
+ protected byte[] salt;
+ protected int iterationCount;
+
+ /**
+ * base constructor.
+ */
+ protected PBEParametersGenerator()
+ {
+ }
+
+ /**
+ * initialise the PBE generator.
+ *
+ * @param password the password converted into bytes (see below).
+ * @param salt the salt to be mixed with the password.
+ * @param iterationCount the number of iterations the "mixing" function
+ * is to be applied for.
+ */
+ public void init(
+ byte[] password,
+ byte[] salt,
+ int iterationCount)
+ {
+ this.password = password;
+ this.salt = salt;
+ this.iterationCount = iterationCount;
+ }
+
+ /**
+ * return the password byte array.
+ *
+ * @return the password byte array.
+ */
+ public byte[] getPassword()
+ {
+ return password;
+ }
+
+ /**
+ * return the salt byte array.
+ *
+ * @return the salt byte array.
+ */
+ public byte[] getSalt()
+ {
+ return salt;
+ }
+
+ /**
+ * return the iteration count.
+ *
+ * @return the iteration count.
+ */
+ public int getIterationCount()
+ {
+ return iterationCount;
+ }
+
+ /**
+ * generate derived parameters for a key of length keySize.
+ *
+ * @param keySize the length, in bits, of the key required.
+ * @return a parameters object representing a key.
+ */
+ public abstract CipherParameters generateDerivedParameters(int keySize);
+
+ /**
+ * generate derived parameters for a key of length keySize, and
+ * an initialisation vector (IV) of length ivSize.
+ *
+ * @param keySize the length, in bits, of the key required.
+ * @param ivSize the length, in bits, of the iv required.
+ * @return a parameters object representing a key and an IV.
+ */
+ public abstract CipherParameters generateDerivedParameters(int keySize, int ivSize);
+
+ /**
+ * generate derived parameters for a key of length keySize, specifically
+ * for use with a MAC.
+ *
+ * @param keySize the length, in bits, of the key required.
+ * @return a parameters object representing a key.
+ */
+ public abstract CipherParameters generateDerivedMacParameters(int keySize);
+
+ /**
+ * converts a password to a byte array according to the scheme in
+ * PKCS5 (ascii, no padding)
+ *
+ * @param password a character array representing the password.
+ * @return a byte array representing the password.
+ */
+ public static byte[] PKCS5PasswordToBytes(
+ char[] password)
+ {
+ if (password != null)
+ {
+ byte[] bytes = new byte[password.length];
+
+ for (int i = 0; i != bytes.length; i++)
+ {
+ bytes[i] = (byte)password[i];
+ }
+
+ return bytes;
+ }
+ else
+ {
+ return new byte[0];
+ }
+ }
+
+ /**
+ * converts a password to a byte array according to the scheme in
+ * PKCS5 (UTF-8, no padding)
+ *
+ * @param password a character array representing the password.
+ * @return a byte array representing the password.
+ */
+ public static byte[] PKCS5PasswordToUTF8Bytes(
+ char[] password)
+ {
+ if (password != null)
+ {
+ return Strings.toUTF8ByteArray(password);
+ }
+ else
+ {
+ return new byte[0];
+ }
+ }
+
+ /**
+ * converts a password to a byte array according to the scheme in
+ * PKCS12 (unicode, big endian, 2 zero pad bytes at the end).
+ *
+ * @param password a character array representing the password.
+ * @return a byte array representing the password.
+ */
+ public static byte[] PKCS12PasswordToBytes(
+ char[] password)
+ {
+ if (password != null && password.length > 0)
+ {
+ // +1 for extra 2 pad bytes.
+ byte[] bytes = new byte[(password.length + 1) * 2];
+
+ for (int i = 0; i != password.length; i ++)
+ {
+ bytes[i * 2] = (byte)(password[i] >>> 8);
+ bytes[i * 2 + 1] = (byte)password[i];
+ }
+
+ return bytes;
+ }
+ else
+ {
+ return new byte[0];
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/RuntimeCryptoException.java b/core/src/main/java/org/bouncycastle/crypto/RuntimeCryptoException.java
new file mode 100644
index 00000000..c1572020
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/RuntimeCryptoException.java
@@ -0,0 +1,26 @@
+package org.bouncycastle.crypto;
+
+/**
+ * the foundation class for the exceptions thrown by the crypto packages.
+ */
+public class RuntimeCryptoException
+ extends RuntimeException
+{
+ /**
+ * base constructor.
+ */
+ public RuntimeCryptoException()
+ {
+ }
+
+ /**
+ * create a RuntimeCryptoException with the given message.
+ *
+ * @param message the message to be carried with the exception.
+ */
+ public RuntimeCryptoException(
+ String message)
+ {
+ super(message);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/Signer.java b/core/src/main/java/org/bouncycastle/crypto/Signer.java
new file mode 100644
index 00000000..357b0da4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/Signer.java
@@ -0,0 +1,43 @@
+package org.bouncycastle.crypto;
+
+/**
+ * Generic signer interface for hash based and message recovery signers.
+ */
+public interface Signer
+{
+ /**
+ * Initialise the signer for signing or verification.
+ *
+ * @param forSigning true if for signing, false otherwise
+ * @param param necessary parameters.
+ */
+ public void init(boolean forSigning, CipherParameters param);
+
+ /**
+ * update the internal digest with the byte b
+ */
+ public void update(byte b);
+
+ /**
+ * update the internal digest with the byte array in
+ */
+ public void update(byte[] in, int off, int len);
+
+ /**
+ * generate a signature for the message we've been loaded with using
+ * the key we were initialised with.
+ */
+ public byte[] generateSignature()
+ throws CryptoException, DataLengthException;
+
+ /**
+ * return true if the internal state represents the signature described
+ * in the passed in array.
+ */
+ public boolean verifySignature(byte[] signature);
+
+ /**
+ * reset the internal state
+ */
+ public void reset();
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/SignerWithRecovery.java b/core/src/main/java/org/bouncycastle/crypto/SignerWithRecovery.java
new file mode 100644
index 00000000..452b367f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/SignerWithRecovery.java
@@ -0,0 +1,34 @@
+package org.bouncycastle.crypto;
+
+/**
+ * Signer with message recovery.
+ */
+public interface SignerWithRecovery
+ extends Signer
+{
+ /**
+ * Returns true if the signer has recovered the full message as
+ * part of signature verification.
+ *
+ * @return true if full message recovered.
+ */
+ public boolean hasFullMessage();
+
+ /**
+ * Returns a reference to what message was recovered (if any).
+ *
+ * @return full/partial message, null if nothing.
+ */
+ public byte[] getRecoveredMessage();
+
+ /**
+ * Perform an update with the recovered message before adding any other data. This must
+ * be the first update method called, and calling it will result in the signer assuming
+ * that further calls to update will include message content past what is recoverable.
+ *
+ * @param signature the signature that we are in the process of verifying.
+ * @throws IllegalStateException
+ */
+ public void updateWithRecoveredMessage(byte[] signature)
+ throws InvalidCipherTextException;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java
new file mode 100644
index 00000000..8fdd2324
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.crypto;
+
+/**
+ * a wrapper for block ciphers with a single byte block size, so that they
+ * can be treated like stream ciphers.
+ */
+public class StreamBlockCipher
+ implements StreamCipher
+{
+ private BlockCipher cipher;
+
+ private byte[] oneByte = new byte[1];
+
+ /**
+ * basic constructor.
+ *
+ * @param cipher the block cipher to be wrapped.
+ * @exception IllegalArgumentException if the cipher has a block size other than
+ * one.
+ */
+ public StreamBlockCipher(
+ BlockCipher cipher)
+ {
+ if (cipher.getBlockSize() != 1)
+ {
+ throw new IllegalArgumentException("block cipher block size != 1.");
+ }
+
+ this.cipher = cipher;
+ }
+
+ /**
+ * initialise the underlying cipher.
+ *
+ * @param forEncryption true if we are setting up for encryption, false otherwise.
+ * @param params the necessary parameters for the underlying cipher to be initialised.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ cipher.init(forEncryption, params);
+ }
+
+ /**
+ * return the name of the algorithm we are wrapping.
+ *
+ * @return the name of the algorithm we are wrapping.
+ */
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName();
+ }
+
+ /**
+ * encrypt/decrypt a single byte returning the result.
+ *
+ * @param in the byte to be processed.
+ * @return the result of processing the input byte.
+ */
+ public byte returnByte(
+ byte in)
+ {
+ oneByte[0] = in;
+
+ cipher.processBlock(oneByte, 0, oneByte, 0);
+
+ return oneByte[0];
+ }
+
+ /**
+ * process a block of bytes from in putting the result into out.
+ *
+ * @param in the input byte array.
+ * @param inOff the offset into the in array where the data to be processed starts.
+ * @param len the number of bytes to be processed.
+ * @param out the output buffer the processed bytes go into.
+ * @param outOff the offset into the output byte array the processed data stars at.
+ * @exception DataLengthException if the output buffer is too small.
+ */
+ public void processBytes(
+ byte[] in,
+ int inOff,
+ int len,
+ byte[] out,
+ int outOff)
+ throws DataLengthException
+ {
+ if (outOff + len > out.length)
+ {
+ throw new DataLengthException("output buffer too small in processBytes()");
+ }
+
+ for (int i = 0; i != len; i++)
+ {
+ cipher.processBlock(in, inOff + i, out, outOff + i);
+ }
+ }
+
+ /**
+ * reset the underlying cipher. This leaves it in the same state
+ * it was at after the last init (if there was one).
+ */
+ public void reset()
+ {
+ cipher.reset();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/StreamCipher.java b/core/src/main/java/org/bouncycastle/crypto/StreamCipher.java
new file mode 100644
index 00000000..2a55d4f6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/StreamCipher.java
@@ -0,0 +1,53 @@
+package org.bouncycastle.crypto;
+
+/**
+ * the interface stream ciphers conform to.
+ */
+public interface StreamCipher
+{
+ /**
+ * Initialise the cipher.
+ *
+ * @param forEncryption if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param params the key and other data required by the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException;
+
+ /**
+ * Return the name of the algorithm the cipher implements.
+ *
+ * @return the name of the algorithm the cipher implements.
+ */
+ public String getAlgorithmName();
+
+ /**
+ * encrypt/decrypt a single byte returning the result.
+ *
+ * @param in the byte to be processed.
+ * @return the result of processing the input byte.
+ */
+ public byte returnByte(byte in);
+
+ /**
+ * process a block of bytes from in putting the result into out.
+ *
+ * @param in the input byte array.
+ * @param inOff the offset into the in array where the data to be processed starts.
+ * @param len the number of bytes to be processed.
+ * @param out the output buffer the processed bytes go into.
+ * @param outOff the offset into the output byte array the processed data starts at.
+ * @exception DataLengthException if the output buffer is too small.
+ */
+ public void processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
+ throws DataLengthException;
+
+ /**
+ * reset the cipher. This leaves it in the same state
+ * it was at after the last init (if there was one).
+ */
+ public void reset();
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/Wrapper.java b/core/src/main/java/org/bouncycastle/crypto/Wrapper.java
new file mode 100644
index 00000000..3956a6fc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/Wrapper.java
@@ -0,0 +1,18 @@
+package org.bouncycastle.crypto;
+
+public interface Wrapper
+{
+ public void init(boolean forWrapping, CipherParameters param);
+
+ /**
+ * Return the name of the algorithm the wrapper implements.
+ *
+ * @return the name of the algorithm the wrapper implements.
+ */
+ public String getAlgorithmName();
+
+ public byte[] wrap(byte[] in, int inOff, int inLen);
+
+ public byte[] unwrap(byte[] in, int inOff, int inLen)
+ throws InvalidCipherTextException;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/DHAgreement.java b/core/src/main/java/org/bouncycastle/crypto/agreement/DHAgreement.java
new file mode 100644
index 00000000..021a7153
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/agreement/DHAgreement.java
@@ -0,0 +1,94 @@
+package org.bouncycastle.crypto.agreement;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.generators.DHKeyPairGenerator;
+import org.bouncycastle.crypto.params.DHKeyGenerationParameters;
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+
+/**
+ * a Diffie-Hellman key exchange engine.
+ * <p>
+ * note: This uses MTI/A0 key agreement in order to make the key agreement
+ * secure against passive attacks. If you're doing Diffie-Hellman and both
+ * parties have long term public keys you should look at using this. For
+ * further information have a look at RFC 2631.
+ * <p>
+ * It's possible to extend this to more than two parties as well, for the moment
+ * that is left as an exercise for the reader.
+ */
+public class DHAgreement
+{
+ private DHPrivateKeyParameters key;
+ private DHParameters dhParams;
+ private BigInteger privateValue;
+ private SecureRandom random;
+
+ public void init(
+ CipherParameters param)
+ {
+ AsymmetricKeyParameter kParam;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.random = rParam.getRandom();
+ kParam = (AsymmetricKeyParameter)rParam.getParameters();
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ kParam = (AsymmetricKeyParameter)param;
+ }
+
+
+ if (!(kParam instanceof DHPrivateKeyParameters))
+ {
+ throw new IllegalArgumentException("DHEngine expects DHPrivateKeyParameters");
+ }
+
+ this.key = (DHPrivateKeyParameters)kParam;
+ this.dhParams = key.getParameters();
+ }
+
+ /**
+ * calculate our initial message.
+ */
+ public BigInteger calculateMessage()
+ {
+ DHKeyPairGenerator dhGen = new DHKeyPairGenerator();
+ dhGen.init(new DHKeyGenerationParameters(random, dhParams));
+ AsymmetricCipherKeyPair dhPair = dhGen.generateKeyPair();
+
+ this.privateValue = ((DHPrivateKeyParameters)dhPair.getPrivate()).getX();
+
+ return ((DHPublicKeyParameters)dhPair.getPublic()).getY();
+ }
+
+ /**
+ * given a message from a given party and the corresponding public key,
+ * calculate the next message in the agreement sequence. In this case
+ * this will represent the shared secret.
+ */
+ public BigInteger calculateAgreement(
+ DHPublicKeyParameters pub,
+ BigInteger message)
+ {
+ if (!pub.getParameters().equals(dhParams))
+ {
+ throw new IllegalArgumentException("Diffie-Hellman public key has wrong parameters.");
+ }
+
+ BigInteger p = dhParams.getP();
+
+ return message.modPow(key.getX(), p).multiply(pub.getY().modPow(privateValue, p)).mod(p);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/DHBasicAgreement.java b/core/src/main/java/org/bouncycastle/crypto/agreement/DHBasicAgreement.java
new file mode 100644
index 00000000..d2e2a09f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/agreement/DHBasicAgreement.java
@@ -0,0 +1,71 @@
+package org.bouncycastle.crypto.agreement;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.BasicAgreement;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
+import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+
+/**
+ * a Diffie-Hellman key agreement class.
+ * <p>
+ * note: This is only the basic algorithm, it doesn't take advantage of
+ * long term public keys if they are available. See the DHAgreement class
+ * for a "better" implementation.
+ */
+public class DHBasicAgreement
+ implements BasicAgreement
+{
+ private DHPrivateKeyParameters key;
+ private DHParameters dhParams;
+
+ public void init(
+ CipherParameters param)
+ {
+ AsymmetricKeyParameter kParam;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+ kParam = (AsymmetricKeyParameter)rParam.getParameters();
+ }
+ else
+ {
+ kParam = (AsymmetricKeyParameter)param;
+ }
+
+ if (!(kParam instanceof DHPrivateKeyParameters))
+ {
+ throw new IllegalArgumentException("DHEngine expects DHPrivateKeyParameters");
+ }
+
+ this.key = (DHPrivateKeyParameters)kParam;
+ this.dhParams = key.getParameters();
+ }
+
+ public int getFieldSize()
+ {
+ return (key.getParameters().getP().bitLength() + 7) / 8;
+ }
+
+ /**
+ * given a short term public key from a given party calculate the next
+ * message in the agreement sequence.
+ */
+ public BigInteger calculateAgreement(
+ CipherParameters pubKey)
+ {
+ DHPublicKeyParameters pub = (DHPublicKeyParameters)pubKey;
+
+ if (!pub.getParameters().equals(dhParams))
+ {
+ throw new IllegalArgumentException("Diffie-Hellman public key has wrong parameters.");
+ }
+
+ return pub.getY().modPow(key.getX(), dhParams.getP());
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/DHStandardGroups.java b/core/src/main/java/org/bouncycastle/crypto/agreement/DHStandardGroups.java
new file mode 100644
index 00000000..638bcb13
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/agreement/DHStandardGroups.java
@@ -0,0 +1,206 @@
+package org.bouncycastle.crypto.agreement;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.util.encoders.Hex;
+
+/**
+ * Standard Diffie-Hellman groups from various IETF specifications.
+ */
+public class DHStandardGroups
+{
+
+ private static DHParameters fromPG(String hexP, String hexG)
+ {
+ BigInteger p = new BigInteger(1, Hex.decode(hexP));
+ BigInteger g = new BigInteger(1, Hex.decode(hexG));
+ return new DHParameters(p, g);
+ }
+
+ private static DHParameters fromPGQ(String hexP, String hexG, String hexQ)
+ {
+ BigInteger p = new BigInteger(1, Hex.decode(hexP));
+ BigInteger g = new BigInteger(1, Hex.decode(hexG));
+ BigInteger q = new BigInteger(1, Hex.decode(hexQ));
+ return new DHParameters(p, g, q);
+ }
+
+ /*
+ * RFC 2409
+ */
+ private static final String rfc2409_768_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF";
+ private static final String rfc2409_768_g = "02";
+ public static final DHParameters rfc2409_768 = fromPG(rfc2409_768_p, rfc2409_768_g);
+
+ private static final String rfc2409_1024_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381"
+ + "FFFFFFFFFFFFFFFF";
+ private static final String rfc2409_1024_g = "02";
+ public static final DHParameters rfc2409_1024 = fromPG(rfc2409_1024_p, rfc2409_1024_g);
+
+ /*
+ * RFC 3526
+ */
+ private static final String rfc3526_1536_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF";
+ private static final String rfc3526_1536_g = "02";
+ public static final DHParameters rfc3526_1536 = fromPG(rfc3526_1536_p, rfc3526_1536_g);
+
+ private static final String rfc3526_2048_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AACAA68FFFFFFFFFFFFFFFF";
+ private static final String rfc3526_2048_g = "02";
+ public static final DHParameters rfc3526_2048 = fromPG(rfc3526_2048_p, rfc3526_2048_g);
+
+ private static final String rfc3526_3072_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"
+ + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"
+ + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"
+ + "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF";
+ private static final String rfc3526_3072_g = "02";
+ public static final DHParameters rfc3526_3072 = fromPG(rfc3526_3072_p, rfc3526_3072_g);
+
+ private static final String rfc3526_4096_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"
+ + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"
+ + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"
+ + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA"
+ + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED"
+ + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199"
+ + "FFFFFFFFFFFFFFFF";
+ private static final String rfc3526_4096_g = "02";
+ public static final DHParameters rfc3526_4096 = fromPG(rfc3526_4096_p, rfc3526_4096_g);
+
+ private static final String rfc3526_6144_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+ + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+ + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+ + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+ + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+ + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+ + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
+ + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
+ + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
+ + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
+ + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
+ + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
+ + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406"
+ + "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918"
+ + "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151"
+ + "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03"
+ + "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F"
+ + "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
+ + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B"
+ + "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632"
+ + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" + "6DCC4024FFFFFFFFFFFFFFFF";
+ private static final String rfc3526_6144_g = "02";
+ public static final DHParameters rfc3526_6144 = fromPG(rfc3526_6144_p, rfc3526_6144_g);
+
+ private static final String rfc3526_8192_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"
+ + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"
+ + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"
+ + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA"
+ + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED"
+ + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
+ + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" + "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831"
+ + "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" + "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF"
+ + "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" + "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3"
+ + "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328"
+ + "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" + "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE"
+ + "12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" + "38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300"
+ + "741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" + "3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9"
+ + "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" + "4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A"
+ + "062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" + "4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1"
+ + "B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" + "4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47"
+ + "9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" + "60C980DD98EDD3DFFFFFFFFFFFFFFFFF";
+ private static final String rfc3526_8192_g = "02";
+ public static final DHParameters rfc3526_8192 = fromPG(rfc3526_8192_p, rfc3526_8192_g);
+
+ /*
+ * RFC 4306
+ */
+ public static final DHParameters rfc4306_768 = rfc2409_768;
+ public static final DHParameters rfc4306_1024 = rfc2409_1024;
+
+ /*
+ * RFC 5114
+ */
+ private static final String rfc5114_1024_160_p = "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C6"
+ + "9A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C0" + "13ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD70"
+ + "98488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0" + "A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708"
+ + "DF1FB2BC2E4A4371";
+ private static final String rfc5114_1024_160_g = "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507F"
+ + "D6406CFF14266D31266FEA1E5C41564B777E690F5504F213" + "160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1"
+ + "909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28A" + "D662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24"
+ + "855E6EEB22B3B2E5";
+ private static final String rfc5114_1024_160_q = "F518AA8781A8DF278ABA4E7D64B7CB9D49462353";
+ public static final DHParameters rfc5114_1024_160 = fromPGQ(rfc5114_1024_160_p, rfc5114_1024_160_g,
+ rfc5114_1024_160_q);
+
+ private static final String rfc5114_2048_224_p = "AD107E1E9123A9D0D660FAA79559C51FA20D64E5683B9FD1"
+ + "B54B1597B61D0A75E6FA141DF95A56DBAF9A3C407BA1DF15" + "EB3D688A309C180E1DE6B85A1274A0A66D3F8152AD6AC212"
+ + "9037C9EDEFDA4DF8D91E8FEF55B7394B7AD5B7D0B6C12207" + "C9F98D11ED34DBF6C6BA0B2C8BBC27BE6A00E0A0B9C49708"
+ + "B3BF8A317091883681286130BC8985DB1602E714415D9330" + "278273C7DE31EFDC7310F7121FD5A07415987D9ADC0A486D"
+ + "CDF93ACC44328387315D75E198C641A480CD86A1B9E587E8" + "BE60E69CC928B2B9C52172E413042E9B23F10B0E16E79763"
+ + "C9B53DCF4BA80A29E3FB73C16B8E75B97EF363E2FFA31F71" + "CF9DE5384E71B81C0AC4DFFE0C10E64F";
+ private static final String rfc5114_2048_224_g = "AC4032EF4F2D9AE39DF30B5C8FFDAC506CDEBE7B89998CAF"
+ + "74866A08CFE4FFE3A6824A4E10B9A6F0DD921F01A70C4AFA" + "AB739D7700C29F52C57DB17C620A8652BE5E9001A8D66AD7"
+ + "C17669101999024AF4D027275AC1348BB8A762D0521BC98A" + "E247150422EA1ED409939D54DA7460CDB5F6C6B250717CBE"
+ + "F180EB34118E98D119529A45D6F834566E3025E316A330EF" + "BB77A86F0C1AB15B051AE3D428C8F8ACB70A8137150B8EEB"
+ + "10E183EDD19963DDD9E263E4770589EF6AA21E7F5F2FF381" + "B539CCE3409D13CD566AFBB48D6C019181E1BCFE94B30269"
+ + "EDFE72FE9B6AA4BD7B5A0F1C71CFFF4C19C418E1F6EC0179" + "81BC087F2A7065B384B890D3191F2BFA";
+ private static final String rfc5114_2048_224_q = "801C0D34C58D93FE997177101F80535A4738CEBCBF389A99B36371EB";
+ public static final DHParameters rfc5114_2048_224 = fromPGQ(rfc5114_2048_224_p, rfc5114_2048_224_g,
+ rfc5114_2048_224_q);
+
+ private static final String rfc5114_2048_256_p = "87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F2"
+ + "5D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA30" + "16C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD"
+ + "5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B" + "6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C"
+ + "4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0E" + "F13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D9"
+ + "67E144E5140564251CCACB83E6B486F6B3CA3F7971506026" + "C0B857F689962856DED4010ABD0BE621C3A3960A54E710C3"
+ + "75F26375D7014103A4B54330C198AF126116D2276E11715F" + "693877FAD7EF09CADB094AE91E1A1597";
+ private static final String rfc5114_2048_256_g = "3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF2054"
+ + "07F4793A1A0BA12510DBC15077BE463FFF4FED4AAC0BB555" + "BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18"
+ + "A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B" + "777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC83"
+ + "1D14348F6F2F9193B5045AF2767164E1DFC967C1FB3F2E55" + "A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14"
+ + "C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915" + "B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6"
+ + "184B523D1DB246C32F63078490F00EF8D647D148D4795451" + "5E2327CFEF98C582664B4C0F6CC41659";
+ private static final String rfc5114_2048_256_q = "8CF83642A709A097B447997640129DA299B1A47D1EB3750B"
+ + "A308B0FE64F5FBD3";
+ public static final DHParameters rfc5114_2048_256 = fromPGQ(rfc5114_2048_256_p, rfc5114_2048_256_g,
+ rfc5114_2048_256_q);
+
+ /*
+ * RFC 5996
+ */
+ public static final DHParameters rfc5996_768 = rfc4306_768;
+ public static final DHParameters rfc5996_1024 = rfc4306_1024;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java b/core/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
new file mode 100644
index 00000000..59944e07
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
@@ -0,0 +1,51 @@
+package org.bouncycastle.crypto.agreement;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.BasicAgreement;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.math.ec.ECPoint;
+
+/**
+ * P1363 7.2.1 ECSVDP-DH
+ *
+ * ECSVDP-DH is Elliptic Curve Secret Value Derivation Primitive,
+ * Diffie-Hellman version. It is based on the work of [DH76], [Mil86],
+ * and [Kob87]. This primitive derives a shared secret value from one
+ * party's private key and another party's public key, where both have
+ * the same set of EC domain parameters. If two parties correctly
+ * execute this primitive, they will produce the same output. This
+ * primitive can be invoked by a scheme to derive a shared secret key;
+ * specifically, it may be used with the schemes ECKAS-DH1 and
+ * DL/ECKAS-DH2. It assumes that the input keys are valid (see also
+ * Section 7.2.2).
+ */
+public class ECDHBasicAgreement
+ implements BasicAgreement
+{
+ private ECPrivateKeyParameters key;
+
+ public void init(
+ CipherParameters key)
+ {
+ this.key = (ECPrivateKeyParameters)key;
+ }
+
+ public int getFieldSize()
+ {
+ return (key.getParameters().getCurve().getFieldSize() + 7) / 8;
+ }
+
+ public BigInteger calculateAgreement(
+ CipherParameters pubKey)
+ {
+ ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey;
+ ECPoint P = pub.getQ().multiply(key.getD());
+
+ // if (p.isInfinity()) throw new RuntimeException("d*Q == infinity");
+
+ return P.getX().toBigInteger();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/ECDHCBasicAgreement.java b/core/src/main/java/org/bouncycastle/crypto/agreement/ECDHCBasicAgreement.java
new file mode 100644
index 00000000..12b84052
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/agreement/ECDHCBasicAgreement.java
@@ -0,0 +1,58 @@
+package org.bouncycastle.crypto.agreement;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.BasicAgreement;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.math.ec.ECPoint;
+
+/**
+ * P1363 7.2.2 ECSVDP-DHC
+ *
+ * ECSVDP-DHC is Elliptic Curve Secret Value Derivation Primitive,
+ * Diffie-Hellman version with cofactor multiplication. It is based on
+ * the work of [DH76], [Mil86], [Kob87], [LMQ98] and [Kal98a]. This
+ * primitive derives a shared secret value from one party's private key
+ * and another party's public key, where both have the same set of EC
+ * domain parameters. If two parties correctly execute this primitive,
+ * they will produce the same output. This primitive can be invoked by a
+ * scheme to derive a shared secret key; specifically, it may be used
+ * with the schemes ECKAS-DH1 and DL/ECKAS-DH2. It does not assume the
+ * validity of the input public key (see also Section 7.2.1).
+ * <p>
+ * Note: As stated P1363 compatibility mode with ECDH can be preset, and
+ * in this case the implementation doesn't have a ECDH compatibility mode
+ * (if you want that just use ECDHBasicAgreement and note they both implement
+ * BasicAgreement!).
+ */
+public class ECDHCBasicAgreement
+ implements BasicAgreement
+{
+ ECPrivateKeyParameters key;
+
+ public void init(
+ CipherParameters key)
+ {
+ this.key = (ECPrivateKeyParameters)key;
+ }
+
+ public int getFieldSize()
+ {
+ return (key.getParameters().getCurve().getFieldSize() + 7) / 8;
+ }
+
+ public BigInteger calculateAgreement(
+ CipherParameters pubKey)
+ {
+ ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey;
+ ECDomainParameters params = pub.getParameters();
+ ECPoint P = pub.getQ().multiply(params.getH().multiply(key.getD()));
+
+ // if (p.isInfinity()) throw new RuntimeException("Invalid public key");
+
+ return P.getX().toBigInteger();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/ECMQVBasicAgreement.java b/core/src/main/java/org/bouncycastle/crypto/agreement/ECMQVBasicAgreement.java
new file mode 100644
index 00000000..da88b4ac
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/agreement/ECMQVBasicAgreement.java
@@ -0,0 +1,91 @@
+package org.bouncycastle.crypto.agreement;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.BasicAgreement;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.MQVPrivateParameters;
+import org.bouncycastle.crypto.params.MQVPublicParameters;
+import org.bouncycastle.math.ec.ECAlgorithms;
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECPoint;
+
+public class ECMQVBasicAgreement
+ implements BasicAgreement
+{
+ MQVPrivateParameters privParams;
+
+ public void init(
+ CipherParameters key)
+ {
+ this.privParams = (MQVPrivateParameters)key;
+ }
+
+ public int getFieldSize()
+ {
+ return (privParams.getStaticPrivateKey().getParameters().getCurve().getFieldSize() + 7) / 8;
+ }
+
+ public BigInteger calculateAgreement(CipherParameters pubKey)
+ {
+ MQVPublicParameters pubParams = (MQVPublicParameters)pubKey;
+
+ ECPrivateKeyParameters staticPrivateKey = privParams.getStaticPrivateKey();
+
+ ECPoint agreement = calculateMqvAgreement(staticPrivateKey.getParameters(), staticPrivateKey,
+ privParams.getEphemeralPrivateKey(), privParams.getEphemeralPublicKey(),
+ pubParams.getStaticPublicKey(), pubParams.getEphemeralPublicKey());
+
+ return agreement.getX().toBigInteger();
+ }
+
+ // The ECMQV Primitive as described in SEC-1, 3.4
+ private ECPoint calculateMqvAgreement(
+ ECDomainParameters parameters,
+ ECPrivateKeyParameters d1U,
+ ECPrivateKeyParameters d2U,
+ ECPublicKeyParameters Q2U,
+ ECPublicKeyParameters Q1V,
+ ECPublicKeyParameters Q2V)
+ {
+ BigInteger n = parameters.getN();
+ int e = (n.bitLength() + 1) / 2;
+ BigInteger powE = ECConstants.ONE.shiftLeft(e);
+
+ // The Q2U public key is optional
+ ECPoint q;
+ if (Q2U == null)
+ {
+ q = parameters.getG().multiply(d2U.getD());
+ }
+ else
+ {
+ q = Q2U.getQ();
+ }
+
+ BigInteger x = q.getX().toBigInteger();
+ BigInteger xBar = x.mod(powE);
+ BigInteger Q2UBar = xBar.setBit(e);
+ BigInteger s = d1U.getD().multiply(Q2UBar).mod(n).add(d2U.getD()).mod(n);
+
+ BigInteger xPrime = Q2V.getQ().getX().toBigInteger();
+ BigInteger xPrimeBar = xPrime.mod(powE);
+ BigInteger Q2VBar = xPrimeBar.setBit(e);
+
+ BigInteger hs = parameters.getH().multiply(s).mod(n);
+
+// ECPoint p = Q1V.getQ().multiply(Q2VBar).add(Q2V.getQ()).multiply(hs);
+ ECPoint p = ECAlgorithms.sumOfTwoMultiplies(
+ Q1V.getQ(), Q2VBar.multiply(hs).mod(n), Q2V.getQ(), hs);
+
+ if (p.isInfinity())
+ {
+ throw new IllegalStateException("Infinity is not a valid agreement value for MQV");
+ }
+
+ return p;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEParticipant.java b/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEParticipant.java
new file mode 100644
index 00000000..94efd92d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEParticipant.java
@@ -0,0 +1,573 @@
+package org.bouncycastle.crypto.agreement.jpake;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A participant in a Password Authenticated Key Exchange by Juggling (J-PAKE) exchange.
+ * <p/>
+ * <p/>
+ * The J-PAKE exchange is defined by Feng Hao and Peter Ryan in the paper
+ * <a href="http://grouper.ieee.org/groups/1363/Research/contributions/hao-ryan-2008.pdf">
+ * "Password Authenticated Key Exchange by Juggling, 2008."</a>
+ * <p/>
+ * <p/>
+ * The J-PAKE protocol is symmetric.
+ * There is no notion of a <i>client</i> or <i>server</i>, but rather just two <i>participants</i>.
+ * An instance of {@link JPAKEParticipant} represents one participant, and
+ * is the primary interface for executing the exchange.
+ * <p/>
+ * <p/>
+ * To execute an exchange, construct a {@link JPAKEParticipant} on each end,
+ * and call the following 7 methods
+ * (once and only once, in the given order, for each participant, sending messages between them as described):
+ * <ol>
+ * <li>{@link #createRound1PayloadToSend()} - and send the payload to the other participant</li>
+ * <li>{@link #validateRound1PayloadReceived(JPAKERound1Payload)} - use the payload received from the other participant</li>
+ * <li>{@link #createRound2PayloadToSend()} - and send the payload to the other participant</li>
+ * <li>{@link #validateRound2PayloadReceived(JPAKERound2Payload)} - use the payload received from the other participant</li>
+ * <li>{@link #calculateKeyingMaterial()}</li>
+ * <li>{@link #createRound3PayloadToSend(BigInteger)} - and send the payload to the other participant</li>
+ * <li>{@link #validateRound3PayloadReceived(JPAKERound3Payload, BigInteger)} - use the payload received from the other participant</li>
+ * </ol>
+ * <p/>
+ * <p/>
+ * Each side should derive a session key from the keying material returned by {@link #calculateKeyingMaterial()}.
+ * The caller is responsible for deriving the session key using a secure key derivation function (KDF).
+ * <p/>
+ * <p/>
+ * Round 3 is an optional key confirmation process.
+ * If you do not execute round 3, then there is no assurance that both participants are using the same key.
+ * (i.e. if the participants used different passwords, then their session keys will differ.)
+ * <p/>
+ * <p/>
+ * If the round 3 validation succeeds, then the keys are guaranteed to be the same on both sides.
+ * <p/>
+ * <p/>
+ * The symmetric design can easily support the asymmetric cases when one party initiates the communication.
+ * e.g. Sometimes the round1 payload and round2 payload may be sent in one pass.
+ * Also, in some cases, the key confirmation payload can be sent together with the round2 payload.
+ * These are the trivial techniques to optimize the communication.
+ * <p/>
+ * <p/>
+ * The key confirmation process is implemented as specified in
+ * <a href="http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf">NIST SP 800-56A Revision 1</a>,
+ * Section 8.2 Unilateral Key Confirmation for Key Agreement Schemes.
+ * <p/>
+ * <p/>
+ * This class is stateful and NOT threadsafe.
+ * Each instance should only be used for ONE complete J-PAKE exchange
+ * (i.e. a new {@link JPAKEParticipant} should be constructed for each new J-PAKE exchange).
+ * <p/>
+ * <p/>
+ * See {@link JPAKEExample} for example usage.
+ */
+public class JPAKEParticipant
+{
+ /*
+ * Possible internal states. Used for state checking.
+ */
+
+ public static final int STATE_INITIALIZED = 0;
+ public static final int STATE_ROUND_1_CREATED = 10;
+ public static final int STATE_ROUND_1_VALIDATED = 20;
+ public static final int STATE_ROUND_2_CREATED = 30;
+ public static final int STATE_ROUND_2_VALIDATED = 40;
+ public static final int STATE_KEY_CALCULATED = 50;
+ public static final int STATE_ROUND_3_CREATED = 60;
+ public static final int STATE_ROUND_3_VALIDATED = 70;
+
+ /**
+ * Unique identifier of this participant.
+ * The two participants in the exchange must NOT share the same id.
+ */
+ private final String participantId;
+
+ /**
+ * Shared secret. This only contains the secret between construction
+ * and the call to {@link #calculateKeyingMaterial()}.
+ * <p/>
+ * i.e. When {@link #calculateKeyingMaterial()} is called, this buffer overwritten with 0's,
+ * and the field is set to null.
+ */
+ private char[] password;
+
+ /**
+ * Digest to use during calculations.
+ */
+ private final Digest digest;
+
+ /**
+ * Source of secure random data.
+ */
+ private final SecureRandom random;
+
+ private final BigInteger p;
+ private final BigInteger q;
+ private final BigInteger g;
+
+ /**
+ * The participantId of the other participant in this exchange.
+ */
+ private String partnerParticipantId;
+
+ /**
+ * Alice's x1 or Bob's x3.
+ */
+ private BigInteger x1;
+ /**
+ * Alice's x2 or Bob's x4.
+ */
+ private BigInteger x2;
+ /**
+ * Alice's g^x1 or Bob's g^x3.
+ */
+ private BigInteger gx1;
+ /**
+ * Alice's g^x2 or Bob's g^x4.
+ */
+ private BigInteger gx2;
+ /**
+ * Alice's g^x3 or Bob's g^x1.
+ */
+ private BigInteger gx3;
+ /**
+ * Alice's g^x4 or Bob's g^x2.
+ */
+ private BigInteger gx4;
+ /**
+ * Alice's B or Bob's A.
+ */
+ private BigInteger b;
+
+ /**
+ * The current state.
+ * See the <tt>STATE_*</tt> constants for possible values.
+ */
+ private int state;
+
+ /**
+ * Convenience constructor for a new {@link JPAKEParticipant} that uses
+ * the {@link JPAKEPrimeOrderGroups#NIST_3072} prime order group,
+ * a SHA-256 digest, and a default {@link SecureRandom} implementation.
+ * <p/>
+ * After construction, the {@link #getState() state} will be {@link #STATE_INITIALIZED}.
+ *
+ * @param participantId unique identifier of this participant.
+ * The two participants in the exchange must NOT share the same id.
+ * @param password shared secret.
+ * A defensive copy of this array is made (and cleared once {@link #calculateKeyingMaterial()} is called).
+ * Caller should clear the input password as soon as possible.
+ * @throws NullPointerException if any argument is null
+ * @throws IllegalArgumentException if password is empty
+ */
+ public JPAKEParticipant(
+ String participantId,
+ char[] password)
+ {
+ this(
+ participantId,
+ password,
+ JPAKEPrimeOrderGroups.NIST_3072);
+ }
+
+
+ /**
+ * Convenience constructor for a new {@link JPAKEParticipant} that uses
+ * a SHA-256 digest and a default {@link SecureRandom} implementation.
+ * <p/>
+ * After construction, the {@link #getState() state} will be {@link #STATE_INITIALIZED}.
+ *
+ * @param participantId unique identifier of this participant.
+ * The two participants in the exchange must NOT share the same id.
+ * @param password shared secret.
+ * A defensive copy of this array is made (and cleared once {@link #calculateKeyingMaterial()} is called).
+ * Caller should clear the input password as soon as possible.
+ * @param group prime order group.
+ * See {@link JPAKEPrimeOrderGroups} for standard groups
+ * @throws NullPointerException if any argument is null
+ * @throws IllegalArgumentException if password is empty
+ */
+ public JPAKEParticipant(
+ String participantId,
+ char[] password,
+ JPAKEPrimeOrderGroup group)
+ {
+ this(
+ participantId,
+ password,
+ group,
+ new SHA256Digest(),
+ new SecureRandom());
+ }
+
+
+ /**
+ * Construct a new {@link JPAKEParticipant}.
+ * <p/>
+ * After construction, the {@link #getState() state} will be {@link #STATE_INITIALIZED}.
+ *
+ * @param participantId unique identifier of this participant.
+ * The two participants in the exchange must NOT share the same id.
+ * @param password shared secret.
+ * A defensive copy of this array is made (and cleared once {@link #calculateKeyingMaterial()} is called).
+ * Caller should clear the input password as soon as possible.
+ * @param group prime order group.
+ * See {@link JPAKEPrimeOrderGroups} for standard groups
+ * @param digest digest to use during zero knowledge proofs and key confirmation (SHA-256 or stronger preferred)
+ * @param random source of secure random data for x1 and x2, and for the zero knowledge proofs
+ * @throws NullPointerException if any argument is null
+ * @throws IllegalArgumentException if password is empty
+ */
+ public JPAKEParticipant(
+ String participantId,
+ char[] password,
+ JPAKEPrimeOrderGroup group,
+ Digest digest,
+ SecureRandom random)
+ {
+ JPAKEUtil.validateNotNull(participantId, "participantId");
+ JPAKEUtil.validateNotNull(password, "password");
+ JPAKEUtil.validateNotNull(group, "p");
+ JPAKEUtil.validateNotNull(digest, "digest");
+ JPAKEUtil.validateNotNull(random, "random");
+ if (password.length == 0)
+ {
+ throw new IllegalArgumentException("Password must not be empty.");
+ }
+
+ this.participantId = participantId;
+
+ /*
+ * Create a defensive copy so as to fully encapsulate the password.
+ *
+ * This array will contain the password for the lifetime of this
+ * participant BEFORE {@link #calculateKeyingMaterial()} is called.
+ *
+ * i.e. When {@link #calculateKeyingMaterial()} is called, the array will be cleared
+ * in order to remove the password from memory.
+ *
+ * The caller is responsible for clearing the original password array
+ * given as input to this constructor.
+ */
+ this.password = Arrays.copyOf(password, password.length);
+
+ this.p = group.getP();
+ this.q = group.getQ();
+ this.g = group.getG();
+
+ this.digest = digest;
+ this.random = random;
+
+ this.state = STATE_INITIALIZED;
+ }
+
+ /**
+ * Gets the current state of this participant.
+ * See the <tt>STATE_*</tt> constants for possible values.
+ */
+ public int getState()
+ {
+ return this.state;
+ }
+
+ /**
+ * Creates and returns the payload to send to the other participant during round 1.
+ * <p/>
+ * <p/>
+ * After execution, the {@link #getState() state} will be {@link #STATE_ROUND_1_CREATED}.
+ */
+ public JPAKERound1Payload createRound1PayloadToSend()
+ {
+ if (this.state >= STATE_ROUND_1_CREATED)
+ {
+ throw new IllegalStateException("Round1 payload already created for " + participantId);
+ }
+
+ this.x1 = JPAKEUtil.generateX1(q, random);
+ this.x2 = JPAKEUtil.generateX2(q, random);
+
+ this.gx1 = JPAKEUtil.calculateGx(p, g, x1);
+ this.gx2 = JPAKEUtil.calculateGx(p, g, x2);
+ BigInteger[] knowledgeProofForX1 = JPAKEUtil.calculateZeroKnowledgeProof(p, q, g, gx1, x1, participantId, digest, random);
+ BigInteger[] knowledgeProofForX2 = JPAKEUtil.calculateZeroKnowledgeProof(p, q, g, gx2, x2, participantId, digest, random);
+
+ this.state = STATE_ROUND_1_CREATED;
+
+ return new JPAKERound1Payload(participantId, gx1, gx2, knowledgeProofForX1, knowledgeProofForX2);
+ }
+
+ /**
+ * Validates the payload received from the other participant during round 1.
+ * <p/>
+ * <p/>
+ * Must be called prior to {@link #createRound2PayloadToSend()}.
+ * <p/>
+ * <p/>
+ * After execution, the {@link #getState() state} will be {@link #STATE_ROUND_1_VALIDATED}.
+ *
+ * @throws CryptoException if validation fails.
+ * @throws IllegalStateException if called multiple times.
+ */
+ public void validateRound1PayloadReceived(JPAKERound1Payload round1PayloadReceived)
+ throws CryptoException
+ {
+ if (this.state >= STATE_ROUND_1_VALIDATED)
+ {
+ throw new IllegalStateException("Validation already attempted for round1 payload for" + participantId);
+ }
+ this.partnerParticipantId = round1PayloadReceived.getParticipantId();
+ this.gx3 = round1PayloadReceived.getGx1();
+ this.gx4 = round1PayloadReceived.getGx2();
+
+ BigInteger[] knowledgeProofForX3 = round1PayloadReceived.getKnowledgeProofForX1();
+ BigInteger[] knowledgeProofForX4 = round1PayloadReceived.getKnowledgeProofForX2();
+
+ JPAKEUtil.validateParticipantIdsDiffer(participantId, round1PayloadReceived.getParticipantId());
+ JPAKEUtil.validateGx4(gx4);
+ JPAKEUtil.validateZeroKnowledgeProof(p, q, g, gx3, knowledgeProofForX3, round1PayloadReceived.getParticipantId(), digest);
+ JPAKEUtil.validateZeroKnowledgeProof(p, q, g, gx4, knowledgeProofForX4, round1PayloadReceived.getParticipantId(), digest);
+
+ this.state = STATE_ROUND_1_VALIDATED;
+ }
+
+ /**
+ * Creates and returns the payload to send to the other participant during round 2.
+ * <p/>
+ * <p/>
+ * {@link #validateRound1PayloadReceived(JPAKERound1Payload)} must be called prior to this method.
+ * <p/>
+ * <p/>
+ * After execution, the {@link #getState() state} will be {@link #STATE_ROUND_2_CREATED}.
+ *
+ * @throws IllegalStateException if called prior to {@link #validateRound1PayloadReceived(JPAKERound1Payload)}, or multiple times
+ */
+ public JPAKERound2Payload createRound2PayloadToSend()
+ {
+ if (this.state >= STATE_ROUND_2_CREATED)
+ {
+ throw new IllegalStateException("Round2 payload already created for " + this.participantId);
+ }
+ if (this.state < STATE_ROUND_1_VALIDATED)
+ {
+ throw new IllegalStateException("Round1 payload must be validated prior to creating Round2 payload for " + this.participantId);
+ }
+ BigInteger gA = JPAKEUtil.calculateGA(p, gx1, gx3, gx4);
+ BigInteger s = JPAKEUtil.calculateS(password);
+ BigInteger x2s = JPAKEUtil.calculateX2s(q, x2, s);
+ BigInteger A = JPAKEUtil.calculateA(p, q, gA, x2s);
+ BigInteger[] knowledgeProofForX2s = JPAKEUtil.calculateZeroKnowledgeProof(p, q, gA, A, x2s, participantId, digest, random);
+
+ this.state = STATE_ROUND_2_CREATED;
+
+ return new JPAKERound2Payload(participantId, A, knowledgeProofForX2s);
+ }
+
+ /**
+ * Validates the payload received from the other participant during round 2.
+ * <p/>
+ * <p/>
+ * Note that this DOES NOT detect a non-common password.
+ * The only indication of a non-common password is through derivation
+ * of different keys (which can be detected explicitly by executing round 3 and round 4)
+ * <p/>
+ * <p/>
+ * Must be called prior to {@link #calculateKeyingMaterial()}.
+ * <p/>
+ * <p/>
+ * After execution, the {@link #getState() state} will be {@link #STATE_ROUND_2_VALIDATED}.
+ *
+ * @throws CryptoException if validation fails.
+ * @throws IllegalStateException if called prior to {@link #validateRound1PayloadReceived(JPAKERound1Payload)}, or multiple times
+ */
+ public void validateRound2PayloadReceived(JPAKERound2Payload round2PayloadReceived)
+ throws CryptoException
+ {
+ if (this.state >= STATE_ROUND_2_VALIDATED)
+ {
+ throw new IllegalStateException("Validation already attempted for round2 payload for" + participantId);
+ }
+ if (this.state < STATE_ROUND_1_VALIDATED)
+ {
+ throw new IllegalStateException("Round1 payload must be validated prior to validating Round2 payload for " + this.participantId);
+ }
+ BigInteger gB = JPAKEUtil.calculateGA(p, gx3, gx1, gx2);
+ this.b = round2PayloadReceived.getA();
+ BigInteger[] knowledgeProofForX4s = round2PayloadReceived.getKnowledgeProofForX2s();
+
+ JPAKEUtil.validateParticipantIdsDiffer(participantId, round2PayloadReceived.getParticipantId());
+ JPAKEUtil.validateParticipantIdsEqual(this.partnerParticipantId, round2PayloadReceived.getParticipantId());
+ JPAKEUtil.validateGa(gB);
+ JPAKEUtil.validateZeroKnowledgeProof(p, q, gB, b, knowledgeProofForX4s, round2PayloadReceived.getParticipantId(), digest);
+
+ this.state = STATE_ROUND_2_VALIDATED;
+ }
+
+ /**
+ * Calculates and returns the key material.
+ * A session key must be derived from this key material using a secure key derivation function (KDF).
+ * The KDF used to derive the key is handled externally (i.e. not by {@link JPAKEParticipant}).
+ * <p/>
+ * <p/>
+ * The keying material will be identical for each participant if and only if
+ * each participant's password is the same. i.e. If the participants do not
+ * share the same password, then each participant will derive a different key.
+ * Therefore, if you immediately start using a key derived from
+ * the keying material, then you must handle detection of incorrect keys.
+ * If you want to handle this detection explicitly, you can optionally perform
+ * rounds 3 and 4. See {@link JPAKEParticipant} for details on how to execute
+ * rounds 3 and 4.
+ * <p/>
+ * <p/>
+ * The keying material will be in the range <tt>[0, p-1]</tt>.
+ * <p/>
+ * <p/>
+ * {@link #validateRound2PayloadReceived(JPAKERound2Payload)} must be called prior to this method.
+ * <p/>
+ * <p/>
+ * As a side effect, the internal {@link #password} array is cleared, since it is no longer needed.
+ * <p/>
+ * <p/>
+ * After execution, the {@link #getState() state} will be {@link #STATE_KEY_CALCULATED}.
+ *
+ * @throws IllegalStateException if called prior to {@link #validateRound2PayloadReceived(JPAKERound2Payload)},
+ * or if called multiple times.
+ */
+ public BigInteger calculateKeyingMaterial()
+ {
+ if (this.state >= STATE_KEY_CALCULATED)
+ {
+ throw new IllegalStateException("Key already calculated for " + participantId);
+ }
+ if (this.state < STATE_ROUND_2_VALIDATED)
+ {
+ throw new IllegalStateException("Round2 payload must be validated prior to creating key for " + participantId);
+ }
+ BigInteger s = JPAKEUtil.calculateS(password);
+
+ /*
+ * Clear the password array from memory, since we don't need it anymore.
+ *
+ * Also set the field to null as a flag to indicate that the key has already been calculated.
+ */
+ Arrays.fill(password, (char)0);
+ this.password = null;
+
+ BigInteger keyingMaterial = JPAKEUtil.calculateKeyingMaterial(p, q, gx4, x2, s, b);
+
+ /*
+ * Clear the ephemeral private key fields as well.
+ * Note that we're relying on the garbage collector to do its job to clean these up.
+ * The old objects will hang around in memory until the garbage collector destroys them.
+ *
+ * If the ephemeral private keys x1 and x2 are leaked,
+ * the attacker might be able to brute-force the password.
+ */
+ this.x1 = null;
+ this.x2 = null;
+ this.b = null;
+
+ /*
+ * Do not clear gx* yet, since those are needed by round 3.
+ */
+
+ this.state = STATE_KEY_CALCULATED;
+
+ return keyingMaterial;
+ }
+
+
+ /**
+ * Creates and returns the payload to send to the other participant during round 3.
+ * <p/>
+ * <p/>
+ * See {@link JPAKEParticipant} for more details on round 3.
+ * <p/>
+ * <p/>
+ * After execution, the {@link #getState() state} will be {@link #STATE_ROUND_3_CREATED}.
+ *
+ * @param keyingMaterial The keying material as returned from {@link #calculateKeyingMaterial()}.
+ * @throws IllegalStateException if called prior to {@link #calculateKeyingMaterial()}, or multiple times
+ */
+ public JPAKERound3Payload createRound3PayloadToSend(BigInteger keyingMaterial)
+ {
+ if (this.state >= STATE_ROUND_3_CREATED)
+ {
+ throw new IllegalStateException("Round3 payload already created for " + this.participantId);
+ }
+ if (this.state < STATE_KEY_CALCULATED)
+ {
+ throw new IllegalStateException("Keying material must be calculated prior to creating Round3 payload for " + this.participantId);
+ }
+
+ BigInteger macTag = JPAKEUtil.calculateMacTag(
+ this.participantId,
+ this.partnerParticipantId,
+ this.gx1,
+ this.gx2,
+ this.gx3,
+ this.gx4,
+ keyingMaterial,
+ this.digest);
+
+ this.state = STATE_ROUND_3_CREATED;
+
+ return new JPAKERound3Payload(participantId, macTag);
+ }
+
+ /**
+ * Validates the payload received from the other participant during round 3.
+ * <p/>
+ * <p/>
+ * See {@link JPAKEParticipant} for more details on round 3.
+ * <p/>
+ * <p/>
+ * After execution, the {@link #getState() state} will be {@link #STATE_ROUND_3_VALIDATED}.
+ *
+ * @param keyingMaterial The keying material as returned from {@link #calculateKeyingMaterial()}.
+ * @throws CryptoException if validation fails.
+ * @throws IllegalStateException if called prior to {@link #calculateKeyingMaterial()}, or multiple times
+ */
+ public void validateRound3PayloadReceived(JPAKERound3Payload round3PayloadReceived, BigInteger keyingMaterial)
+ throws CryptoException
+ {
+ if (this.state >= STATE_ROUND_3_VALIDATED)
+ {
+ throw new IllegalStateException("Validation already attempted for round3 payload for" + participantId);
+ }
+ if (this.state < STATE_KEY_CALCULATED)
+ {
+ throw new IllegalStateException("Keying material must be calculated validated prior to validating Round3 payload for " + this.participantId);
+ }
+ JPAKEUtil.validateParticipantIdsDiffer(participantId, round3PayloadReceived.getParticipantId());
+ JPAKEUtil.validateParticipantIdsEqual(this.partnerParticipantId, round3PayloadReceived.getParticipantId());
+
+ JPAKEUtil.validateMacTag(
+ this.participantId,
+ this.partnerParticipantId,
+ this.gx1,
+ this.gx2,
+ this.gx3,
+ this.gx4,
+ keyingMaterial,
+ this.digest,
+ round3PayloadReceived.getMacTag());
+
+
+ /*
+ * Clear the rest of the fields.
+ */
+ this.gx1 = null;
+ this.gx2 = null;
+ this.gx3 = null;
+ this.gx4 = null;
+
+ this.state = STATE_ROUND_3_VALIDATED;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroup.java b/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroup.java
new file mode 100644
index 00000000..d5df727d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroup.java
@@ -0,0 +1,122 @@
+package org.bouncycastle.crypto.agreement.jpake;
+
+import java.math.BigInteger;
+
+/**
+ * A pre-computed prime order group for use during a J-PAKE exchange.
+ * <p/>
+ * <p/>
+ * Typically a Schnorr group is used. In general, J-PAKE can use any prime order group
+ * that is suitable for public key cryptography, including elliptic curve cryptography.
+ * <p/>
+ * <p/>
+ * See {@link JPAKEPrimeOrderGroups} for convenient standard groups.
+ * <p/>
+ * <p/>
+ * NIST <a href="http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/DSA2_All.pdf">publishes</a>
+ * many groups that can be used for the desired level of security.
+ */
+public class JPAKEPrimeOrderGroup
+{
+ private final BigInteger p;
+ private final BigInteger q;
+ private final BigInteger g;
+
+ /**
+ * Constructs a new {@link JPAKEPrimeOrderGroup}.
+ * <p/>
+ * <p/>
+ * In general, you should use one of the pre-approved groups from
+ * {@link JPAKEPrimeOrderGroups}, rather than manually constructing one.
+ * <p/>
+ * <p/>
+ * The following basic checks are performed:
+ * <ul>
+ * <li>p-1 must be evenly divisible by q</li>
+ * <li>g must be in [2, p-1]</li>
+ * <li>g^q mod p must equal 1</li>
+ * <li>p must be prime (within reasonably certainty)</li>
+ * <li>q must be prime (within reasonably certainty)</li>
+ * </ul>
+ * <p/>
+ * <p/>
+ * The prime checks are performed using {@link BigInteger#isProbablePrime(int)},
+ * and are therefore subject to the same probability guarantees.
+ * <p/>
+ * <p/>
+ * These checks prevent trivial mistakes.
+ * However, due to the small uncertainties if p and q are not prime,
+ * advanced attacks are not prevented.
+ * Use it at your own risk.
+ *
+ * @throws NullPointerException if any argument is null
+ * @throws IllegalArgumentException if any of the above validations fail
+ */
+ public JPAKEPrimeOrderGroup(BigInteger p, BigInteger q, BigInteger g)
+ {
+ /*
+ * Don't skip the checks on user-specified groups.
+ */
+ this(p, q, g, false);
+ }
+
+ /**
+ * Internal package-private constructor used by the pre-approved
+ * groups in {@link JPAKEPrimeOrderGroups}.
+ * These pre-approved groups can avoid the expensive checks.
+ */
+ JPAKEPrimeOrderGroup(BigInteger p, BigInteger q, BigInteger g, boolean skipChecks)
+ {
+ JPAKEUtil.validateNotNull(p, "p");
+ JPAKEUtil.validateNotNull(q, "q");
+ JPAKEUtil.validateNotNull(g, "g");
+
+ if (!skipChecks)
+ {
+ if (!p.subtract(JPAKEUtil.ONE).mod(q).equals(JPAKEUtil.ZERO))
+ {
+ throw new IllegalArgumentException("p-1 must be evenly divisible by q");
+ }
+ if (g.compareTo(BigInteger.valueOf(2)) == -1 || g.compareTo(p.subtract(JPAKEUtil.ONE)) == 1)
+ {
+ throw new IllegalArgumentException("g must be in [2, p-1]");
+ }
+ if (!g.modPow(q, p).equals(JPAKEUtil.ONE))
+ {
+ throw new IllegalArgumentException("g^q mod p must equal 1");
+ }
+ /*
+ * Note that these checks do not guarantee that p and q are prime.
+ * We just have reasonable certainty that they are prime.
+ */
+ if (!p.isProbablePrime(20))
+ {
+ throw new IllegalArgumentException("p must be prime");
+ }
+ if (!q.isProbablePrime(20))
+ {
+ throw new IllegalArgumentException("q must be prime");
+ }
+ }
+
+ this.p = p;
+ this.q = q;
+ this.g = g;
+ }
+
+ public BigInteger getP()
+ {
+ return p;
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public BigInteger getG()
+ {
+ return g;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroups.java b/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroups.java
new file mode 100644
index 00000000..812d776e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroups.java
@@ -0,0 +1,113 @@
+package org.bouncycastle.crypto.agreement.jpake;
+
+import java.math.BigInteger;
+
+/**
+ * Standard pre-computed prime order groups for use by J-PAKE.
+ * (J-PAKE can use pre-computed prime order groups, same as DSA and Diffie-Hellman.)
+ * <p/>
+ * <p/>
+ * This class contains some convenient constants for use as input for
+ * constructing {@link JPAKEParticipant}s.
+ * <p/>
+ * <p/>
+ * The prime order groups below are taken from Sun's JDK JavaDoc (docs/guide/security/CryptoSpec.html#AppB),
+ * and from the prime order groups
+ * <a href="http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/DSA2_All.pdf">published by NIST</a>.
+ */
+public class JPAKEPrimeOrderGroups
+{
+ /**
+ * From Sun's JDK JavaDoc (docs/guide/security/CryptoSpec.html#AppB)
+ * 1024-bit p, 160-bit q and 1024-bit g for 80-bit security.
+ */
+ public static final JPAKEPrimeOrderGroup SUN_JCE_1024 = new JPAKEPrimeOrderGroup(
+ // p
+ new BigInteger(
+ "fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80b6512669" +
+ "455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b801d346ff26660b7" +
+ "6b9950a5a49f9fe8047b1022c24fbba9d7feb7c61bf83b57e7c6a8a6150f04fb" +
+ "83f6d3c51ec3023554135a169132f675f3ae2b61d72aeff22203199dd14801c7", 16),
+ // q
+ new BigInteger(
+ "9760508f15230bccb292b982a2eb840bf0581cf5", 16),
+ // g
+ new BigInteger(
+ "f7e1a085d69b3ddecbbcab5c36b857b97994afbbfa3aea82f9574c0b3d078267" +
+ "5159578ebad4594fe67107108180b449167123e84c281613b7cf09328cc8a6e1" +
+ "3c167a8b547c8d28e0a3ae1e2bb3a675916ea37f0bfa213562f1fb627a01243b" +
+ "cca4f1bea8519089a883dfe15ae59f06928b665e807b552564014c3bfecf492a", 16),
+ true
+ );
+
+ /**
+ * From NIST.
+ * 2048-bit p, 224-bit q and 2048-bit g for 112-bit security.
+ */
+ public static final JPAKEPrimeOrderGroup NIST_2048 = new JPAKEPrimeOrderGroup(
+ // p
+ new BigInteger(
+ "C196BA05AC29E1F9C3C72D56DFFC6154A033F1477AC88EC37F09BE6C5BB95F51" +
+ "C296DD20D1A28A067CCC4D4316A4BD1DCA55ED1066D438C35AEBAABF57E7DAE4" +
+ "28782A95ECA1C143DB701FD48533A3C18F0FE23557EA7AE619ECACC7E0B51652" +
+ "A8776D02A425567DED36EABD90CA33A1E8D988F0BBB92D02D1D20290113BB562" +
+ "CE1FC856EEB7CDD92D33EEA6F410859B179E7E789A8F75F645FAE2E136D252BF" +
+ "FAFF89528945C1ABE705A38DBC2D364AADE99BE0D0AAD82E5320121496DC65B3" +
+ "930E38047294FF877831A16D5228418DE8AB275D7D75651CEFED65F78AFC3EA7" +
+ "FE4D79B35F62A0402A1117599ADAC7B269A59F353CF450E6982D3B1702D9CA83", 16),
+ // q
+ new BigInteger(
+ "90EAF4D1AF0708B1B612FF35E0A2997EB9E9D263C9CE659528945C0D", 16),
+ // g
+ new BigInteger(
+ "A59A749A11242C58C894E9E5A91804E8FA0AC64B56288F8D47D51B1EDC4D6544" +
+ "4FECA0111D78F35FC9FDD4CB1F1B79A3BA9CBEE83A3F811012503C8117F98E50" +
+ "48B089E387AF6949BF8784EBD9EF45876F2E6A5A495BE64B6E770409494B7FEE" +
+ "1DBB1E4B2BC2A53D4F893D418B7159592E4FFFDF6969E91D770DAEBD0B5CB14C" +
+ "00AD68EC7DC1E5745EA55C706C4A1C5C88964E34D09DEB753AD418C1AD0F4FDF" +
+ "D049A955E5D78491C0B7A2F1575A008CCD727AB376DB6E695515B05BD412F5B8" +
+ "C2F4C77EE10DA48ABD53F5DD498927EE7B692BBBCDA2FB23A516C5B4533D7398" +
+ "0B2A3B60E384ED200AE21B40D273651AD6060C13D97FD69AA13C5611A51B9085", 16),
+ true
+ );
+
+ /**
+ * From NIST.
+ * 3072-bit p, 256-bit q and 3072-bit g for 128-bit security.
+ */
+ public static final JPAKEPrimeOrderGroup NIST_3072 = new JPAKEPrimeOrderGroup(
+ // p
+ new BigInteger(
+ "90066455B5CFC38F9CAA4A48B4281F292C260FEEF01FD61037E56258A7795A1C" +
+ "7AD46076982CE6BB956936C6AB4DCFE05E6784586940CA544B9B2140E1EB523F" +
+ "009D20A7E7880E4E5BFA690F1B9004A27811CD9904AF70420EEFD6EA11EF7DA1" +
+ "29F58835FF56B89FAA637BC9AC2EFAAB903402229F491D8D3485261CD068699B" +
+ "6BA58A1DDBBEF6DB51E8FE34E8A78E542D7BA351C21EA8D8F1D29F5D5D159394" +
+ "87E27F4416B0CA632C59EFD1B1EB66511A5A0FBF615B766C5862D0BD8A3FE7A0" +
+ "E0DA0FB2FE1FCB19E8F9996A8EA0FCCDE538175238FC8B0EE6F29AF7F642773E" +
+ "BE8CD5402415A01451A840476B2FCEB0E388D30D4B376C37FE401C2A2C2F941D" +
+ "AD179C540C1C8CE030D460C4D983BE9AB0B20F69144C1AE13F9383EA1C08504F" +
+ "B0BF321503EFE43488310DD8DC77EC5B8349B8BFE97C2C560EA878DE87C11E3D" +
+ "597F1FEA742D73EEC7F37BE43949EF1A0D15C3F3E3FC0A8335617055AC91328E" +
+ "C22B50FC15B941D3D1624CD88BC25F3E941FDDC6200689581BFEC416B4B2CB73", 16),
+ // q
+ new BigInteger(
+ "CFA0478A54717B08CE64805B76E5B14249A77A4838469DF7F7DC987EFCCFB11D", 16),
+ // g
+ new BigInteger(
+ "5E5CBA992E0A680D885EB903AEA78E4A45A469103D448EDE3B7ACCC54D521E37" +
+ "F84A4BDD5B06B0970CC2D2BBB715F7B82846F9A0C393914C792E6A923E2117AB" +
+ "805276A975AADB5261D91673EA9AAFFEECBFA6183DFCB5D3B7332AA19275AFA1" +
+ "F8EC0B60FB6F66CC23AE4870791D5982AAD1AA9485FD8F4A60126FEB2CF05DB8" +
+ "A7F0F09B3397F3937F2E90B9E5B9C9B6EFEF642BC48351C46FB171B9BFA9EF17" +
+ "A961CE96C7E7A7CC3D3D03DFAD1078BA21DA425198F07D2481622BCE45969D9C" +
+ "4D6063D72AB7A0F08B2F49A7CC6AF335E08C4720E31476B67299E231F8BD90B3" +
+ "9AC3AE3BE0C6B6CACEF8289A2E2873D58E51E029CAFBD55E6841489AB66B5B4B" +
+ "9BA6E2F784660896AFF387D92844CCB8B69475496DE19DA2E58259B090489AC8" +
+ "E62363CDF82CFD8EF2A427ABCD65750B506F56DDE3B988567A88126B914D7828" +
+ "E2B63A6D7ED0747EC59E0E0A23CE7D8A74C1D2C2A7AFB6A29799620F00E11C33" +
+ "787F7DED3B30E1A22D09F1FBDA1ABBBFBF25CAE05A13F812E34563F99410E73B", 16),
+ true
+ );
+
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound1Payload.java b/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound1Payload.java
new file mode 100644
index 00000000..b319f9c4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound1Payload.java
@@ -0,0 +1,99 @@
+package org.bouncycastle.crypto.agreement.jpake;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * The payload sent/received during the first round of a J-PAKE exchange.
+ * <p/>
+ * <p/>
+ * Each {@link JPAKEParticipant} creates and sends an instance
+ * of this payload to the other {@link JPAKEParticipant}.
+ * The payload to send should be created via
+ * {@link JPAKEParticipant#createRound1PayloadToSend()}.
+ * <p/>
+ * <p/>
+ * Each {@link JPAKEParticipant} must also validate the payload
+ * received from the other {@link JPAKEParticipant}.
+ * The received payload should be validated via
+ * {@link JPAKEParticipant#validateRound1PayloadReceived(JPAKERound1Payload)}.
+ * <p/>
+ */
+public class JPAKERound1Payload
+{
+ /**
+ * The id of the {@link JPAKEParticipant} who created/sent this payload.
+ */
+ private final String participantId;
+
+ /**
+ * The value of g^x1
+ */
+ private final BigInteger gx1;
+
+ /**
+ * The value of g^x2
+ */
+ private final BigInteger gx2;
+
+ /**
+ * The zero knowledge proof for x1.
+ * <p/>
+ * This is a two element array, containing {g^v, r} for x1.
+ */
+ private final BigInteger[] knowledgeProofForX1;
+
+ /**
+ * The zero knowledge proof for x2.
+ * <p/>
+ * This is a two element array, containing {g^v, r} for x2.
+ */
+ private final BigInteger[] knowledgeProofForX2;
+
+ public JPAKERound1Payload(
+ String participantId,
+ BigInteger gx1,
+ BigInteger gx2,
+ BigInteger[] knowledgeProofForX1,
+ BigInteger[] knowledgeProofForX2)
+ {
+ JPAKEUtil.validateNotNull(participantId, "participantId");
+ JPAKEUtil.validateNotNull(gx1, "gx1");
+ JPAKEUtil.validateNotNull(gx2, "gx2");
+ JPAKEUtil.validateNotNull(knowledgeProofForX1, "knowledgeProofForX1");
+ JPAKEUtil.validateNotNull(knowledgeProofForX2, "knowledgeProofForX2");
+
+ this.participantId = participantId;
+ this.gx1 = gx1;
+ this.gx2 = gx2;
+ this.knowledgeProofForX1 = Arrays.copyOf(knowledgeProofForX1, knowledgeProofForX1.length);
+ this.knowledgeProofForX2 = Arrays.copyOf(knowledgeProofForX2, knowledgeProofForX2.length);
+ }
+
+ public String getParticipantId()
+ {
+ return participantId;
+ }
+
+ public BigInteger getGx1()
+ {
+ return gx1;
+ }
+
+ public BigInteger getGx2()
+ {
+ return gx2;
+ }
+
+ public BigInteger[] getKnowledgeProofForX1()
+ {
+ return Arrays.copyOf(knowledgeProofForX1, knowledgeProofForX1.length);
+ }
+
+ public BigInteger[] getKnowledgeProofForX2()
+ {
+ return Arrays.copyOf(knowledgeProofForX2, knowledgeProofForX2.length);
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound2Payload.java b/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound2Payload.java
new file mode 100644
index 00000000..8800cf5f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound2Payload.java
@@ -0,0 +1,71 @@
+package org.bouncycastle.crypto.agreement.jpake;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * The payload sent/received during the second round of a J-PAKE exchange.
+ * <p/>
+ * <p/>
+ * Each {@link JPAKEParticipant} creates and sends an instance
+ * of this payload to the other {@link JPAKEParticipant}.
+ * The payload to send should be created via
+ * {@link JPAKEParticipant#createRound2PayloadToSend()}
+ * <p/>
+ * <p/>
+ * Each {@link JPAKEParticipant} must also validate the payload
+ * received from the other {@link JPAKEParticipant}.
+ * The received payload should be validated via
+ * {@link JPAKEParticipant#validateRound2PayloadReceived(JPAKERound2Payload)}
+ * <p/>
+ */
+public class JPAKERound2Payload
+{
+ /**
+ * The id of the {@link JPAKEParticipant} who created/sent this payload.
+ */
+ private final String participantId;
+
+ /**
+ * The value of A, as computed during round 2.
+ */
+ private final BigInteger a;
+
+ /**
+ * The zero knowledge proof for x2 * s.
+ * <p/>
+ * This is a two element array, containing {g^v, r} for x2 * s.
+ */
+ private final BigInteger[] knowledgeProofForX2s;
+
+ public JPAKERound2Payload(
+ String participantId,
+ BigInteger a,
+ BigInteger[] knowledgeProofForX2s)
+ {
+ JPAKEUtil.validateNotNull(participantId, "participantId");
+ JPAKEUtil.validateNotNull(a, "a");
+ JPAKEUtil.validateNotNull(knowledgeProofForX2s, "knowledgeProofForX2s");
+
+ this.participantId = participantId;
+ this.a = a;
+ this.knowledgeProofForX2s = Arrays.copyOf(knowledgeProofForX2s, knowledgeProofForX2s.length);
+ }
+
+ public String getParticipantId()
+ {
+ return participantId;
+ }
+
+ public BigInteger getA()
+ {
+ return a;
+ }
+
+ public BigInteger[] getKnowledgeProofForX2s()
+ {
+ return Arrays.copyOf(knowledgeProofForX2s, knowledgeProofForX2s.length);
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound3Payload.java b/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound3Payload.java
new file mode 100644
index 00000000..c1255df6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound3Payload.java
@@ -0,0 +1,52 @@
+package org.bouncycastle.crypto.agreement.jpake;
+
+import java.math.BigInteger;
+
+/**
+ * The payload sent/received during the optional third round of a J-PAKE exchange,
+ * which is for explicit key confirmation.
+ * <p/>
+ * <p/>
+ * Each {@link JPAKEParticipant} creates and sends an instance
+ * of this payload to the other {@link JPAKEParticipant}.
+ * The payload to send should be created via
+ * {@link JPAKEParticipant#createRound3PayloadToSend(BigInteger)}
+ * <p/>
+ * <p/>
+ * Each {@link JPAKEParticipant} must also validate the payload
+ * received from the other {@link JPAKEParticipant}.
+ * The received payload should be validated via
+ * {@link JPAKEParticipant#validateRound3PayloadReceived(JPAKERound3Payload, BigInteger)}
+ * <p/>
+ */
+public class JPAKERound3Payload
+{
+ /**
+ * The id of the {@link JPAKEParticipant} who created/sent this payload.
+ */
+ private final String participantId;
+
+ /**
+ * The value of MacTag, as computed by round 3.
+ *
+ * @see JPAKEUtil#calculateMacTag(String, String, BigInteger, BigInteger, BigInteger, BigInteger, BigInteger, org.bouncycastle.crypto.Digest)
+ */
+ private final BigInteger macTag;
+
+ public JPAKERound3Payload(String participantId, BigInteger magTag)
+ {
+ this.participantId = participantId;
+ this.macTag = magTag;
+ }
+
+ public String getParticipantId()
+ {
+ return participantId;
+ }
+
+ public BigInteger getMacTag()
+ {
+ return macTag;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEUtil.java b/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEUtil.java
new file mode 100644
index 00000000..416152e9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEUtil.java
@@ -0,0 +1,508 @@
+package org.bouncycastle.crypto.agreement.jpake;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.BigIntegers;
+import org.bouncycastle.util.Strings;
+
+/**
+ * Primitives needed for a J-PAKE exchange.
+ * <p/>
+ * <p/>
+ * The recommended way to perform a J-PAKE exchange is by using
+ * two {@link JPAKEParticipant}s. Internally, those participants
+ * call these primitive operations in {@link JPAKEUtil}.
+ * <p/>
+ * <p/>
+ * The primitives, however, can be used without a {@link JPAKEParticipant}
+ * if needed.
+ */
+public class JPAKEUtil
+{
+ static final BigInteger ZERO = BigInteger.valueOf(0);
+ static final BigInteger ONE = BigInteger.valueOf(1);
+
+ /**
+ * Return a value that can be used as x1 or x3 during round 1.
+ * <p/>
+ * <p/>
+ * The returned value is a random value in the range <tt>[0, q-1]</tt>.
+ */
+ public static BigInteger generateX1(
+ BigInteger q,
+ SecureRandom random)
+ {
+ BigInteger min = ZERO;
+ BigInteger max = q.subtract(ONE);
+ return BigIntegers.createRandomInRange(min, max, random);
+ }
+
+ /**
+ * Return a value that can be used as x2 or x4 during round 1.
+ * <p/>
+ * <p/>
+ * The returned value is a random value in the range <tt>[1, q-1]</tt>.
+ */
+ public static BigInteger generateX2(
+ BigInteger q,
+ SecureRandom random)
+ {
+ BigInteger min = ONE;
+ BigInteger max = q.subtract(ONE);
+ return BigIntegers.createRandomInRange(min, max, random);
+ }
+
+ /**
+ * Converts the given password to a {@link BigInteger}
+ * for use in arithmetic calculations.
+ */
+ public static BigInteger calculateS(char[] password)
+ {
+ return new BigInteger(Strings.toUTF8ByteArray(password));
+ }
+
+ /**
+ * Calculate g^x mod p as done in round 1.
+ */
+ public static BigInteger calculateGx(
+ BigInteger p,
+ BigInteger g,
+ BigInteger x)
+ {
+ return g.modPow(x, p);
+ }
+
+
+ /**
+ * Calculate ga as done in round 2.
+ */
+ public static BigInteger calculateGA(
+ BigInteger p,
+ BigInteger gx1,
+ BigInteger gx3,
+ BigInteger gx4)
+ {
+ // ga = g^(x1+x3+x4) = g^x1 * g^x3 * g^x4
+ return gx1.multiply(gx3).multiply(gx4).mod(p);
+ }
+
+
+ /**
+ * Calculate x2 * s as done in round 2.
+ */
+ public static BigInteger calculateX2s(
+ BigInteger q,
+ BigInteger x2,
+ BigInteger s)
+ {
+ return x2.multiply(s).mod(q);
+ }
+
+
+ /**
+ * Calculate A as done in round 2.
+ */
+ public static BigInteger calculateA(
+ BigInteger p,
+ BigInteger q,
+ BigInteger gA,
+ BigInteger x2s)
+ {
+ // A = ga^(x*s)
+ return gA.modPow(x2s, p);
+ }
+
+ /**
+ * Calculate a zero knowledge proof of x using Schnorr's signature.
+ * The returned array has two elements {g^v, r = v-x*h} for x.
+ */
+ public static BigInteger[] calculateZeroKnowledgeProof(
+ BigInteger p,
+ BigInteger q,
+ BigInteger g,
+ BigInteger gx,
+ BigInteger x,
+ String participantId,
+ Digest digest,
+ SecureRandom random)
+ {
+ BigInteger[] zeroKnowledgeProof = new BigInteger[2];
+
+ /* Generate a random v, and compute g^v */
+ BigInteger vMin = ZERO;
+ BigInteger vMax = q.subtract(ONE);
+ BigInteger v = BigIntegers.createRandomInRange(vMin, vMax, random);
+
+ BigInteger gv = g.modPow(v, p);
+ BigInteger h = calculateHashForZeroKnowledgeProof(g, gv, gx, participantId, digest); // h
+
+ zeroKnowledgeProof[0] = gv;
+ zeroKnowledgeProof[1] = v.subtract(x.multiply(h)).mod(q); // r = v-x*h
+
+ return zeroKnowledgeProof;
+ }
+
+ private static BigInteger calculateHashForZeroKnowledgeProof(
+ BigInteger g,
+ BigInteger gr,
+ BigInteger gx,
+ String participantId,
+ Digest digest)
+ {
+ digest.reset();
+
+ updateDigestIncludingSize(digest, g);
+
+ updateDigestIncludingSize(digest, gr);
+
+ updateDigestIncludingSize(digest, gx);
+
+ updateDigestIncludingSize(digest, participantId);
+
+ byte[] output = new byte[digest.getDigestSize()];
+ digest.doFinal(output, 0);
+
+ return new BigInteger(output);
+ }
+
+ /**
+ * Validates that g^x4 is not 1.
+ *
+ * @throws CryptoException if g^x4 is 1
+ */
+ public static void validateGx4(BigInteger gx4)
+ throws CryptoException
+ {
+ if (gx4.equals(ONE))
+ {
+ throw new CryptoException("g^x validation failed. g^x should not be 1.");
+ }
+ }
+
+ /**
+ * Validates that ga is not 1.
+ * <p/>
+ * <p/>
+ * As described by Feng Hao...
+ * <p/>
+ * <blockquote>
+ * Alice could simply check ga != 1 to ensure it is a generator.
+ * In fact, as we will explain in Section 3, (x1 + x3 + x4 ) is random over Zq even in the face of active attacks.
+ * Hence, the probability for ga = 1 is extremely small - on the order of 2^160 for 160-bit q.
+ * </blockquote>
+ *
+ * @throws CryptoException if ga is 1
+ */
+ public static void validateGa(BigInteger ga)
+ throws CryptoException
+ {
+ if (ga.equals(ONE))
+ {
+ throw new CryptoException("ga is equal to 1. It should not be. The chances of this happening are on the order of 2^160 for a 160-bit q. Try again.");
+ }
+ }
+
+ /**
+ * Validates the zero knowledge proof (generated by
+ * {@link #calculateZeroKnowledgeProof(BigInteger, BigInteger, BigInteger, BigInteger, BigInteger, String, Digest, SecureRandom)})
+ * is correct.
+ *
+ * @throws CryptoException if the zero knowledge proof is not correct
+ */
+ public static void validateZeroKnowledgeProof(
+ BigInteger p,
+ BigInteger q,
+ BigInteger g,
+ BigInteger gx,
+ BigInteger[] zeroKnowledgeProof,
+ String participantId,
+ Digest digest)
+ throws CryptoException
+ {
+
+ /* sig={g^v,r} */
+ BigInteger gv = zeroKnowledgeProof[0];
+ BigInteger r = zeroKnowledgeProof[1];
+
+ BigInteger h = calculateHashForZeroKnowledgeProof(g, gv, gx, participantId, digest);
+ if (!(gx.compareTo(ZERO) == 1 && // g^x > 0
+ gx.compareTo(p) == -1 && // g^x < p
+ gx.modPow(q, p).compareTo(ONE) == 0 && // g^x^q mod q = 1
+ /*
+ * Below, I took an straightforward way to compute g^r * g^x^h,
+ * which needs 2 exp. Using a simultaneous computation technique
+ * would only need 1 exp.
+ */
+ g.modPow(r, p).multiply(gx.modPow(h, p)).mod(p).compareTo(gv) == 0)) // g^v=g^r * g^x^h
+ {
+ throw new CryptoException("Zero-knowledge proof validation failed");
+ }
+ }
+
+ /**
+ * Calculates the keying material, which can be done after round 2 has completed.
+ * A session key must be derived from this key material using a secure key derivation function (KDF).
+ * The KDF used to derive the key is handled externally (i.e. not by {@link JPAKEParticipant}).
+ * <p/>
+ * <p/>
+ * <pre>
+ * KeyingMaterial = (B/g^{x2*x4*s})^x2
+ * </pre>
+ */
+ public static BigInteger calculateKeyingMaterial(
+ BigInteger p,
+ BigInteger q,
+ BigInteger gx4,
+ BigInteger x2,
+ BigInteger s,
+ BigInteger B)
+ {
+ return gx4.modPow(x2.multiply(s).negate().mod(q), p).multiply(B).modPow(x2, p);
+ }
+
+ /**
+ * Validates that the given participant ids are not equal.
+ * (For the J-PAKE exchange, each participant must use a unique id.)
+ *
+ * @throws CryptoException if the participantId strings are equal.
+ */
+ public static void validateParticipantIdsDiffer(String participantId1, String participantId2)
+ throws CryptoException
+ {
+ if (participantId1.equals(participantId2))
+ {
+ throw new CryptoException(
+ "Both participants are using the same participantId ("
+ + participantId1
+ + "). This is not allowed. "
+ + "Each participant must use a unique participantId.");
+ }
+ }
+
+ /**
+ * Validates that the given participant ids are equal.
+ * This is used to ensure that the payloads received from
+ * each round all come from the same participant.
+ *
+ * @throws CryptoException if the participantId strings are equal.
+ */
+ public static void validateParticipantIdsEqual(String expectedParticipantId, String actualParticipantId)
+ throws CryptoException
+ {
+ if (!expectedParticipantId.equals(actualParticipantId))
+ {
+ throw new CryptoException(
+ "Received payload from incorrect partner ("
+ + actualParticipantId
+ + "). Expected to receive payload from "
+ + expectedParticipantId
+ + ".");
+ }
+ }
+
+ /**
+ * Validates that the given object is not null.
+ *
+ * @param object object in question
+ * @param description name of the object (to be used in exception message)
+ * @throws NullPointerException if the object is null.
+ */
+ public static void validateNotNull(Object object, String description)
+ {
+ if (object == null)
+ {
+ throw new NullPointerException(description + " must not be null");
+ }
+ }
+
+ /**
+ * Calculates the MacTag (to be used for key confirmation), as defined by
+ * <a href="http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf">NIST SP 800-56A Revision 1</a>,
+ * Section 8.2 Unilateral Key Confirmation for Key Agreement Schemes.
+ * <p/>
+ * <p/>
+ * <pre>
+ * MacTag = HMAC(MacKey, MacLen, MacData)
+ *
+ * MacKey = H(K || "JPAKE_KC")
+ *
+ * MacData = "KC_1_U" || participantId || partnerParticipantId || gx1 || gx2 || gx3 || gx4
+ *
+ * Note that both participants use "KC_1_U" because the sender of the round 3 message
+ * is always the initiator for key confirmation.
+ *
+ * HMAC = {@link HMac} used with the given {@link Digest}
+ * H = The given {@link Digest}</li>
+ * MacLen = length of MacTag
+ * </pre>
+ * <p/>
+ */
+ public static BigInteger calculateMacTag(
+ String participantId,
+ String partnerParticipantId,
+ BigInteger gx1,
+ BigInteger gx2,
+ BigInteger gx3,
+ BigInteger gx4,
+ BigInteger keyingMaterial,
+ Digest digest)
+ {
+ byte[] macKey = calculateMacKey(
+ keyingMaterial,
+ digest);
+
+ HMac mac = new HMac(digest);
+ byte[] macOutput = new byte[mac.getMacSize()];
+ mac.init(new KeyParameter(macKey));
+
+ /*
+ * MacData = "KC_1_U" || participantId_Alice || participantId_Bob || gx1 || gx2 || gx3 || gx4.
+ */
+ updateMac(mac, "KC_1_U");
+ updateMac(mac, participantId);
+ updateMac(mac, partnerParticipantId);
+ updateMac(mac, gx1);
+ updateMac(mac, gx2);
+ updateMac(mac, gx3);
+ updateMac(mac, gx4);
+
+ mac.doFinal(macOutput, 0);
+
+ Arrays.fill(macKey, (byte)0);
+
+ return new BigInteger(macOutput);
+
+ }
+
+ /**
+ * Calculates the MacKey (i.e. the key to use when calculating the MagTag for key confirmation).
+ * <p/>
+ * <p/>
+ * <pre>
+ * MacKey = H(K || "JPAKE_KC")
+ * </pre>
+ */
+ private static byte[] calculateMacKey(BigInteger keyingMaterial, Digest digest)
+ {
+ digest.reset();
+
+ updateDigest(digest, keyingMaterial);
+ /*
+ * This constant is used to ensure that the macKey is NOT the same as the derived key.
+ */
+ updateDigest(digest, "JPAKE_KC");
+
+ byte[] output = new byte[digest.getDigestSize()];
+ digest.doFinal(output, 0);
+
+ return output;
+ }
+
+ /**
+ * Validates the MacTag received from the partner participant.
+ * <p/>
+ *
+ * @param partnerMacTag the MacTag received from the partner.
+ * @throws CryptoException if the participantId strings are equal.
+ */
+ public static void validateMacTag(
+ String participantId,
+ String partnerParticipantId,
+ BigInteger gx1,
+ BigInteger gx2,
+ BigInteger gx3,
+ BigInteger gx4,
+ BigInteger keyingMaterial,
+ Digest digest,
+ BigInteger partnerMacTag)
+ throws CryptoException
+ {
+ /*
+ * Calculate the expected MacTag using the parameters as the partner
+ * would have used when the partner called calculateMacTag.
+ *
+ * i.e. basically all the parameters are reversed.
+ * participantId <-> partnerParticipantId
+ * x1 <-> x3
+ * x2 <-> x4
+ */
+ BigInteger expectedMacTag = calculateMacTag(
+ partnerParticipantId,
+ participantId,
+ gx3,
+ gx4,
+ gx1,
+ gx2,
+ keyingMaterial,
+ digest);
+
+ if (!expectedMacTag.equals(partnerMacTag))
+ {
+ throw new CryptoException(
+ "Partner MacTag validation failed. "
+ + "Therefore, the password, MAC, or digest algorithm of each participant does not match.");
+ }
+ }
+
+ private static void updateDigest(Digest digest, BigInteger bigInteger)
+ {
+ byte[] byteArray = BigIntegers.asUnsignedByteArray(bigInteger);
+ digest.update(byteArray, 0, byteArray.length);
+ Arrays.fill(byteArray, (byte)0);
+ }
+
+ private static void updateDigestIncludingSize(Digest digest, BigInteger bigInteger)
+ {
+ byte[] byteArray = BigIntegers.asUnsignedByteArray(bigInteger);
+ digest.update(intToByteArray(byteArray.length), 0, 4);
+ digest.update(byteArray, 0, byteArray.length);
+ Arrays.fill(byteArray, (byte)0);
+ }
+
+ private static void updateDigest(Digest digest, String string)
+ {
+ byte[] byteArray = Strings.toUTF8ByteArray(string);
+ digest.update(byteArray, 0, byteArray.length);
+ Arrays.fill(byteArray, (byte)0);
+ }
+
+ private static void updateDigestIncludingSize(Digest digest, String string)
+ {
+ byte[] byteArray = Strings.toUTF8ByteArray(string);
+ digest.update(intToByteArray(byteArray.length), 0, 4);
+ digest.update(byteArray, 0, byteArray.length);
+ Arrays.fill(byteArray, (byte)0);
+ }
+
+ private static void updateMac(Mac mac, BigInteger bigInteger)
+ {
+ byte[] byteArray = BigIntegers.asUnsignedByteArray(bigInteger);
+ mac.update(byteArray, 0, byteArray.length);
+ Arrays.fill(byteArray, (byte)0);
+ }
+
+ private static void updateMac(Mac mac, String string)
+ {
+ byte[] byteArray = Strings.toUTF8ByteArray(string);
+ mac.update(byteArray, 0, byteArray.length);
+ Arrays.fill(byteArray, (byte)0);
+ }
+
+ private static byte[] intToByteArray(int value)
+ {
+ return new byte[]{
+ (byte)(value >>> 24),
+ (byte)(value >>> 16),
+ (byte)(value >>> 8),
+ (byte)value
+ };
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKDFParameters.java b/core/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKDFParameters.java
new file mode 100644
index 00000000..ae551dd5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKDFParameters.java
@@ -0,0 +1,54 @@
+package org.bouncycastle.crypto.agreement.kdf;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.crypto.DerivationParameters;
+
+public class DHKDFParameters
+ implements DerivationParameters
+{
+ private ASN1ObjectIdentifier algorithm;
+ private int keySize;
+ private byte[] z;
+ private byte[] extraInfo;
+
+ public DHKDFParameters(
+ DERObjectIdentifier algorithm,
+ int keySize,
+ byte[] z)
+ {
+ this(algorithm, keySize, z, null);
+ }
+
+ public DHKDFParameters(
+ DERObjectIdentifier algorithm,
+ int keySize,
+ byte[] z,
+ byte[] extraInfo)
+ {
+ this.algorithm = new ASN1ObjectIdentifier(algorithm.getId());
+ this.keySize = keySize;
+ this.z = z;
+ this.extraInfo = extraInfo;
+ }
+
+ public ASN1ObjectIdentifier getAlgorithm()
+ {
+ return algorithm;
+ }
+
+ public int getKeySize()
+ {
+ return keySize;
+ }
+
+ public byte[] getZ()
+ {
+ return z;
+ }
+
+ public byte[] getExtraInfo()
+ {
+ return extraInfo;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKEKGenerator.java b/core/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKEKGenerator.java
new file mode 100644
index 00000000..947bc5c4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKEKGenerator.java
@@ -0,0 +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;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java b/core/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java
new file mode 100644
index 00000000..68039533
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java
@@ -0,0 +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);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Client.java b/core/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Client.java
new file mode 100644
index 00000000..4df90236
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Client.java
@@ -0,0 +1,93 @@
+package org.bouncycastle.crypto.agreement.srp;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.Digest;
+
+/**
+ * Implements the client side SRP-6a protocol. Note that this class is stateful, and therefore NOT threadsafe.
+ * This implementation of SRP is based on the optimized message sequence put forth by Thomas Wu in the paper
+ * "SRP-6: Improvements and Refinements to the Secure Remote Password Protocol, 2002"
+ */
+public class SRP6Client
+{
+ protected BigInteger N;
+ protected BigInteger g;
+
+ protected BigInteger a;
+ protected BigInteger A;
+
+ protected BigInteger B;
+
+ protected BigInteger x;
+ protected BigInteger u;
+ protected BigInteger S;
+
+ protected Digest digest;
+ protected SecureRandom random;
+
+ public SRP6Client()
+ {
+ }
+
+ /**
+ * Initialises the client to begin new authentication attempt
+ * @param N The safe prime associated with the client's verifier
+ * @param g The group parameter associated with the client's verifier
+ * @param digest The digest algorithm associated with the client's verifier
+ * @param random For key generation
+ */
+ public void init(BigInteger N, BigInteger g, Digest digest, SecureRandom random)
+ {
+ this.N = N;
+ this.g = g;
+ this.digest = digest;
+ this.random = random;
+ }
+
+ /**
+ * Generates client's credentials given the client's salt, identity and password
+ * @param salt The salt used in the client's verifier.
+ * @param identity The user's identity (eg. username)
+ * @param password The user's password
+ * @return Client's public value to send to server
+ */
+ public BigInteger generateClientCredentials(byte[] salt, byte[] identity, byte[] password)
+ {
+ this.x = SRP6Util.calculateX(digest, N, salt, identity, password);
+ this.a = selectPrivateValue();
+ this.A = g.modPow(a, N);
+
+ return A;
+ }
+
+ /**
+ * Generates client's verification message given the server's credentials
+ * @param serverB The server's credentials
+ * @return Client's verification message for the server
+ * @throws CryptoException If server's credentials are invalid
+ */
+ public BigInteger calculateSecret(BigInteger serverB) throws CryptoException
+ {
+ this.B = SRP6Util.validatePublicValue(N, serverB);
+ this.u = SRP6Util.calculateU(digest, N, A, B);
+ this.S = calculateS();
+
+ return S;
+ }
+
+ protected BigInteger selectPrivateValue()
+ {
+ return SRP6Util.generatePrivateValue(digest, N, g, random);
+ }
+
+ private BigInteger calculateS()
+ {
+ BigInteger k = SRP6Util.calculateK(digest, N, g);
+ BigInteger exp = u.multiply(x).add(a);
+ BigInteger tmp = g.modPow(x, N).multiply(k).mod(N);
+ return B.subtract(tmp).mod(N).modPow(exp, N);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Server.java b/core/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Server.java
new file mode 100644
index 00000000..fb208388
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Server.java
@@ -0,0 +1,90 @@
+package org.bouncycastle.crypto.agreement.srp;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.Digest;
+
+/**
+ * Implements the server side SRP-6a protocol. Note that this class is stateful, and therefore NOT threadsafe.
+ * This implementation of SRP is based on the optimized message sequence put forth by Thomas Wu in the paper
+ * "SRP-6: Improvements and Refinements to the Secure Remote Password Protocol, 2002"
+ */
+public class SRP6Server
+{
+ protected BigInteger N;
+ protected BigInteger g;
+ protected BigInteger v;
+
+ protected SecureRandom random;
+ protected Digest digest;
+
+ protected BigInteger A;
+
+ protected BigInteger b;
+ protected BigInteger B;
+
+ protected BigInteger u;
+ protected BigInteger S;
+
+ public SRP6Server()
+ {
+ }
+
+ /**
+ * Initialises the server to accept a new client authentication attempt
+ * @param N The safe prime associated with the client's verifier
+ * @param g The group parameter associated with the client's verifier
+ * @param v The client's verifier
+ * @param digest The digest algorithm associated with the client's verifier
+ * @param random For key generation
+ */
+ public void init(BigInteger N, BigInteger g, BigInteger v, Digest digest, SecureRandom random)
+ {
+ this.N = N;
+ this.g = g;
+ this.v = v;
+
+ this.random = random;
+ this.digest = digest;
+ }
+
+ /**
+ * Generates the server's credentials that are to be sent to the client.
+ * @return The server's public value to the client
+ */
+ public BigInteger generateServerCredentials()
+ {
+ BigInteger k = SRP6Util.calculateK(digest, N, g);
+ this.b = selectPrivateValue();
+ this.B = k.multiply(v).mod(N).add(g.modPow(b, N)).mod(N);
+
+ return B;
+ }
+
+ /**
+ * Processes the client's credentials. If valid the shared secret is generated and returned.
+ * @param clientA The client's credentials
+ * @return A shared secret BigInteger
+ * @throws CryptoException If client's credentials are invalid
+ */
+ public BigInteger calculateSecret(BigInteger clientA) throws CryptoException
+ {
+ this.A = SRP6Util.validatePublicValue(N, clientA);
+ this.u = SRP6Util.calculateU(digest, N, A, B);
+ this.S = calculateS();
+
+ return S;
+ }
+
+ protected BigInteger selectPrivateValue()
+ {
+ return SRP6Util.generatePrivateValue(digest, N, g, random);
+ }
+
+ private BigInteger calculateS()
+ {
+ return v.modPow(u, N).multiply(A).mod(N).modPow(b, N);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Util.java b/core/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Util.java
new file mode 100644
index 00000000..ad5ceac8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Util.java
@@ -0,0 +1,91 @@
+package org.bouncycastle.crypto.agreement.srp;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.util.BigIntegers;
+
+public class SRP6Util
+{
+ private static BigInteger ZERO = BigInteger.valueOf(0);
+ private static BigInteger ONE = BigInteger.valueOf(1);
+
+ public static BigInteger calculateK(Digest digest, BigInteger N, BigInteger g)
+ {
+ return hashPaddedPair(digest, N, N, g);
+ }
+
+ public static BigInteger calculateU(Digest digest, BigInteger N, BigInteger A, BigInteger B)
+ {
+ return hashPaddedPair(digest, N, A, B);
+ }
+
+ public static BigInteger calculateX(Digest digest, BigInteger N, byte[] salt, byte[] identity, byte[] password)
+ {
+ byte[] output = new byte[digest.getDigestSize()];
+
+ digest.update(identity, 0, identity.length);
+ digest.update((byte)':');
+ digest.update(password, 0, password.length);
+ digest.doFinal(output, 0);
+
+ digest.update(salt, 0, salt.length);
+ digest.update(output, 0, output.length);
+ digest.doFinal(output, 0);
+
+ return new BigInteger(1, output);
+ }
+
+ public static BigInteger generatePrivateValue(Digest digest, BigInteger N, BigInteger g, SecureRandom random)
+ {
+ int minBits = Math.min(256, N.bitLength() / 2);
+ BigInteger min = ONE.shiftLeft(minBits - 1);
+ BigInteger max = N.subtract(ONE);
+
+ return BigIntegers.createRandomInRange(min, max, random);
+ }
+
+ public static BigInteger validatePublicValue(BigInteger N, BigInteger val)
+ throws CryptoException
+ {
+ val = val.mod(N);
+
+ // Check that val % N != 0
+ if (val.equals(ZERO))
+ {
+ throw new CryptoException("Invalid public value: 0");
+ }
+
+ return val;
+ }
+
+ private static BigInteger hashPaddedPair(Digest digest, BigInteger N, BigInteger n1, BigInteger n2)
+ {
+ int padLength = (N.bitLength() + 7) / 8;
+
+ byte[] n1_bytes = getPadded(n1, padLength);
+ byte[] n2_bytes = getPadded(n2, padLength);
+
+ digest.update(n1_bytes, 0, n1_bytes.length);
+ digest.update(n2_bytes, 0, n2_bytes.length);
+
+ byte[] output = new byte[digest.getDigestSize()];
+ digest.doFinal(output, 0);
+
+ return new BigInteger(1, output);
+ }
+
+ private static byte[] getPadded(BigInteger n, int length)
+ {
+ byte[] bs = BigIntegers.asUnsignedByteArray(n);
+ if (bs.length < length)
+ {
+ byte[] tmp = new byte[length];
+ System.arraycopy(bs, 0, tmp, length - bs.length, bs.length);
+ bs = tmp;
+ }
+ return bs;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6VerifierGenerator.java b/core/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6VerifierGenerator.java
new file mode 100644
index 00000000..631ecc6e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6VerifierGenerator.java
@@ -0,0 +1,47 @@
+package org.bouncycastle.crypto.agreement.srp;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.Digest;
+
+/**
+ * Generates new SRP verifier for user
+ */
+public class SRP6VerifierGenerator
+{
+ protected BigInteger N;
+ protected BigInteger g;
+ protected Digest digest;
+
+ public SRP6VerifierGenerator()
+ {
+ }
+
+ /**
+ * Initialises generator to create new verifiers
+ * @param N The safe prime to use (see DHParametersGenerator)
+ * @param g The group parameter to use (see DHParametersGenerator)
+ * @param digest The digest to use. The same digest type will need to be used later for the actual authentication
+ * attempt. Also note that the final session key size is dependent on the chosen digest.
+ */
+ public void init(BigInteger N, BigInteger g, Digest digest)
+ {
+ this.N = N;
+ this.g = g;
+ this.digest = digest;
+ }
+
+ /**
+ * Creates a new SRP verifier
+ * @param salt The salt to use, generally should be large and random
+ * @param identity The user's identifying information (eg. username)
+ * @param password The user's password
+ * @return A new verifier for use in future SRP authentication
+ */
+ public BigInteger generateVerifier(byte[] salt, byte[] identity, byte[] password)
+ {
+ BigInteger x = SRP6Util.calculateX(digest, N, salt, identity, password);
+
+ return g.modPow(x, N);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/commitments/HashCommitter.java b/core/src/main/java/org/bouncycastle/crypto/commitments/HashCommitter.java
new file mode 100644
index 00000000..1494c3ce
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/commitments/HashCommitter.java
@@ -0,0 +1,81 @@
+package org.bouncycastle.crypto.commitments;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.Commitment;
+import org.bouncycastle.crypto.Committer;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.ExtendedDigest;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A basic hash-committer as described in "Making Mix Nets Robust for Electronic Voting by Randomized Partial Checking",
+ * by Jakobsson, Juels, and Rivest (11th Usenix Security Symposium, 2002).
+ */
+public class HashCommitter
+ implements Committer
+{
+ private final Digest digest;
+ private final int byteLength;
+ private final SecureRandom random;
+
+ /**
+ * Base Constructor. The maximum message length that can be committed to is half the length of the internal
+ * block size for the digest (ExtendedDigest.getBlockLength()).
+ *
+ * @param digest digest to use for creating commitments.
+ * @param random source of randomness for generating secrets.
+ */
+ public HashCommitter(ExtendedDigest digest, SecureRandom random)
+ {
+ this.digest = digest;
+ this.byteLength = digest.getByteLength();
+ this.random = random;
+ }
+
+ /**
+ * Generate a commitment for the passed in message.
+ *
+ * @param message the message to be committed to,
+ * @return a Commitment
+ */
+ public Commitment commit(byte[] message)
+ {
+ if (message.length > byteLength / 2)
+ {
+ throw new DataLengthException("Message to be committed to too large for digest.");
+ }
+
+ byte[] w = new byte[byteLength - message.length];
+
+ random.nextBytes(w);
+
+ return new Commitment(w, calculateCommitment(w, message));
+ }
+
+ /**
+ * Return true if the passed in commitment represents a commitment to the passed in maessage.
+ *
+ * @param commitment a commitment previously generated.
+ * @param message the message that was expected to have been committed to.
+ * @return true if commitment matches message, false otherwise.
+ */
+ public boolean isRevealed(Commitment commitment, byte[] message)
+ {
+ byte[] calcCommitment = calculateCommitment(commitment.getSecret(), message);
+
+ return Arrays.constantTimeAreEqual(commitment.getCommitment(), calcCommitment);
+ }
+
+ private byte[] calculateCommitment(byte[] w, byte[] message)
+ {
+ byte[] commitment = new byte[digest.getDigestSize()];
+
+ digest.update(w, 0, w.length);
+ digest.update(message, 0, message.length);
+ digest.doFinal(commitment, 0);
+
+ return commitment;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/GOST3411Digest.java b/core/src/main/java/org/bouncycastle/crypto/digests/GOST3411Digest.java
new file mode 100644
index 00000000..38a52aab
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/GOST3411Digest.java
@@ -0,0 +1,362 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.ExtendedDigest;
+import org.bouncycastle.crypto.engines.GOST28147Engine;
+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, Memoable
+{
+ private static final int DIGEST_LENGTH = 32;
+
+ private byte[] H = new byte[32], L = new byte[32],
+ M = new byte[32], Sum = new byte[32];
+ private byte[][] C = new byte[4][32];
+
+ private byte[] xBuf = new byte[32];
+ private int xBufOff;
+ private long byteCount;
+
+ private BlockCipher cipher = new GOST28147Engine();
+ private byte[] sBox;
+
+ /**
+ * Standard constructor
+ */
+ public GOST3411Digest()
+ {
+ sBox = GOST28147Engine.getSBox("D-A");
+ cipher.init(true, new ParametersWithSBox(null, sBox));
+
+ reset();
+ }
+
+ /**
+ * Constructor to allow use of a particular sbox with GOST28147
+ * @see GOST28147Engine#getSBox(String)
+ */
+ public GOST3411Digest(byte[] sBoxParam)
+ {
+ sBox = Arrays.clone(sBoxParam);
+ cipher.init(true, new ParametersWithSBox(null, sBox));
+
+ reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public GOST3411Digest(GOST3411Digest t)
+ {
+ reset(t);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "GOST3411";
+ }
+
+ public int getDigestSize()
+ {
+ return DIGEST_LENGTH;
+ }
+
+ public void update(byte in)
+ {
+ xBuf[xBufOff++] = in;
+ if (xBufOff == xBuf.length)
+ {
+ sumByteArray(xBuf); // calc sum M
+ processBlock(xBuf, 0);
+ xBufOff = 0;
+ }
+ byteCount++;
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ {
+ while ((xBufOff != 0) && (len > 0))
+ {
+ update(in[inOff]);
+ inOff++;
+ len--;
+ }
+
+ while (len > xBuf.length)
+ {
+ System.arraycopy(in, inOff, xBuf, 0, xBuf.length);
+
+ sumByteArray(xBuf); // calc sum M
+ processBlock(xBuf, 0);
+ inOff += xBuf.length;
+ len -= xBuf.length;
+ byteCount += xBuf.length;
+ }
+
+ // load in the remainder.
+ while (len > 0)
+ {
+ update(in[inOff]);
+ inOff++;
+ len--;
+ }
+ }
+
+ // (i + 1 + 4(k - 1)) = 8i + k i = 0-3, k = 1-8
+ private byte[] K = new byte[32];
+
+ private byte[] P(byte[] in)
+ {
+ for(int k = 0; k < 8; k++)
+ {
+ K[4*k] = in[k];
+ K[1 + 4*k] = in[ 8 + k];
+ K[2 + 4*k] = in[16 + k];
+ K[3 + 4*k] = in[24 + k];
+ }
+
+ return K;
+ }
+
+ //A (x) = (x0 ^ x1) || x3 || x2 || x1
+ byte[] a = new byte[8];
+ private byte[] A(byte[] in)
+ {
+ for(int j=0; j<8; j++)
+ {
+ a[j]=(byte)(in[j] ^ in[j+8]);
+ }
+
+ System.arraycopy(in, 8, in, 0, 24);
+ System.arraycopy(a, 0, in, 24, 8);
+
+ return in;
+ }
+
+ //Encrypt function, ECB mode
+ private void E(byte[] key, byte[] s, int sOff, byte[] in, int inOff)
+ {
+ cipher.init(true, new KeyParameter(key));
+
+ cipher.processBlock(in, inOff, s, sOff);
+ }
+
+ // (in:) n16||..||n1 ==> (out:) n1^n2^n3^n4^n13^n16||n16||..||n2
+ short[] wS = new short[16], w_S = new short[16];
+
+ private void fw(byte[] in)
+ {
+ cpyBytesToShort(in, wS);
+ w_S[15] = (short)(wS[0] ^ wS[1] ^ wS[2] ^ wS[3] ^ wS[12] ^ wS[15]);
+ System.arraycopy(wS, 1, w_S, 0, 15);
+ cpyShortToBytes(w_S, in);
+ }
+
+ // block processing
+ byte[] S = new byte[32];
+ byte[] U = new byte[32], V = new byte[32], W = new byte[32];
+
+ protected void processBlock(byte[] in, int inOff)
+ {
+ System.arraycopy(in, inOff, M, 0, 32);
+
+ //key step 1
+
+ // H = h3 || h2 || h1 || h0
+ // S = s3 || s2 || s1 || s0
+ System.arraycopy(H, 0, U, 0, 32);
+ System.arraycopy(M, 0, V, 0, 32);
+ for (int j=0; j<32; j++)
+ {
+ W[j] = (byte)(U[j]^V[j]);
+ }
+ // Encrypt gost28147-ECB
+ E(P(W), S, 0, H, 0); // s0 = EK0 [h0]
+
+ //keys step 2,3,4
+ for (int i=1; i<4; i++)
+ {
+ byte[] tmpA = A(U);
+ for (int j=0; j<32; j++)
+ {
+ U[j] = (byte)(tmpA[j] ^ C[i][j]);
+ }
+ V = A(A(V));
+ for (int j=0; j<32; j++)
+ {
+ W[j] = (byte)(U[j]^V[j]);
+ }
+ // Encrypt gost28147-ECB
+ E(P(W), S, i * 8, H, i * 8); // si = EKi [hi]
+ }
+
+ // x(M, H) = y61(H^y(M^y12(S)))
+ for(int n = 0; n < 12; n++)
+ {
+ fw(S);
+ }
+ for(int n = 0; n < 32; n++)
+ {
+ S[n] = (byte)(S[n] ^ M[n]);
+ }
+
+ fw(S);
+
+ for(int n = 0; n < 32; n++)
+ {
+ S[n] = (byte)(H[n] ^ S[n]);
+ }
+ for(int n = 0; n < 61; n++)
+ {
+ fw(S);
+ }
+ System.arraycopy(S, 0, H, 0, H.length);
+ }
+
+ private void finish()
+ {
+ Pack.longToLittleEndian(byteCount * 8, L, 0); // get length into L (byteCount * 8 = bitCount)
+
+ while (xBufOff != 0)
+ {
+ update((byte)0);
+ }
+
+ processBlock(L, 0);
+ processBlock(Sum, 0);
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ finish();
+
+ System.arraycopy(H, 0, out, outOff, H.length);
+
+ reset();
+
+ return DIGEST_LENGTH;
+ }
+
+ /**
+ * reset the chaining variables to the IV values.
+ */
+ private static final byte[] C2 = {
+ 0x00,(byte)0xFF,0x00,(byte)0xFF,0x00,(byte)0xFF,0x00,(byte)0xFF,
+ (byte)0xFF,0x00,(byte)0xFF,0x00,(byte)0xFF,0x00,(byte)0xFF,0x00,
+ 0x00,(byte)0xFF,(byte)0xFF,0x00,(byte)0xFF,0x00,0x00,(byte)0xFF,
+ (byte)0xFF,0x00,0x00,0x00,(byte)0xFF,(byte)0xFF,0x00,(byte)0xFF};
+
+ public void reset()
+ {
+ byteCount = 0;
+ xBufOff = 0;
+
+ for(int i=0; i<H.length; i++)
+ {
+ H[i] = 0; // start vector H
+ }
+ for(int i=0; i<L.length; i++)
+ {
+ L[i] = 0;
+ }
+ for(int i=0; i<M.length; i++)
+ {
+ M[i] = 0;
+ }
+ for(int i=0; i<C[1].length; i++)
+ {
+ C[1][i] = 0; // real index C = +1 because index array with 0.
+ }
+ for(int i=0; i<C[3].length; i++)
+ {
+ C[3][i] = 0;
+ }
+ for(int i=0; i<Sum.length; i++)
+ {
+ Sum[i] = 0;
+ }
+ for(int i = 0; i < xBuf.length; i++)
+ {
+ xBuf[i] = 0;
+ }
+
+ System.arraycopy(C2, 0, C[2], 0, C2.length);
+ }
+
+ // 256 bitsblock modul -> (Sum + a mod (2^256))
+ private void sumByteArray(byte[] in)
+ {
+ int carry = 0;
+
+ for (int i = 0; i != Sum.length; i++)
+ {
+ int sum = (Sum[i] & 0xff) + (in[i] & 0xff) + carry;
+
+ Sum[i] = (byte)sum;
+
+ carry = sum >>> 8;
+ }
+ }
+
+ private void cpyBytesToShort(byte[] S, short[] wS)
+ {
+ for(int i=0; i<S.length/2; i++)
+ {
+ wS[i] = (short)(((S[i*2+1]<<8)&0xFF00)|(S[i*2]&0xFF));
+ }
+ }
+
+ private void cpyShortToBytes(short[] wS, byte[] S)
+ {
+ for(int i=0; i<S.length/2; i++)
+ {
+ S[i*2 + 1] = (byte)(wS[i] >> 8);
+ S[i*2] = (byte)wS[i];
+ }
+ }
+
+ public int getByteLength()
+ {
+ 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/core/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java
new file mode 100644
index 00000000..15f3ebbd
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java
@@ -0,0 +1,142 @@
+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, Memoable
+{
+ private static final int BYTE_LENGTH = 64;
+ private byte[] xBuf;
+ private int xBufOff;
+
+ private long byteCount;
+
+ /**
+ * Standard constructor
+ */
+ protected GeneralDigest()
+ {
+ xBuf = new byte[4];
+ xBufOff = 0;
+ }
+
+ /**
+ * Copy constructor. We are using copy constructors in place
+ * of the Object.clone() interface as this interface is not
+ * supported by J2ME.
+ */
+ 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;
+ byteCount = t.byteCount;
+ }
+
+ public void update(
+ byte in)
+ {
+ xBuf[xBufOff++] = in;
+
+ if (xBufOff == xBuf.length)
+ {
+ processWord(xBuf, 0);
+ xBufOff = 0;
+ }
+
+ byteCount++;
+ }
+
+ public void update(
+ byte[] in,
+ int inOff,
+ int len)
+ {
+ //
+ // fill the current word
+ //
+ while ((xBufOff != 0) && (len > 0))
+ {
+ update(in[inOff]);
+
+ inOff++;
+ len--;
+ }
+
+ //
+ // process whole words.
+ //
+ while (len > xBuf.length)
+ {
+ processWord(in, inOff);
+
+ inOff += xBuf.length;
+ len -= xBuf.length;
+ byteCount += xBuf.length;
+ }
+
+ //
+ // load in the remainder.
+ //
+ while (len > 0)
+ {
+ update(in[inOff]);
+
+ inOff++;
+ len--;
+ }
+ }
+
+ public void finish()
+ {
+ long bitLength = (byteCount << 3);
+
+ //
+ // add the pad bytes.
+ //
+ update((byte)128);
+
+ while (xBufOff != 0)
+ {
+ update((byte)0);
+ }
+
+ processLength(bitLength);
+
+ processBlock();
+ }
+
+ public void reset()
+ {
+ byteCount = 0;
+
+ xBufOff = 0;
+ for (int i = 0; i < xBuf.length; i++)
+ {
+ xBuf[i] = 0;
+ }
+ }
+
+ public int getByteLength()
+ {
+ return BYTE_LENGTH;
+ }
+
+ protected abstract void processWord(byte[] in, int inOff);
+
+ protected abstract void processLength(long bitLength);
+
+ protected abstract void processBlock();
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java
new file mode 100644
index 00000000..5c79e4ee
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java
@@ -0,0 +1,361 @@
+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, Memoable
+{
+ private static final int BYTE_LENGTH = 128;
+
+ private byte[] xBuf;
+ private int xBufOff;
+
+ private long byteCount1;
+ private long byteCount2;
+
+ protected long H1, H2, H3, H4, H5, H6, H7, H8;
+
+ private long[] W = new long[80];
+ private int wOff;
+
+ /**
+ * Constructor for variable length word
+ */
+ protected LongDigest()
+ {
+ xBuf = new byte[8];
+ xBufOff = 0;
+
+ reset();
+ }
+
+ /**
+ * Copy constructor. We are using copy constructors in place
+ * of the Object.clone() interface as this interface is not
+ * supported by J2ME.
+ */
+ 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;
+ byteCount1 = t.byteCount1;
+ byteCount2 = t.byteCount2;
+
+ H1 = t.H1;
+ H2 = t.H2;
+ H3 = t.H3;
+ H4 = t.H4;
+ H5 = t.H5;
+ H6 = t.H6;
+ H7 = t.H7;
+ H8 = t.H8;
+
+ System.arraycopy(t.W, 0, W, 0, t.W.length);
+ wOff = t.wOff;
+ }
+
+ public void update(
+ byte in)
+ {
+ xBuf[xBufOff++] = in;
+
+ if (xBufOff == xBuf.length)
+ {
+ processWord(xBuf, 0);
+ xBufOff = 0;
+ }
+
+ byteCount1++;
+ }
+
+ public void update(
+ byte[] in,
+ int inOff,
+ int len)
+ {
+ //
+ // fill the current word
+ //
+ while ((xBufOff != 0) && (len > 0))
+ {
+ update(in[inOff]);
+
+ inOff++;
+ len--;
+ }
+
+ //
+ // process whole words.
+ //
+ while (len > xBuf.length)
+ {
+ processWord(in, inOff);
+
+ inOff += xBuf.length;
+ len -= xBuf.length;
+ byteCount1 += xBuf.length;
+ }
+
+ //
+ // load in the remainder.
+ //
+ while (len > 0)
+ {
+ update(in[inOff]);
+
+ inOff++;
+ len--;
+ }
+ }
+
+ public void finish()
+ {
+ adjustByteCounts();
+
+ long lowBitLength = byteCount1 << 3;
+ long hiBitLength = byteCount2;
+
+ //
+ // add the pad bytes.
+ //
+ update((byte)128);
+
+ while (xBufOff != 0)
+ {
+ update((byte)0);
+ }
+
+ processLength(lowBitLength, hiBitLength);
+
+ processBlock();
+ }
+
+ public void reset()
+ {
+ byteCount1 = 0;
+ byteCount2 = 0;
+
+ xBufOff = 0;
+ for (int i = 0; i < xBuf.length; i++)
+ {
+ xBuf[i] = 0;
+ }
+
+ wOff = 0;
+ for (int i = 0; i != W.length; i++)
+ {
+ W[i] = 0;
+ }
+ }
+
+ public int getByteLength()
+ {
+ return BYTE_LENGTH;
+ }
+
+ protected void processWord(
+ byte[] in,
+ int inOff)
+ {
+ W[wOff] = Pack.bigEndianToLong(in, inOff);
+
+ if (++wOff == 16)
+ {
+ processBlock();
+ }
+ }
+
+ /**
+ * adjust the byte counts so that byteCount2 represents the
+ * upper long (less 3 bits) word of the byte count.
+ */
+ private void adjustByteCounts()
+ {
+ if (byteCount1 > 0x1fffffffffffffffL)
+ {
+ byteCount2 += (byteCount1 >>> 61);
+ byteCount1 &= 0x1fffffffffffffffL;
+ }
+ }
+
+ protected void processLength(
+ long lowW,
+ long hiW)
+ {
+ if (wOff > 14)
+ {
+ processBlock();
+ }
+
+ W[14] = hiW;
+ W[15] = lowW;
+ }
+
+ protected void processBlock()
+ {
+ adjustByteCounts();
+
+ //
+ // expand 16 word block into 80 word blocks.
+ //
+ for (int t = 16; t <= 79; t++)
+ {
+ W[t] = Sigma1(W[t - 2]) + W[t - 7] + Sigma0(W[t - 15]) + W[t - 16];
+ }
+
+ //
+ // set up working variables.
+ //
+ long a = H1;
+ long b = H2;
+ long c = H3;
+ long d = H4;
+ long e = H5;
+ long f = H6;
+ long g = H7;
+ long h = H8;
+
+ int t = 0;
+ for(int i = 0; i < 10; i ++)
+ {
+ // t = 8 * i
+ h += Sum1(e) + Ch(e, f, g) + K[t] + W[t++];
+ d += h;
+ h += Sum0(a) + Maj(a, b, c);
+
+ // t = 8 * i + 1
+ g += Sum1(d) + Ch(d, e, f) + K[t] + W[t++];
+ c += g;
+ g += Sum0(h) + Maj(h, a, b);
+
+ // t = 8 * i + 2
+ f += Sum1(c) + Ch(c, d, e) + K[t] + W[t++];
+ b += f;
+ f += Sum0(g) + Maj(g, h, a);
+
+ // t = 8 * i + 3
+ e += Sum1(b) + Ch(b, c, d) + K[t] + W[t++];
+ a += e;
+ e += Sum0(f) + Maj(f, g, h);
+
+ // t = 8 * i + 4
+ d += Sum1(a) + Ch(a, b, c) + K[t] + W[t++];
+ h += d;
+ d += Sum0(e) + Maj(e, f, g);
+
+ // t = 8 * i + 5
+ c += Sum1(h) + Ch(h, a, b) + K[t] + W[t++];
+ g += c;
+ c += Sum0(d) + Maj(d, e, f);
+
+ // t = 8 * i + 6
+ b += Sum1(g) + Ch(g, h, a) + K[t] + W[t++];
+ f += b;
+ b += Sum0(c) + Maj(c, d, e);
+
+ // t = 8 * i + 7
+ a += Sum1(f) + Ch(f, g, h) + K[t] + W[t++];
+ e += a;
+ a += Sum0(b) + Maj(b, c, d);
+ }
+
+ H1 += a;
+ H2 += b;
+ H3 += c;
+ H4 += d;
+ H5 += e;
+ H6 += f;
+ H7 += g;
+ H8 += h;
+
+ //
+ // reset the offset and clean out the word buffer.
+ //
+ wOff = 0;
+ for (int i = 0; i < 16; i++)
+ {
+ W[i] = 0;
+ }
+ }
+
+ /* SHA-384 and SHA-512 functions (as for SHA-256 but for longs) */
+ private long Ch(
+ long x,
+ long y,
+ long z)
+ {
+ return ((x & y) ^ ((~x) & z));
+ }
+
+ private long Maj(
+ long x,
+ long y,
+ long z)
+ {
+ return ((x & y) ^ (x & z) ^ (y & z));
+ }
+
+ private long Sum0(
+ long x)
+ {
+ return ((x << 36)|(x >>> 28)) ^ ((x << 30)|(x >>> 34)) ^ ((x << 25)|(x >>> 39));
+ }
+
+ private long Sum1(
+ long x)
+ {
+ return ((x << 50)|(x >>> 14)) ^ ((x << 46)|(x >>> 18)) ^ ((x << 23)|(x >>> 41));
+ }
+
+ private long Sigma0(
+ long x)
+ {
+ return ((x << 63)|(x >>> 1)) ^ ((x << 56)|(x >>> 8)) ^ (x >>> 7);
+ }
+
+ private long Sigma1(
+ long x)
+ {
+ return ((x << 45)|(x >>> 19)) ^ ((x << 3)|(x >>> 61)) ^ (x >>> 6);
+ }
+
+ /* SHA-384 and SHA-512 Constants
+ * (represent the first 64 bits of the fractional parts of the
+ * cube roots of the first sixty-four prime numbers)
+ */
+ static final long K[] = {
+0x428a2f98d728ae22L, 0x7137449123ef65cdL, 0xb5c0fbcfec4d3b2fL, 0xe9b5dba58189dbbcL,
+0x3956c25bf348b538L, 0x59f111f1b605d019L, 0x923f82a4af194f9bL, 0xab1c5ed5da6d8118L,
+0xd807aa98a3030242L, 0x12835b0145706fbeL, 0x243185be4ee4b28cL, 0x550c7dc3d5ffb4e2L,
+0x72be5d74f27b896fL, 0x80deb1fe3b1696b1L, 0x9bdc06a725c71235L, 0xc19bf174cf692694L,
+0xe49b69c19ef14ad2L, 0xefbe4786384f25e3L, 0x0fc19dc68b8cd5b5L, 0x240ca1cc77ac9c65L,
+0x2de92c6f592b0275L, 0x4a7484aa6ea6e483L, 0x5cb0a9dcbd41fbd4L, 0x76f988da831153b5L,
+0x983e5152ee66dfabL, 0xa831c66d2db43210L, 0xb00327c898fb213fL, 0xbf597fc7beef0ee4L,
+0xc6e00bf33da88fc2L, 0xd5a79147930aa725L, 0x06ca6351e003826fL, 0x142929670a0e6e70L,
+0x27b70a8546d22ffcL, 0x2e1b21385c26c926L, 0x4d2c6dfc5ac42aedL, 0x53380d139d95b3dfL,
+0x650a73548baf63deL, 0x766a0abb3c77b2a8L, 0x81c2c92e47edaee6L, 0x92722c851482353bL,
+0xa2bfe8a14cf10364L, 0xa81a664bbc423001L, 0xc24b8b70d0f89791L, 0xc76c51a30654be30L,
+0xd192e819d6ef5218L, 0xd69906245565a910L, 0xf40e35855771202aL, 0x106aa07032bbd1b8L,
+0x19a4c116b8d2d0c8L, 0x1e376c085141ab53L, 0x2748774cdf8eeb99L, 0x34b0bcb5e19b48a8L,
+0x391c0cb3c5c95a63L, 0x4ed8aa4ae3418acbL, 0x5b9cca4f7763e373L, 0x682e6ff3d6b2b8a3L,
+0x748f82ee5defb2fcL, 0x78a5636f43172f60L, 0x84c87814a1f0ab72L, 0x8cc702081a6439ecL,
+0x90befffa23631e28L, 0xa4506cebde82bde9L, 0xbef9a3f7b2c67915L, 0xc67178f2e372532bL,
+0xca273eceea26619cL, 0xd186b8c721c0c207L, 0xeada7dd6cde0eb1eL, 0xf57d4f7fee6ed178L,
+0x06f067aa72176fbaL, 0x0a637dc5a2c898a6L, 0x113f9804bef90daeL, 0x1b710b35131c471bL,
+0x28db77f523047d84L, 0x32caab7b40c72493L, 0x3c9ebe0a15c9bebcL, 0x431d67c49c100d4cL,
+0x4cc5d4becb3e42b6L, 0x597f299cfc657e2aL, 0x5fcb6fab3ad6faecL, 0x6c44198c4a475817L
+ };
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/MD2Digest.java b/core/src/main/java/org/bouncycastle/crypto/digests/MD2Digest.java
new file mode 100644
index 00000000..f96b4a15
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/MD2Digest.java
@@ -0,0 +1,258 @@
+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, Memoable
+{
+ private static final int DIGEST_LENGTH = 16;
+
+ /* X buffer */
+ private byte[] X = new byte[48];
+ private int xOff;
+ /* M buffer */
+ private byte[] M = new byte[16];
+ private int mOff;
+ /* check sum */
+ private byte[] C = new byte[16];
+ private int COff;
+
+ public 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);
+ mOff = t.mOff;
+ System.arraycopy(t.C, 0, C, 0, t.C.length);
+ COff = t.COff;
+ }
+
+ /**
+ * return the algorithm name
+ *
+ * @return the algorithm name
+ */
+ public String getAlgorithmName()
+ {
+ return "MD2";
+ }
+ /**
+ * return the size, in bytes, of the digest produced by this message digest.
+ *
+ * @return the size, in bytes, of the digest produced by this message digest.
+ */
+ public int getDigestSize()
+ {
+ return DIGEST_LENGTH;
+ }
+ /**
+ * close the digest, producing the final digest value. The doFinal
+ * call leaves the digest reset.
+ *
+ * @param out the array the digest is to be copied into.
+ * @param outOff the offset into the out array the digest is to start at.
+ */
+ public int doFinal(byte[] out, int outOff)
+ {
+ // add padding
+ byte paddingByte = (byte)(M.length-mOff);
+ for (int i=mOff;i<M.length;i++)
+ {
+ M[i] = paddingByte;
+ }
+ //do final check sum
+ processCheckSum(M);
+ // do final block process
+ processBlock(M);
+
+ processBlock(C);
+
+ System.arraycopy(X,xOff,out,outOff,16);
+
+ reset();
+
+ return DIGEST_LENGTH;
+ }
+ /**
+ * reset the digest back to it's initial state.
+ */
+ public void reset()
+ {
+ xOff = 0;
+ for (int i = 0; i != X.length; i++)
+ {
+ X[i] = 0;
+ }
+ mOff = 0;
+ for (int i = 0; i != M.length; i++)
+ {
+ M[i] = 0;
+ }
+ COff = 0;
+ for (int i = 0; i != C.length; i++)
+ {
+ C[i] = 0;
+ }
+ }
+ /**
+ * update the message digest with a single byte.
+ *
+ * @param in the input byte to be entered.
+ */
+ public void update(byte in)
+ {
+ M[mOff++] = in;
+
+ if (mOff == 16)
+ {
+ processCheckSum(M);
+ processBlock(M);
+ mOff = 0;
+ }
+ }
+
+ /**
+ * update the message digest with a block of bytes.
+ *
+ * @param in the byte array containing the data.
+ * @param inOff the offset into the byte array where the data starts.
+ * @param len the length of the data.
+ */
+ public void update(byte[] in, int inOff, int len)
+ {
+ //
+ // fill the current word
+ //
+ while ((mOff != 0) && (len > 0))
+ {
+ update(in[inOff]);
+ inOff++;
+ len--;
+ }
+
+ //
+ // process whole words.
+ //
+ while (len > 16)
+ {
+ System.arraycopy(in,inOff,M,0,16);
+ processCheckSum(M);
+ processBlock(M);
+ len -= 16;
+ inOff += 16;
+ }
+
+ //
+ // load in the remainder.
+ //
+ while (len > 0)
+ {
+ update(in[inOff]);
+ inOff++;
+ len--;
+ }
+ }
+ protected void processCheckSum(byte[] m)
+ {
+ int L = C[15];
+ for (int i=0;i<16;i++)
+ {
+ C[i] ^= S[(m[i] ^ L) & 0xff];
+ L = C[i];
+ }
+ }
+ protected void processBlock(byte[] m)
+ {
+ for (int i=0;i<16;i++)
+ {
+ X[i+16] = m[i];
+ X[i+32] = (byte)(m[i] ^ X[i]);
+ }
+ // encrypt block
+ int t = 0;
+
+ for (int j=0;j<18;j++)
+ {
+ for (int k=0;k<48;k++)
+ {
+ t = X[k] ^= S[t];
+ t = t & 0xff;
+ }
+ t = (t + j)%256;
+ }
+ }
+ // 256-byte random permutation constructed from the digits of PI
+ private static final byte[] S = {
+ (byte)41,(byte)46,(byte)67,(byte)201,(byte)162,(byte)216,(byte)124,
+ (byte)1,(byte)61,(byte)54,(byte)84,(byte)161,(byte)236,(byte)240,
+ (byte)6,(byte)19,(byte)98,(byte)167,(byte)5,(byte)243,(byte)192,
+ (byte)199,(byte)115,(byte)140,(byte)152,(byte)147,(byte)43,(byte)217,
+ (byte)188,(byte)76,(byte)130,(byte)202,(byte)30,(byte)155,(byte)87,
+ (byte)60,(byte)253,(byte)212,(byte)224,(byte)22,(byte)103,(byte)66,
+ (byte)111,(byte)24,(byte)138,(byte)23,(byte)229,(byte)18,(byte)190,
+ (byte)78,(byte)196,(byte)214,(byte)218,(byte)158,(byte)222,(byte)73,
+ (byte)160,(byte)251,(byte)245,(byte)142,(byte)187,(byte)47,(byte)238,
+ (byte)122,(byte)169,(byte)104,(byte)121,(byte)145,(byte)21,(byte)178,
+ (byte)7,(byte)63,(byte)148,(byte)194,(byte)16,(byte)137,(byte)11,
+ (byte)34,(byte)95,(byte)33,(byte)128,(byte)127,(byte)93,(byte)154,
+ (byte)90,(byte)144,(byte)50,(byte)39,(byte)53,(byte)62,(byte)204,
+ (byte)231,(byte)191,(byte)247,(byte)151,(byte)3,(byte)255,(byte)25,
+ (byte)48,(byte)179,(byte)72,(byte)165,(byte)181,(byte)209,(byte)215,
+ (byte)94,(byte)146,(byte)42,(byte)172,(byte)86,(byte)170,(byte)198,
+ (byte)79,(byte)184,(byte)56,(byte)210,(byte)150,(byte)164,(byte)125,
+ (byte)182,(byte)118,(byte)252,(byte)107,(byte)226,(byte)156,(byte)116,
+ (byte)4,(byte)241,(byte)69,(byte)157,(byte)112,(byte)89,(byte)100,
+ (byte)113,(byte)135,(byte)32,(byte)134,(byte)91,(byte)207,(byte)101,
+ (byte)230,(byte)45,(byte)168,(byte)2,(byte)27,(byte)96,(byte)37,
+ (byte)173,(byte)174,(byte)176,(byte)185,(byte)246,(byte)28,(byte)70,
+ (byte)97,(byte)105,(byte)52,(byte)64,(byte)126,(byte)15,(byte)85,
+ (byte)71,(byte)163,(byte)35,(byte)221,(byte)81,(byte)175,(byte)58,
+ (byte)195,(byte)92,(byte)249,(byte)206,(byte)186,(byte)197,(byte)234,
+ (byte)38,(byte)44,(byte)83,(byte)13,(byte)110,(byte)133,(byte)40,
+ (byte)132, 9,(byte)211,(byte)223,(byte)205,(byte)244,(byte)65,
+ (byte)129,(byte)77,(byte)82,(byte)106,(byte)220,(byte)55,(byte)200,
+ (byte)108,(byte)193,(byte)171,(byte)250,(byte)36,(byte)225,(byte)123,
+ (byte)8,(byte)12,(byte)189,(byte)177,(byte)74,(byte)120,(byte)136,
+ (byte)149,(byte)139,(byte)227,(byte)99,(byte)232,(byte)109,(byte)233,
+ (byte)203,(byte)213,(byte)254,(byte)59,(byte)0,(byte)29,(byte)57,
+ (byte)242,(byte)239,(byte)183,(byte)14,(byte)102,(byte)88,(byte)208,
+ (byte)228,(byte)166,(byte)119,(byte)114,(byte)248,(byte)235,(byte)117,
+ (byte)75,(byte)10,(byte)49,(byte)68,(byte)80,(byte)180,(byte)143,
+ (byte)237,(byte)31,(byte)26,(byte)219,(byte)153,(byte)141,(byte)51,
+ (byte)159,(byte)17,(byte)131,(byte)20
+ };
+
+ public int getByteLength()
+ {
+ return 16;
+ }
+
+ public Memoable copy()
+ {
+ return new MD2Digest(this);
+ }
+
+ public void reset(Memoable other)
+ {
+ MD2Digest d = (MD2Digest)other;
+
+ copyIn(d);
+ }
+}
+
+
diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/MD4Digest.java b/core/src/main/java/org/bouncycastle/crypto/digests/MD4Digest.java
new file mode 100644
index 00000000..68532bd2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/MD4Digest.java
@@ -0,0 +1,291 @@
+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.
+ * <p>
+ * <b>NOTE</b>: This algorithm is only included for backwards compatability
+ * with legacy applications, it's not secure, don't use it for anything new!
+ */
+public class MD4Digest
+ extends GeneralDigest
+{
+ private static final int DIGEST_LENGTH = 16;
+
+ private int H1, H2, H3, H4; // IV's
+
+ private int[] X = new int[16];
+ private int xOff;
+
+ /**
+ * Standard constructor
+ */
+ public MD4Digest()
+ {
+ reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public MD4Digest(MD4Digest t)
+ {
+ super(t);
+
+ copyIn(t);
+ }
+
+ private void copyIn(MD4Digest t)
+ {
+ super.copyIn(t);
+
+ H1 = t.H1;
+ H2 = t.H2;
+ H3 = t.H3;
+ H4 = t.H4;
+
+ System.arraycopy(t.X, 0, X, 0, t.X.length);
+ xOff = t.xOff;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "MD4";
+ }
+
+ public int getDigestSize()
+ {
+ return DIGEST_LENGTH;
+ }
+
+ protected void processWord(
+ byte[] in,
+ int inOff)
+ {
+ X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8)
+ | ((in[inOff + 2] & 0xff) << 16) | ((in[inOff + 3] & 0xff) << 24);
+
+ if (xOff == 16)
+ {
+ processBlock();
+ }
+ }
+
+ protected void processLength(
+ long bitLength)
+ {
+ if (xOff > 14)
+ {
+ processBlock();
+ }
+
+ X[14] = (int)(bitLength & 0xffffffff);
+ X[15] = (int)(bitLength >>> 32);
+ }
+
+ private void unpackWord(
+ int word,
+ byte[] out,
+ int outOff)
+ {
+ out[outOff] = (byte)word;
+ out[outOff + 1] = (byte)(word >>> 8);
+ out[outOff + 2] = (byte)(word >>> 16);
+ out[outOff + 3] = (byte)(word >>> 24);
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ finish();
+
+ unpackWord(H1, out, outOff);
+ unpackWord(H2, out, outOff + 4);
+ unpackWord(H3, out, outOff + 8);
+ unpackWord(H4, out, outOff + 12);
+
+ reset();
+
+ return DIGEST_LENGTH;
+ }
+
+ /**
+ * reset the chaining variables to the IV values.
+ */
+ public void reset()
+ {
+ super.reset();
+
+ H1 = 0x67452301;
+ H2 = 0xefcdab89;
+ H3 = 0x98badcfe;
+ H4 = 0x10325476;
+
+ xOff = 0;
+
+ for (int i = 0; i != X.length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ //
+ // round 1 left rotates
+ //
+ private static final int S11 = 3;
+ private static final int S12 = 7;
+ private static final int S13 = 11;
+ private static final int S14 = 19;
+
+ //
+ // round 2 left rotates
+ //
+ private static final int S21 = 3;
+ private static final int S22 = 5;
+ private static final int S23 = 9;
+ private static final int S24 = 13;
+
+ //
+ // round 3 left rotates
+ //
+ private static final int S31 = 3;
+ private static final int S32 = 9;
+ private static final int S33 = 11;
+ private static final int S34 = 15;
+
+ /*
+ * rotate int x left n bits.
+ */
+ private int rotateLeft(
+ int x,
+ int n)
+ {
+ return (x << n) | (x >>> (32 - n));
+ }
+
+ /*
+ * F, G, H and I are the basic MD4 functions.
+ */
+ private int F(
+ int u,
+ int v,
+ int w)
+ {
+ return (u & v) | (~u & w);
+ }
+
+ private int G(
+ int u,
+ int v,
+ int w)
+ {
+ return (u & v) | (u & w) | (v & w);
+ }
+
+ private int H(
+ int u,
+ int v,
+ int w)
+ {
+ return u ^ v ^ w;
+ }
+
+ protected void processBlock()
+ {
+ int a = H1;
+ int b = H2;
+ int c = H3;
+ int d = H4;
+
+ //
+ // Round 1 - F cycle, 16 times.
+ //
+ a = rotateLeft(a + F(b, c, d) + X[ 0], S11);
+ d = rotateLeft(d + F(a, b, c) + X[ 1], S12);
+ c = rotateLeft(c + F(d, a, b) + X[ 2], S13);
+ b = rotateLeft(b + F(c, d, a) + X[ 3], S14);
+ a = rotateLeft(a + F(b, c, d) + X[ 4], S11);
+ d = rotateLeft(d + F(a, b, c) + X[ 5], S12);
+ c = rotateLeft(c + F(d, a, b) + X[ 6], S13);
+ b = rotateLeft(b + F(c, d, a) + X[ 7], S14);
+ a = rotateLeft(a + F(b, c, d) + X[ 8], S11);
+ d = rotateLeft(d + F(a, b, c) + X[ 9], S12);
+ c = rotateLeft(c + F(d, a, b) + X[10], S13);
+ b = rotateLeft(b + F(c, d, a) + X[11], S14);
+ a = rotateLeft(a + F(b, c, d) + X[12], S11);
+ d = rotateLeft(d + F(a, b, c) + X[13], S12);
+ c = rotateLeft(c + F(d, a, b) + X[14], S13);
+ b = rotateLeft(b + F(c, d, a) + X[15], S14);
+
+ //
+ // Round 2 - G cycle, 16 times.
+ //
+ a = rotateLeft(a + G(b, c, d) + X[ 0] + 0x5a827999, S21);
+ d = rotateLeft(d + G(a, b, c) + X[ 4] + 0x5a827999, S22);
+ c = rotateLeft(c + G(d, a, b) + X[ 8] + 0x5a827999, S23);
+ b = rotateLeft(b + G(c, d, a) + X[12] + 0x5a827999, S24);
+ a = rotateLeft(a + G(b, c, d) + X[ 1] + 0x5a827999, S21);
+ d = rotateLeft(d + G(a, b, c) + X[ 5] + 0x5a827999, S22);
+ c = rotateLeft(c + G(d, a, b) + X[ 9] + 0x5a827999, S23);
+ b = rotateLeft(b + G(c, d, a) + X[13] + 0x5a827999, S24);
+ a = rotateLeft(a + G(b, c, d) + X[ 2] + 0x5a827999, S21);
+ d = rotateLeft(d + G(a, b, c) + X[ 6] + 0x5a827999, S22);
+ c = rotateLeft(c + G(d, a, b) + X[10] + 0x5a827999, S23);
+ b = rotateLeft(b + G(c, d, a) + X[14] + 0x5a827999, S24);
+ a = rotateLeft(a + G(b, c, d) + X[ 3] + 0x5a827999, S21);
+ d = rotateLeft(d + G(a, b, c) + X[ 7] + 0x5a827999, S22);
+ c = rotateLeft(c + G(d, a, b) + X[11] + 0x5a827999, S23);
+ b = rotateLeft(b + G(c, d, a) + X[15] + 0x5a827999, S24);
+
+ //
+ // Round 3 - H cycle, 16 times.
+ //
+ a = rotateLeft(a + H(b, c, d) + X[ 0] + 0x6ed9eba1, S31);
+ d = rotateLeft(d + H(a, b, c) + X[ 8] + 0x6ed9eba1, S32);
+ c = rotateLeft(c + H(d, a, b) + X[ 4] + 0x6ed9eba1, S33);
+ b = rotateLeft(b + H(c, d, a) + X[12] + 0x6ed9eba1, S34);
+ a = rotateLeft(a + H(b, c, d) + X[ 2] + 0x6ed9eba1, S31);
+ d = rotateLeft(d + H(a, b, c) + X[10] + 0x6ed9eba1, S32);
+ c = rotateLeft(c + H(d, a, b) + X[ 6] + 0x6ed9eba1, S33);
+ b = rotateLeft(b + H(c, d, a) + X[14] + 0x6ed9eba1, S34);
+ a = rotateLeft(a + H(b, c, d) + X[ 1] + 0x6ed9eba1, S31);
+ d = rotateLeft(d + H(a, b, c) + X[ 9] + 0x6ed9eba1, S32);
+ c = rotateLeft(c + H(d, a, b) + X[ 5] + 0x6ed9eba1, S33);
+ b = rotateLeft(b + H(c, d, a) + X[13] + 0x6ed9eba1, S34);
+ a = rotateLeft(a + H(b, c, d) + X[ 3] + 0x6ed9eba1, S31);
+ d = rotateLeft(d + H(a, b, c) + X[11] + 0x6ed9eba1, S32);
+ c = rotateLeft(c + H(d, a, b) + X[ 7] + 0x6ed9eba1, S33);
+ b = rotateLeft(b + H(c, d, a) + X[15] + 0x6ed9eba1, S34);
+
+ H1 += a;
+ H2 += b;
+ H3 += c;
+ H4 += d;
+
+ //
+ // reset the offset and clean out the word buffer.
+ //
+ xOff = 0;
+ for (int i = 0; i != X.length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ public Memoable copy()
+ {
+ return new MD4Digest(this);
+ }
+
+ public void reset(Memoable other)
+ {
+ MD4Digest d = (MD4Digest)other;
+
+ copyIn(d);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/MD5Digest.java b/core/src/main/java/org/bouncycastle/crypto/digests/MD5Digest.java
new file mode 100644
index 00000000..ff9cedf0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/MD5Digest.java
@@ -0,0 +1,323 @@
+package org.bouncycastle.crypto.digests;
+
+
+import org.bouncycastle.util.Memoable;
+
+/**
+ * implementation of MD5 as outlined in "Handbook of Applied Cryptography", pages 346 - 347.
+ */
+public class MD5Digest
+ extends GeneralDigest
+{
+ private static final int DIGEST_LENGTH = 16;
+
+ private int H1, H2, H3, H4; // IV's
+
+ private int[] X = new int[16];
+ private int xOff;
+
+ /**
+ * Standard constructor
+ */
+ public MD5Digest()
+ {
+ reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public MD5Digest(MD5Digest t)
+ {
+ super(t);
+
+ copyIn(t);
+ }
+
+ private void copyIn(MD5Digest t)
+ {
+ super.copyIn(t);
+
+ H1 = t.H1;
+ H2 = t.H2;
+ H3 = t.H3;
+ H4 = t.H4;
+
+ System.arraycopy(t.X, 0, X, 0, t.X.length);
+ xOff = t.xOff;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "MD5";
+ }
+
+ public int getDigestSize()
+ {
+ return DIGEST_LENGTH;
+ }
+
+ protected void processWord(
+ byte[] in,
+ int inOff)
+ {
+ X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8)
+ | ((in[inOff + 2] & 0xff) << 16) | ((in[inOff + 3] & 0xff) << 24);
+
+ if (xOff == 16)
+ {
+ processBlock();
+ }
+ }
+
+ protected void processLength(
+ long bitLength)
+ {
+ if (xOff > 14)
+ {
+ processBlock();
+ }
+
+ X[14] = (int)(bitLength & 0xffffffff);
+ X[15] = (int)(bitLength >>> 32);
+ }
+
+ private void unpackWord(
+ int word,
+ byte[] out,
+ int outOff)
+ {
+ out[outOff] = (byte)word;
+ out[outOff + 1] = (byte)(word >>> 8);
+ out[outOff + 2] = (byte)(word >>> 16);
+ out[outOff + 3] = (byte)(word >>> 24);
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ finish();
+
+ unpackWord(H1, out, outOff);
+ unpackWord(H2, out, outOff + 4);
+ unpackWord(H3, out, outOff + 8);
+ unpackWord(H4, out, outOff + 12);
+
+ reset();
+
+ return DIGEST_LENGTH;
+ }
+
+ /**
+ * reset the chaining variables to the IV values.
+ */
+ public void reset()
+ {
+ super.reset();
+
+ H1 = 0x67452301;
+ H2 = 0xefcdab89;
+ H3 = 0x98badcfe;
+ H4 = 0x10325476;
+
+ xOff = 0;
+
+ for (int i = 0; i != X.length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ //
+ // round 1 left rotates
+ //
+ private static final int S11 = 7;
+ private static final int S12 = 12;
+ private static final int S13 = 17;
+ private static final int S14 = 22;
+
+ //
+ // round 2 left rotates
+ //
+ private static final int S21 = 5;
+ private static final int S22 = 9;
+ private static final int S23 = 14;
+ private static final int S24 = 20;
+
+ //
+ // round 3 left rotates
+ //
+ private static final int S31 = 4;
+ private static final int S32 = 11;
+ private static final int S33 = 16;
+ private static final int S34 = 23;
+
+ //
+ // round 4 left rotates
+ //
+ private static final int S41 = 6;
+ private static final int S42 = 10;
+ private static final int S43 = 15;
+ private static final int S44 = 21;
+
+ /*
+ * rotate int x left n bits.
+ */
+ private int rotateLeft(
+ int x,
+ int n)
+ {
+ return (x << n) | (x >>> (32 - n));
+ }
+
+ /*
+ * F, G, H and I are the basic MD5 functions.
+ */
+ private int F(
+ int u,
+ int v,
+ int w)
+ {
+ return (u & v) | (~u & w);
+ }
+
+ private int G(
+ int u,
+ int v,
+ int w)
+ {
+ return (u & w) | (v & ~w);
+ }
+
+ private int H(
+ int u,
+ int v,
+ int w)
+ {
+ return u ^ v ^ w;
+ }
+
+ private int K(
+ int u,
+ int v,
+ int w)
+ {
+ return v ^ (u | ~w);
+ }
+
+ protected void processBlock()
+ {
+ int a = H1;
+ int b = H2;
+ int c = H3;
+ int d = H4;
+
+ //
+ // Round 1 - F cycle, 16 times.
+ //
+ a = rotateLeft(a + F(b, c, d) + X[ 0] + 0xd76aa478, S11) + b;
+ d = rotateLeft(d + F(a, b, c) + X[ 1] + 0xe8c7b756, S12) + a;
+ c = rotateLeft(c + F(d, a, b) + X[ 2] + 0x242070db, S13) + d;
+ b = rotateLeft(b + F(c, d, a) + X[ 3] + 0xc1bdceee, S14) + c;
+ a = rotateLeft(a + F(b, c, d) + X[ 4] + 0xf57c0faf, S11) + b;
+ d = rotateLeft(d + F(a, b, c) + X[ 5] + 0x4787c62a, S12) + a;
+ c = rotateLeft(c + F(d, a, b) + X[ 6] + 0xa8304613, S13) + d;
+ b = rotateLeft(b + F(c, d, a) + X[ 7] + 0xfd469501, S14) + c;
+ a = rotateLeft(a + F(b, c, d) + X[ 8] + 0x698098d8, S11) + b;
+ d = rotateLeft(d + F(a, b, c) + X[ 9] + 0x8b44f7af, S12) + a;
+ c = rotateLeft(c + F(d, a, b) + X[10] + 0xffff5bb1, S13) + d;
+ b = rotateLeft(b + F(c, d, a) + X[11] + 0x895cd7be, S14) + c;
+ a = rotateLeft(a + F(b, c, d) + X[12] + 0x6b901122, S11) + b;
+ d = rotateLeft(d + F(a, b, c) + X[13] + 0xfd987193, S12) + a;
+ c = rotateLeft(c + F(d, a, b) + X[14] + 0xa679438e, S13) + d;
+ b = rotateLeft(b + F(c, d, a) + X[15] + 0x49b40821, S14) + c;
+
+ //
+ // Round 2 - G cycle, 16 times.
+ //
+ a = rotateLeft(a + G(b, c, d) + X[ 1] + 0xf61e2562, S21) + b;
+ d = rotateLeft(d + G(a, b, c) + X[ 6] + 0xc040b340, S22) + a;
+ c = rotateLeft(c + G(d, a, b) + X[11] + 0x265e5a51, S23) + d;
+ b = rotateLeft(b + G(c, d, a) + X[ 0] + 0xe9b6c7aa, S24) + c;
+ a = rotateLeft(a + G(b, c, d) + X[ 5] + 0xd62f105d, S21) + b;
+ d = rotateLeft(d + G(a, b, c) + X[10] + 0x02441453, S22) + a;
+ c = rotateLeft(c + G(d, a, b) + X[15] + 0xd8a1e681, S23) + d;
+ b = rotateLeft(b + G(c, d, a) + X[ 4] + 0xe7d3fbc8, S24) + c;
+ a = rotateLeft(a + G(b, c, d) + X[ 9] + 0x21e1cde6, S21) + b;
+ d = rotateLeft(d + G(a, b, c) + X[14] + 0xc33707d6, S22) + a;
+ c = rotateLeft(c + G(d, a, b) + X[ 3] + 0xf4d50d87, S23) + d;
+ b = rotateLeft(b + G(c, d, a) + X[ 8] + 0x455a14ed, S24) + c;
+ a = rotateLeft(a + G(b, c, d) + X[13] + 0xa9e3e905, S21) + b;
+ d = rotateLeft(d + G(a, b, c) + X[ 2] + 0xfcefa3f8, S22) + a;
+ c = rotateLeft(c + G(d, a, b) + X[ 7] + 0x676f02d9, S23) + d;
+ b = rotateLeft(b + G(c, d, a) + X[12] + 0x8d2a4c8a, S24) + c;
+
+ //
+ // Round 3 - H cycle, 16 times.
+ //
+ a = rotateLeft(a + H(b, c, d) + X[ 5] + 0xfffa3942, S31) + b;
+ d = rotateLeft(d + H(a, b, c) + X[ 8] + 0x8771f681, S32) + a;
+ c = rotateLeft(c + H(d, a, b) + X[11] + 0x6d9d6122, S33) + d;
+ b = rotateLeft(b + H(c, d, a) + X[14] + 0xfde5380c, S34) + c;
+ a = rotateLeft(a + H(b, c, d) + X[ 1] + 0xa4beea44, S31) + b;
+ d = rotateLeft(d + H(a, b, c) + X[ 4] + 0x4bdecfa9, S32) + a;
+ c = rotateLeft(c + H(d, a, b) + X[ 7] + 0xf6bb4b60, S33) + d;
+ b = rotateLeft(b + H(c, d, a) + X[10] + 0xbebfbc70, S34) + c;
+ a = rotateLeft(a + H(b, c, d) + X[13] + 0x289b7ec6, S31) + b;
+ d = rotateLeft(d + H(a, b, c) + X[ 0] + 0xeaa127fa, S32) + a;
+ c = rotateLeft(c + H(d, a, b) + X[ 3] + 0xd4ef3085, S33) + d;
+ b = rotateLeft(b + H(c, d, a) + X[ 6] + 0x04881d05, S34) + c;
+ a = rotateLeft(a + H(b, c, d) + X[ 9] + 0xd9d4d039, S31) + b;
+ d = rotateLeft(d + H(a, b, c) + X[12] + 0xe6db99e5, S32) + a;
+ c = rotateLeft(c + H(d, a, b) + X[15] + 0x1fa27cf8, S33) + d;
+ b = rotateLeft(b + H(c, d, a) + X[ 2] + 0xc4ac5665, S34) + c;
+
+ //
+ // Round 4 - K cycle, 16 times.
+ //
+ a = rotateLeft(a + K(b, c, d) + X[ 0] + 0xf4292244, S41) + b;
+ d = rotateLeft(d + K(a, b, c) + X[ 7] + 0x432aff97, S42) + a;
+ c = rotateLeft(c + K(d, a, b) + X[14] + 0xab9423a7, S43) + d;
+ b = rotateLeft(b + K(c, d, a) + X[ 5] + 0xfc93a039, S44) + c;
+ a = rotateLeft(a + K(b, c, d) + X[12] + 0x655b59c3, S41) + b;
+ d = rotateLeft(d + K(a, b, c) + X[ 3] + 0x8f0ccc92, S42) + a;
+ c = rotateLeft(c + K(d, a, b) + X[10] + 0xffeff47d, S43) + d;
+ b = rotateLeft(b + K(c, d, a) + X[ 1] + 0x85845dd1, S44) + c;
+ a = rotateLeft(a + K(b, c, d) + X[ 8] + 0x6fa87e4f, S41) + b;
+ d = rotateLeft(d + K(a, b, c) + X[15] + 0xfe2ce6e0, S42) + a;
+ c = rotateLeft(c + K(d, a, b) + X[ 6] + 0xa3014314, S43) + d;
+ b = rotateLeft(b + K(c, d, a) + X[13] + 0x4e0811a1, S44) + c;
+ a = rotateLeft(a + K(b, c, d) + X[ 4] + 0xf7537e82, S41) + b;
+ d = rotateLeft(d + K(a, b, c) + X[11] + 0xbd3af235, S42) + a;
+ c = rotateLeft(c + K(d, a, b) + X[ 2] + 0x2ad7d2bb, S43) + d;
+ b = rotateLeft(b + K(c, d, a) + X[ 9] + 0xeb86d391, S44) + c;
+
+ H1 += a;
+ H2 += b;
+ H3 += c;
+ H4 += d;
+
+ //
+ // reset the offset and clean out the word buffer.
+ //
+ xOff = 0;
+ for (int i = 0; i != X.length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ public Memoable copy()
+ {
+ return new MD5Digest(this);
+ }
+
+ public void reset(Memoable other)
+ {
+ MD5Digest d = (MD5Digest)other;
+
+ copyIn(d);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/NonMemoableDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/NonMemoableDigest.java
new file mode 100644
index 00000000..87a4d249
--- /dev/null
+++ b/core/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/core/src/main/java/org/bouncycastle/crypto/digests/NullDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/NullDigest.java
new file mode 100644
index 00000000..6cb0d4ac
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/NullDigest.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.crypto.digests;
+
+import java.io.ByteArrayOutputStream;
+
+import org.bouncycastle.crypto.Digest;
+
+
+public class NullDigest
+ implements Digest
+{
+ private ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ public String getAlgorithmName()
+ {
+ return "NULL";
+ }
+
+ public int getDigestSize()
+ {
+ return bOut.size();
+ }
+
+ public void update(byte in)
+ {
+ bOut.write(in);
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ {
+ bOut.write(in, inOff, len);
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ {
+ byte[] res = bOut.toByteArray();
+
+ System.arraycopy(res, 0, out, outOff, res.length);
+
+ reset();
+
+ return res.length;
+ }
+
+ public void reset()
+ {
+ bOut.reset();
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/RIPEMD128Digest.java b/core/src/main/java/org/bouncycastle/crypto/digests/RIPEMD128Digest.java
new file mode 100644
index 00000000..ec7fa859
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/RIPEMD128Digest.java
@@ -0,0 +1,482 @@
+package org.bouncycastle.crypto.digests;
+
+
+import org.bouncycastle.util.Memoable;
+
+/**
+ * implementation of RIPEMD128
+ */
+public class RIPEMD128Digest
+ extends GeneralDigest
+{
+ private static final int DIGEST_LENGTH = 16;
+
+ private int H0, H1, H2, H3; // IV's
+
+ private int[] X = new int[16];
+ private int xOff;
+
+ /**
+ * Standard constructor
+ */
+ public RIPEMD128Digest()
+ {
+ reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public RIPEMD128Digest(RIPEMD128Digest t)
+ {
+ super(t);
+
+ copyIn(t);
+ }
+
+ private void copyIn(RIPEMD128Digest t)
+ {
+ super.copyIn(t);
+
+ H0 = t.H0;
+ H1 = t.H1;
+ H2 = t.H2;
+ H3 = t.H3;
+
+ System.arraycopy(t.X, 0, X, 0, t.X.length);
+ xOff = t.xOff;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "RIPEMD128";
+ }
+
+ public int getDigestSize()
+ {
+ return DIGEST_LENGTH;
+ }
+
+ protected void processWord(
+ byte[] in,
+ int inOff)
+ {
+ X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8)
+ | ((in[inOff + 2] & 0xff) << 16) | ((in[inOff + 3] & 0xff) << 24);
+
+ if (xOff == 16)
+ {
+ processBlock();
+ }
+ }
+
+ protected void processLength(
+ long bitLength)
+ {
+ if (xOff > 14)
+ {
+ processBlock();
+ }
+
+ X[14] = (int)(bitLength & 0xffffffff);
+ X[15] = (int)(bitLength >>> 32);
+ }
+
+ private void unpackWord(
+ int word,
+ byte[] out,
+ int outOff)
+ {
+ out[outOff] = (byte)word;
+ out[outOff + 1] = (byte)(word >>> 8);
+ out[outOff + 2] = (byte)(word >>> 16);
+ out[outOff + 3] = (byte)(word >>> 24);
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ finish();
+
+ unpackWord(H0, out, outOff);
+ unpackWord(H1, out, outOff + 4);
+ unpackWord(H2, out, outOff + 8);
+ unpackWord(H3, out, outOff + 12);
+
+ reset();
+
+ return DIGEST_LENGTH;
+ }
+
+ /**
+ * reset the chaining variables to the IV values.
+ */
+ public void reset()
+ {
+ super.reset();
+
+ H0 = 0x67452301;
+ H1 = 0xefcdab89;
+ H2 = 0x98badcfe;
+ H3 = 0x10325476;
+
+ xOff = 0;
+
+ for (int i = 0; i != X.length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ /*
+ * rotate int x left n bits.
+ */
+ private int RL(
+ int x,
+ int n)
+ {
+ return (x << n) | (x >>> (32 - n));
+ }
+
+ /*
+ * f1,f2,f3,f4 are the basic RIPEMD128 functions.
+ */
+
+ /*
+ * F
+ */
+ private int f1(
+ int x,
+ int y,
+ int z)
+ {
+ return x ^ y ^ z;
+ }
+
+ /*
+ * G
+ */
+ private int f2(
+ int x,
+ int y,
+ int z)
+ {
+ return (x & y) | (~x & z);
+ }
+
+ /*
+ * H
+ */
+ private int f3(
+ int x,
+ int y,
+ int z)
+ {
+ return (x | ~y) ^ z;
+ }
+
+ /*
+ * I
+ */
+ private int f4(
+ int x,
+ int y,
+ int z)
+ {
+ return (x & z) | (y & ~z);
+ }
+
+ private int F1(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + f1(b, c, d) + x, s);
+ }
+
+ private int F2(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + f2(b, c, d) + x + 0x5a827999, s);
+ }
+
+ private int F3(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + f3(b, c, d) + x + 0x6ed9eba1, s);
+ }
+
+ private int F4(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + f4(b, c, d) + x + 0x8f1bbcdc, s);
+ }
+
+ private int FF1(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + f1(b, c, d) + x, s);
+ }
+
+ private int FF2(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + f2(b, c, d) + x + 0x6d703ef3, s);
+ }
+
+ private int FF3(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + f3(b, c, d) + x + 0x5c4dd124, s);
+ }
+
+ private int FF4(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + f4(b, c, d) + x + 0x50a28be6, s);
+ }
+
+ protected void processBlock()
+ {
+ int a, aa;
+ int b, bb;
+ int c, cc;
+ int d, dd;
+
+ a = aa = H0;
+ b = bb = H1;
+ c = cc = H2;
+ d = dd = H3;
+
+ //
+ // Round 1
+ //
+ a = F1(a, b, c, d, X[ 0], 11);
+ d = F1(d, a, b, c, X[ 1], 14);
+ c = F1(c, d, a, b, X[ 2], 15);
+ b = F1(b, c, d, a, X[ 3], 12);
+ a = F1(a, b, c, d, X[ 4], 5);
+ d = F1(d, a, b, c, X[ 5], 8);
+ c = F1(c, d, a, b, X[ 6], 7);
+ b = F1(b, c, d, a, X[ 7], 9);
+ a = F1(a, b, c, d, X[ 8], 11);
+ d = F1(d, a, b, c, X[ 9], 13);
+ c = F1(c, d, a, b, X[10], 14);
+ b = F1(b, c, d, a, X[11], 15);
+ a = F1(a, b, c, d, X[12], 6);
+ d = F1(d, a, b, c, X[13], 7);
+ c = F1(c, d, a, b, X[14], 9);
+ b = F1(b, c, d, a, X[15], 8);
+
+ //
+ // Round 2
+ //
+ a = F2(a, b, c, d, X[ 7], 7);
+ d = F2(d, a, b, c, X[ 4], 6);
+ c = F2(c, d, a, b, X[13], 8);
+ b = F2(b, c, d, a, X[ 1], 13);
+ a = F2(a, b, c, d, X[10], 11);
+ d = F2(d, a, b, c, X[ 6], 9);
+ c = F2(c, d, a, b, X[15], 7);
+ b = F2(b, c, d, a, X[ 3], 15);
+ a = F2(a, b, c, d, X[12], 7);
+ d = F2(d, a, b, c, X[ 0], 12);
+ c = F2(c, d, a, b, X[ 9], 15);
+ b = F2(b, c, d, a, X[ 5], 9);
+ a = F2(a, b, c, d, X[ 2], 11);
+ d = F2(d, a, b, c, X[14], 7);
+ c = F2(c, d, a, b, X[11], 13);
+ b = F2(b, c, d, a, X[ 8], 12);
+
+ //
+ // Round 3
+ //
+ a = F3(a, b, c, d, X[ 3], 11);
+ d = F3(d, a, b, c, X[10], 13);
+ c = F3(c, d, a, b, X[14], 6);
+ b = F3(b, c, d, a, X[ 4], 7);
+ a = F3(a, b, c, d, X[ 9], 14);
+ d = F3(d, a, b, c, X[15], 9);
+ c = F3(c, d, a, b, X[ 8], 13);
+ b = F3(b, c, d, a, X[ 1], 15);
+ a = F3(a, b, c, d, X[ 2], 14);
+ d = F3(d, a, b, c, X[ 7], 8);
+ c = F3(c, d, a, b, X[ 0], 13);
+ b = F3(b, c, d, a, X[ 6], 6);
+ a = F3(a, b, c, d, X[13], 5);
+ d = F3(d, a, b, c, X[11], 12);
+ c = F3(c, d, a, b, X[ 5], 7);
+ b = F3(b, c, d, a, X[12], 5);
+
+ //
+ // Round 4
+ //
+ a = F4(a, b, c, d, X[ 1], 11);
+ d = F4(d, a, b, c, X[ 9], 12);
+ c = F4(c, d, a, b, X[11], 14);
+ b = F4(b, c, d, a, X[10], 15);
+ a = F4(a, b, c, d, X[ 0], 14);
+ d = F4(d, a, b, c, X[ 8], 15);
+ c = F4(c, d, a, b, X[12], 9);
+ b = F4(b, c, d, a, X[ 4], 8);
+ a = F4(a, b, c, d, X[13], 9);
+ d = F4(d, a, b, c, X[ 3], 14);
+ c = F4(c, d, a, b, X[ 7], 5);
+ b = F4(b, c, d, a, X[15], 6);
+ a = F4(a, b, c, d, X[14], 8);
+ d = F4(d, a, b, c, X[ 5], 6);
+ c = F4(c, d, a, b, X[ 6], 5);
+ b = F4(b, c, d, a, X[ 2], 12);
+
+ //
+ // Parallel round 1
+ //
+ aa = FF4(aa, bb, cc, dd, X[ 5], 8);
+ dd = FF4(dd, aa, bb, cc, X[14], 9);
+ cc = FF4(cc, dd, aa, bb, X[ 7], 9);
+ bb = FF4(bb, cc, dd, aa, X[ 0], 11);
+ aa = FF4(aa, bb, cc, dd, X[ 9], 13);
+ dd = FF4(dd, aa, bb, cc, X[ 2], 15);
+ cc = FF4(cc, dd, aa, bb, X[11], 15);
+ bb = FF4(bb, cc, dd, aa, X[ 4], 5);
+ aa = FF4(aa, bb, cc, dd, X[13], 7);
+ dd = FF4(dd, aa, bb, cc, X[ 6], 7);
+ cc = FF4(cc, dd, aa, bb, X[15], 8);
+ bb = FF4(bb, cc, dd, aa, X[ 8], 11);
+ aa = FF4(aa, bb, cc, dd, X[ 1], 14);
+ dd = FF4(dd, aa, bb, cc, X[10], 14);
+ cc = FF4(cc, dd, aa, bb, X[ 3], 12);
+ bb = FF4(bb, cc, dd, aa, X[12], 6);
+
+ //
+ // Parallel round 2
+ //
+ aa = FF3(aa, bb, cc, dd, X[ 6], 9);
+ dd = FF3(dd, aa, bb, cc, X[11], 13);
+ cc = FF3(cc, dd, aa, bb, X[ 3], 15);
+ bb = FF3(bb, cc, dd, aa, X[ 7], 7);
+ aa = FF3(aa, bb, cc, dd, X[ 0], 12);
+ dd = FF3(dd, aa, bb, cc, X[13], 8);
+ cc = FF3(cc, dd, aa, bb, X[ 5], 9);
+ bb = FF3(bb, cc, dd, aa, X[10], 11);
+ aa = FF3(aa, bb, cc, dd, X[14], 7);
+ dd = FF3(dd, aa, bb, cc, X[15], 7);
+ cc = FF3(cc, dd, aa, bb, X[ 8], 12);
+ bb = FF3(bb, cc, dd, aa, X[12], 7);
+ aa = FF3(aa, bb, cc, dd, X[ 4], 6);
+ dd = FF3(dd, aa, bb, cc, X[ 9], 15);
+ cc = FF3(cc, dd, aa, bb, X[ 1], 13);
+ bb = FF3(bb, cc, dd, aa, X[ 2], 11);
+
+ //
+ // Parallel round 3
+ //
+ aa = FF2(aa, bb, cc, dd, X[15], 9);
+ dd = FF2(dd, aa, bb, cc, X[ 5], 7);
+ cc = FF2(cc, dd, aa, bb, X[ 1], 15);
+ bb = FF2(bb, cc, dd, aa, X[ 3], 11);
+ aa = FF2(aa, bb, cc, dd, X[ 7], 8);
+ dd = FF2(dd, aa, bb, cc, X[14], 6);
+ cc = FF2(cc, dd, aa, bb, X[ 6], 6);
+ bb = FF2(bb, cc, dd, aa, X[ 9], 14);
+ aa = FF2(aa, bb, cc, dd, X[11], 12);
+ dd = FF2(dd, aa, bb, cc, X[ 8], 13);
+ cc = FF2(cc, dd, aa, bb, X[12], 5);
+ bb = FF2(bb, cc, dd, aa, X[ 2], 14);
+ aa = FF2(aa, bb, cc, dd, X[10], 13);
+ dd = FF2(dd, aa, bb, cc, X[ 0], 13);
+ cc = FF2(cc, dd, aa, bb, X[ 4], 7);
+ bb = FF2(bb, cc, dd, aa, X[13], 5);
+
+ //
+ // Parallel round 4
+ //
+ aa = FF1(aa, bb, cc, dd, X[ 8], 15);
+ dd = FF1(dd, aa, bb, cc, X[ 6], 5);
+ cc = FF1(cc, dd, aa, bb, X[ 4], 8);
+ bb = FF1(bb, cc, dd, aa, X[ 1], 11);
+ aa = FF1(aa, bb, cc, dd, X[ 3], 14);
+ dd = FF1(dd, aa, bb, cc, X[11], 14);
+ cc = FF1(cc, dd, aa, bb, X[15], 6);
+ bb = FF1(bb, cc, dd, aa, X[ 0], 14);
+ aa = FF1(aa, bb, cc, dd, X[ 5], 6);
+ dd = FF1(dd, aa, bb, cc, X[12], 9);
+ cc = FF1(cc, dd, aa, bb, X[ 2], 12);
+ bb = FF1(bb, cc, dd, aa, X[13], 9);
+ aa = FF1(aa, bb, cc, dd, X[ 9], 12);
+ dd = FF1(dd, aa, bb, cc, X[ 7], 5);
+ cc = FF1(cc, dd, aa, bb, X[10], 15);
+ bb = FF1(bb, cc, dd, aa, X[14], 8);
+
+ dd += c + H1; // final result for H0
+
+ //
+ // combine the results
+ //
+ H1 = H2 + d + aa;
+ H2 = H3 + a + bb;
+ H3 = H0 + b + cc;
+ H0 = dd;
+
+ //
+ // reset the offset and clean out the word buffer.
+ //
+ xOff = 0;
+ for (int i = 0; i != X.length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ public Memoable copy()
+ {
+ return new RIPEMD128Digest(this);
+ }
+
+ public void reset(Memoable other)
+ {
+ RIPEMD128Digest d = (RIPEMD128Digest)other;
+
+ copyIn(d);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/RIPEMD160Digest.java b/core/src/main/java/org/bouncycastle/crypto/digests/RIPEMD160Digest.java
new file mode 100644
index 00000000..20c81e68
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/RIPEMD160Digest.java
@@ -0,0 +1,443 @@
+package org.bouncycastle.crypto.digests;
+
+
+import org.bouncycastle.util.Memoable;
+
+/**
+ * implementation of RIPEMD see,
+ * http://www.esat.kuleuven.ac.be/~bosselae/ripemd160.html
+ */
+public class RIPEMD160Digest
+ extends GeneralDigest
+{
+ private static final int DIGEST_LENGTH = 20;
+
+ private int H0, H1, H2, H3, H4; // IV's
+
+ private int[] X = new int[16];
+ private int xOff;
+
+ /**
+ * Standard constructor
+ */
+ public RIPEMD160Digest()
+ {
+ reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public RIPEMD160Digest(RIPEMD160Digest t)
+ {
+ super(t);
+
+ copyIn(t);
+ }
+
+ private void copyIn(RIPEMD160Digest t)
+ {
+ super.copyIn(t);
+
+ H0 = t.H0;
+ H1 = t.H1;
+ H2 = t.H2;
+ H3 = t.H3;
+ H4 = t.H4;
+
+ System.arraycopy(t.X, 0, X, 0, t.X.length);
+ xOff = t.xOff;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "RIPEMD160";
+ }
+
+ public int getDigestSize()
+ {
+ return DIGEST_LENGTH;
+ }
+
+ protected void processWord(
+ byte[] in,
+ int inOff)
+ {
+ X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8)
+ | ((in[inOff + 2] & 0xff) << 16) | ((in[inOff + 3] & 0xff) << 24);
+
+ if (xOff == 16)
+ {
+ processBlock();
+ }
+ }
+
+ protected void processLength(
+ long bitLength)
+ {
+ if (xOff > 14)
+ {
+ processBlock();
+ }
+
+ X[14] = (int)(bitLength & 0xffffffff);
+ X[15] = (int)(bitLength >>> 32);
+ }
+
+ private void unpackWord(
+ int word,
+ byte[] out,
+ int outOff)
+ {
+ out[outOff] = (byte)word;
+ out[outOff + 1] = (byte)(word >>> 8);
+ out[outOff + 2] = (byte)(word >>> 16);
+ out[outOff + 3] = (byte)(word >>> 24);
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ finish();
+
+ unpackWord(H0, out, outOff);
+ unpackWord(H1, out, outOff + 4);
+ unpackWord(H2, out, outOff + 8);
+ unpackWord(H3, out, outOff + 12);
+ unpackWord(H4, out, outOff + 16);
+
+ reset();
+
+ return DIGEST_LENGTH;
+ }
+
+ /**
+ * reset the chaining variables to the IV values.
+ */
+ public void reset()
+ {
+ super.reset();
+
+ H0 = 0x67452301;
+ H1 = 0xefcdab89;
+ H2 = 0x98badcfe;
+ H3 = 0x10325476;
+ H4 = 0xc3d2e1f0;
+
+ xOff = 0;
+
+ for (int i = 0; i != X.length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ /*
+ * rotate int x left n bits.
+ */
+ private int RL(
+ int x,
+ int n)
+ {
+ return (x << n) | (x >>> (32 - n));
+ }
+
+ /*
+ * f1,f2,f3,f4,f5 are the basic RIPEMD160 functions.
+ */
+
+ /*
+ * rounds 0-15
+ */
+ private int f1(
+ int x,
+ int y,
+ int z)
+ {
+ return x ^ y ^ z;
+ }
+
+ /*
+ * rounds 16-31
+ */
+ private int f2(
+ int x,
+ int y,
+ int z)
+ {
+ return (x & y) | (~x & z);
+ }
+
+ /*
+ * rounds 32-47
+ */
+ private int f3(
+ int x,
+ int y,
+ int z)
+ {
+ return (x | ~y) ^ z;
+ }
+
+ /*
+ * rounds 48-63
+ */
+ private int f4(
+ int x,
+ int y,
+ int z)
+ {
+ return (x & z) | (y & ~z);
+ }
+
+ /*
+ * rounds 64-79
+ */
+ private int f5(
+ int x,
+ int y,
+ int z)
+ {
+ return x ^ (y | ~z);
+ }
+
+ protected void processBlock()
+ {
+ int a, aa;
+ int b, bb;
+ int c, cc;
+ int d, dd;
+ int e, ee;
+
+ a = aa = H0;
+ b = bb = H1;
+ c = cc = H2;
+ d = dd = H3;
+ e = ee = H4;
+
+ //
+ // Rounds 1 - 16
+ //
+ // left
+ a = RL(a + f1(b,c,d) + X[ 0], 11) + e; c = RL(c, 10);
+ e = RL(e + f1(a,b,c) + X[ 1], 14) + d; b = RL(b, 10);
+ d = RL(d + f1(e,a,b) + X[ 2], 15) + c; a = RL(a, 10);
+ c = RL(c + f1(d,e,a) + X[ 3], 12) + b; e = RL(e, 10);
+ b = RL(b + f1(c,d,e) + X[ 4], 5) + a; d = RL(d, 10);
+ a = RL(a + f1(b,c,d) + X[ 5], 8) + e; c = RL(c, 10);
+ e = RL(e + f1(a,b,c) + X[ 6], 7) + d; b = RL(b, 10);
+ d = RL(d + f1(e,a,b) + X[ 7], 9) + c; a = RL(a, 10);
+ c = RL(c + f1(d,e,a) + X[ 8], 11) + b; e = RL(e, 10);
+ b = RL(b + f1(c,d,e) + X[ 9], 13) + a; d = RL(d, 10);
+ a = RL(a + f1(b,c,d) + X[10], 14) + e; c = RL(c, 10);
+ e = RL(e + f1(a,b,c) + X[11], 15) + d; b = RL(b, 10);
+ d = RL(d + f1(e,a,b) + X[12], 6) + c; a = RL(a, 10);
+ c = RL(c + f1(d,e,a) + X[13], 7) + b; e = RL(e, 10);
+ b = RL(b + f1(c,d,e) + X[14], 9) + a; d = RL(d, 10);
+ a = RL(a + f1(b,c,d) + X[15], 8) + e; c = RL(c, 10);
+
+ // right
+ aa = RL(aa + f5(bb,cc,dd) + X[ 5] + 0x50a28be6, 8) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f5(aa,bb,cc) + X[14] + 0x50a28be6, 9) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f5(ee,aa,bb) + X[ 7] + 0x50a28be6, 9) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f5(dd,ee,aa) + X[ 0] + 0x50a28be6, 11) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f5(cc,dd,ee) + X[ 9] + 0x50a28be6, 13) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f5(bb,cc,dd) + X[ 2] + 0x50a28be6, 15) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f5(aa,bb,cc) + X[11] + 0x50a28be6, 15) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f5(ee,aa,bb) + X[ 4] + 0x50a28be6, 5) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f5(dd,ee,aa) + X[13] + 0x50a28be6, 7) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f5(cc,dd,ee) + X[ 6] + 0x50a28be6, 7) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f5(bb,cc,dd) + X[15] + 0x50a28be6, 8) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f5(aa,bb,cc) + X[ 8] + 0x50a28be6, 11) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f5(ee,aa,bb) + X[ 1] + 0x50a28be6, 14) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f5(dd,ee,aa) + X[10] + 0x50a28be6, 14) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f5(cc,dd,ee) + X[ 3] + 0x50a28be6, 12) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f5(bb,cc,dd) + X[12] + 0x50a28be6, 6) + ee; cc = RL(cc, 10);
+
+ //
+ // Rounds 16-31
+ //
+ // left
+ e = RL(e + f2(a,b,c) + X[ 7] + 0x5a827999, 7) + d; b = RL(b, 10);
+ d = RL(d + f2(e,a,b) + X[ 4] + 0x5a827999, 6) + c; a = RL(a, 10);
+ c = RL(c + f2(d,e,a) + X[13] + 0x5a827999, 8) + b; e = RL(e, 10);
+ b = RL(b + f2(c,d,e) + X[ 1] + 0x5a827999, 13) + a; d = RL(d, 10);
+ a = RL(a + f2(b,c,d) + X[10] + 0x5a827999, 11) + e; c = RL(c, 10);
+ e = RL(e + f2(a,b,c) + X[ 6] + 0x5a827999, 9) + d; b = RL(b, 10);
+ d = RL(d + f2(e,a,b) + X[15] + 0x5a827999, 7) + c; a = RL(a, 10);
+ c = RL(c + f2(d,e,a) + X[ 3] + 0x5a827999, 15) + b; e = RL(e, 10);
+ b = RL(b + f2(c,d,e) + X[12] + 0x5a827999, 7) + a; d = RL(d, 10);
+ a = RL(a + f2(b,c,d) + X[ 0] + 0x5a827999, 12) + e; c = RL(c, 10);
+ e = RL(e + f2(a,b,c) + X[ 9] + 0x5a827999, 15) + d; b = RL(b, 10);
+ d = RL(d + f2(e,a,b) + X[ 5] + 0x5a827999, 9) + c; a = RL(a, 10);
+ c = RL(c + f2(d,e,a) + X[ 2] + 0x5a827999, 11) + b; e = RL(e, 10);
+ b = RL(b + f2(c,d,e) + X[14] + 0x5a827999, 7) + a; d = RL(d, 10);
+ a = RL(a + f2(b,c,d) + X[11] + 0x5a827999, 13) + e; c = RL(c, 10);
+ e = RL(e + f2(a,b,c) + X[ 8] + 0x5a827999, 12) + d; b = RL(b, 10);
+
+ // right
+ ee = RL(ee + f4(aa,bb,cc) + X[ 6] + 0x5c4dd124, 9) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f4(ee,aa,bb) + X[11] + 0x5c4dd124, 13) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f4(dd,ee,aa) + X[ 3] + 0x5c4dd124, 15) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f4(cc,dd,ee) + X[ 7] + 0x5c4dd124, 7) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f4(bb,cc,dd) + X[ 0] + 0x5c4dd124, 12) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f4(aa,bb,cc) + X[13] + 0x5c4dd124, 8) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f4(ee,aa,bb) + X[ 5] + 0x5c4dd124, 9) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f4(dd,ee,aa) + X[10] + 0x5c4dd124, 11) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f4(cc,dd,ee) + X[14] + 0x5c4dd124, 7) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f4(bb,cc,dd) + X[15] + 0x5c4dd124, 7) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f4(aa,bb,cc) + X[ 8] + 0x5c4dd124, 12) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f4(ee,aa,bb) + X[12] + 0x5c4dd124, 7) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f4(dd,ee,aa) + X[ 4] + 0x5c4dd124, 6) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f4(cc,dd,ee) + X[ 9] + 0x5c4dd124, 15) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f4(bb,cc,dd) + X[ 1] + 0x5c4dd124, 13) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f4(aa,bb,cc) + X[ 2] + 0x5c4dd124, 11) + dd; bb = RL(bb, 10);
+
+ //
+ // Rounds 32-47
+ //
+ // left
+ d = RL(d + f3(e,a,b) + X[ 3] + 0x6ed9eba1, 11) + c; a = RL(a, 10);
+ c = RL(c + f3(d,e,a) + X[10] + 0x6ed9eba1, 13) + b; e = RL(e, 10);
+ b = RL(b + f3(c,d,e) + X[14] + 0x6ed9eba1, 6) + a; d = RL(d, 10);
+ a = RL(a + f3(b,c,d) + X[ 4] + 0x6ed9eba1, 7) + e; c = RL(c, 10);
+ e = RL(e + f3(a,b,c) + X[ 9] + 0x6ed9eba1, 14) + d; b = RL(b, 10);
+ d = RL(d + f3(e,a,b) + X[15] + 0x6ed9eba1, 9) + c; a = RL(a, 10);
+ c = RL(c + f3(d,e,a) + X[ 8] + 0x6ed9eba1, 13) + b; e = RL(e, 10);
+ b = RL(b + f3(c,d,e) + X[ 1] + 0x6ed9eba1, 15) + a; d = RL(d, 10);
+ a = RL(a + f3(b,c,d) + X[ 2] + 0x6ed9eba1, 14) + e; c = RL(c, 10);
+ e = RL(e + f3(a,b,c) + X[ 7] + 0x6ed9eba1, 8) + d; b = RL(b, 10);
+ d = RL(d + f3(e,a,b) + X[ 0] + 0x6ed9eba1, 13) + c; a = RL(a, 10);
+ c = RL(c + f3(d,e,a) + X[ 6] + 0x6ed9eba1, 6) + b; e = RL(e, 10);
+ b = RL(b + f3(c,d,e) + X[13] + 0x6ed9eba1, 5) + a; d = RL(d, 10);
+ a = RL(a + f3(b,c,d) + X[11] + 0x6ed9eba1, 12) + e; c = RL(c, 10);
+ e = RL(e + f3(a,b,c) + X[ 5] + 0x6ed9eba1, 7) + d; b = RL(b, 10);
+ d = RL(d + f3(e,a,b) + X[12] + 0x6ed9eba1, 5) + c; a = RL(a, 10);
+
+ // right
+ dd = RL(dd + f3(ee,aa,bb) + X[15] + 0x6d703ef3, 9) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f3(dd,ee,aa) + X[ 5] + 0x6d703ef3, 7) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f3(cc,dd,ee) + X[ 1] + 0x6d703ef3, 15) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f3(bb,cc,dd) + X[ 3] + 0x6d703ef3, 11) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f3(aa,bb,cc) + X[ 7] + 0x6d703ef3, 8) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f3(ee,aa,bb) + X[14] + 0x6d703ef3, 6) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f3(dd,ee,aa) + X[ 6] + 0x6d703ef3, 6) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f3(cc,dd,ee) + X[ 9] + 0x6d703ef3, 14) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f3(bb,cc,dd) + X[11] + 0x6d703ef3, 12) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f3(aa,bb,cc) + X[ 8] + 0x6d703ef3, 13) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f3(ee,aa,bb) + X[12] + 0x6d703ef3, 5) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f3(dd,ee,aa) + X[ 2] + 0x6d703ef3, 14) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f3(cc,dd,ee) + X[10] + 0x6d703ef3, 13) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f3(bb,cc,dd) + X[ 0] + 0x6d703ef3, 13) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f3(aa,bb,cc) + X[ 4] + 0x6d703ef3, 7) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f3(ee,aa,bb) + X[13] + 0x6d703ef3, 5) + cc; aa = RL(aa, 10);
+
+ //
+ // Rounds 48-63
+ //
+ // left
+ c = RL(c + f4(d,e,a) + X[ 1] + 0x8f1bbcdc, 11) + b; e = RL(e, 10);
+ b = RL(b + f4(c,d,e) + X[ 9] + 0x8f1bbcdc, 12) + a; d = RL(d, 10);
+ a = RL(a + f4(b,c,d) + X[11] + 0x8f1bbcdc, 14) + e; c = RL(c, 10);
+ e = RL(e + f4(a,b,c) + X[10] + 0x8f1bbcdc, 15) + d; b = RL(b, 10);
+ d = RL(d + f4(e,a,b) + X[ 0] + 0x8f1bbcdc, 14) + c; a = RL(a, 10);
+ c = RL(c + f4(d,e,a) + X[ 8] + 0x8f1bbcdc, 15) + b; e = RL(e, 10);
+ b = RL(b + f4(c,d,e) + X[12] + 0x8f1bbcdc, 9) + a; d = RL(d, 10);
+ a = RL(a + f4(b,c,d) + X[ 4] + 0x8f1bbcdc, 8) + e; c = RL(c, 10);
+ e = RL(e + f4(a,b,c) + X[13] + 0x8f1bbcdc, 9) + d; b = RL(b, 10);
+ d = RL(d + f4(e,a,b) + X[ 3] + 0x8f1bbcdc, 14) + c; a = RL(a, 10);
+ c = RL(c + f4(d,e,a) + X[ 7] + 0x8f1bbcdc, 5) + b; e = RL(e, 10);
+ b = RL(b + f4(c,d,e) + X[15] + 0x8f1bbcdc, 6) + a; d = RL(d, 10);
+ a = RL(a + f4(b,c,d) + X[14] + 0x8f1bbcdc, 8) + e; c = RL(c, 10);
+ e = RL(e + f4(a,b,c) + X[ 5] + 0x8f1bbcdc, 6) + d; b = RL(b, 10);
+ d = RL(d + f4(e,a,b) + X[ 6] + 0x8f1bbcdc, 5) + c; a = RL(a, 10);
+ c = RL(c + f4(d,e,a) + X[ 2] + 0x8f1bbcdc, 12) + b; e = RL(e, 10);
+
+ // right
+ cc = RL(cc + f2(dd,ee,aa) + X[ 8] + 0x7a6d76e9, 15) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f2(cc,dd,ee) + X[ 6] + 0x7a6d76e9, 5) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f2(bb,cc,dd) + X[ 4] + 0x7a6d76e9, 8) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f2(aa,bb,cc) + X[ 1] + 0x7a6d76e9, 11) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f2(ee,aa,bb) + X[ 3] + 0x7a6d76e9, 14) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f2(dd,ee,aa) + X[11] + 0x7a6d76e9, 14) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f2(cc,dd,ee) + X[15] + 0x7a6d76e9, 6) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f2(bb,cc,dd) + X[ 0] + 0x7a6d76e9, 14) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f2(aa,bb,cc) + X[ 5] + 0x7a6d76e9, 6) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f2(ee,aa,bb) + X[12] + 0x7a6d76e9, 9) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f2(dd,ee,aa) + X[ 2] + 0x7a6d76e9, 12) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f2(cc,dd,ee) + X[13] + 0x7a6d76e9, 9) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f2(bb,cc,dd) + X[ 9] + 0x7a6d76e9, 12) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f2(aa,bb,cc) + X[ 7] + 0x7a6d76e9, 5) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f2(ee,aa,bb) + X[10] + 0x7a6d76e9, 15) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f2(dd,ee,aa) + X[14] + 0x7a6d76e9, 8) + bb; ee = RL(ee, 10);
+
+ //
+ // Rounds 64-79
+ //
+ // left
+ b = RL(b + f5(c,d,e) + X[ 4] + 0xa953fd4e, 9) + a; d = RL(d, 10);
+ a = RL(a + f5(b,c,d) + X[ 0] + 0xa953fd4e, 15) + e; c = RL(c, 10);
+ e = RL(e + f5(a,b,c) + X[ 5] + 0xa953fd4e, 5) + d; b = RL(b, 10);
+ d = RL(d + f5(e,a,b) + X[ 9] + 0xa953fd4e, 11) + c; a = RL(a, 10);
+ c = RL(c + f5(d,e,a) + X[ 7] + 0xa953fd4e, 6) + b; e = RL(e, 10);
+ b = RL(b + f5(c,d,e) + X[12] + 0xa953fd4e, 8) + a; d = RL(d, 10);
+ a = RL(a + f5(b,c,d) + X[ 2] + 0xa953fd4e, 13) + e; c = RL(c, 10);
+ e = RL(e + f5(a,b,c) + X[10] + 0xa953fd4e, 12) + d; b = RL(b, 10);
+ d = RL(d + f5(e,a,b) + X[14] + 0xa953fd4e, 5) + c; a = RL(a, 10);
+ c = RL(c + f5(d,e,a) + X[ 1] + 0xa953fd4e, 12) + b; e = RL(e, 10);
+ b = RL(b + f5(c,d,e) + X[ 3] + 0xa953fd4e, 13) + a; d = RL(d, 10);
+ a = RL(a + f5(b,c,d) + X[ 8] + 0xa953fd4e, 14) + e; c = RL(c, 10);
+ e = RL(e + f5(a,b,c) + X[11] + 0xa953fd4e, 11) + d; b = RL(b, 10);
+ d = RL(d + f5(e,a,b) + X[ 6] + 0xa953fd4e, 8) + c; a = RL(a, 10);
+ c = RL(c + f5(d,e,a) + X[15] + 0xa953fd4e, 5) + b; e = RL(e, 10);
+ b = RL(b + f5(c,d,e) + X[13] + 0xa953fd4e, 6) + a; d = RL(d, 10);
+
+ // right
+ bb = RL(bb + f1(cc,dd,ee) + X[12], 8) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f1(bb,cc,dd) + X[15], 5) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f1(aa,bb,cc) + X[10], 12) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f1(ee,aa,bb) + X[ 4], 9) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f1(dd,ee,aa) + X[ 1], 12) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f1(cc,dd,ee) + X[ 5], 5) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f1(bb,cc,dd) + X[ 8], 14) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f1(aa,bb,cc) + X[ 7], 6) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f1(ee,aa,bb) + X[ 6], 8) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f1(dd,ee,aa) + X[ 2], 13) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f1(cc,dd,ee) + X[13], 6) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f1(bb,cc,dd) + X[14], 5) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f1(aa,bb,cc) + X[ 0], 15) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f1(ee,aa,bb) + X[ 3], 13) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f1(dd,ee,aa) + X[ 9], 11) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f1(cc,dd,ee) + X[11], 11) + aa; dd = RL(dd, 10);
+
+ dd += c + H1;
+ H1 = H2 + d + ee;
+ H2 = H3 + e + aa;
+ H3 = H4 + a + bb;
+ H4 = H0 + b + cc;
+ H0 = dd;
+
+ //
+ // reset the offset and clean out the word buffer.
+ //
+ xOff = 0;
+ for (int i = 0; i != X.length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ public Memoable copy()
+ {
+ return new RIPEMD160Digest(this);
+ }
+
+ public void reset(Memoable other)
+ {
+ RIPEMD160Digest d = (RIPEMD160Digest)other;
+
+ copyIn(d);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/RIPEMD256Digest.java b/core/src/main/java/org/bouncycastle/crypto/digests/RIPEMD256Digest.java
new file mode 100644
index 00000000..86746b45
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/RIPEMD256Digest.java
@@ -0,0 +1,497 @@
+package org.bouncycastle.crypto.digests;
+
+
+import org.bouncycastle.util.Memoable;
+
+/**
+ * implementation of RIPEMD256.
+ * <p>
+ * <b>note:</b> this algorithm offers the same level of security as RIPEMD128.
+ */
+public class RIPEMD256Digest
+ extends GeneralDigest
+{
+ private static final int DIGEST_LENGTH = 32;
+
+ private int H0, H1, H2, H3, H4, H5, H6, H7; // IV's
+
+ private int[] X = new int[16];
+ private int xOff;
+
+ /**
+ * Standard constructor
+ */
+ public RIPEMD256Digest()
+ {
+ reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public RIPEMD256Digest(RIPEMD256Digest t)
+ {
+ super(t);
+
+ copyIn(t);
+ }
+
+ private void copyIn(RIPEMD256Digest t)
+ {
+ super.copyIn(t);
+
+ H0 = t.H0;
+ H1 = t.H1;
+ H2 = t.H2;
+ H3 = t.H3;
+ H4 = t.H4;
+ H5 = t.H5;
+ H6 = t.H6;
+ H7 = t.H7;
+
+ System.arraycopy(t.X, 0, X, 0, t.X.length);
+ xOff = t.xOff;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "RIPEMD256";
+ }
+
+ public int getDigestSize()
+ {
+ return DIGEST_LENGTH;
+ }
+
+ protected void processWord(
+ byte[] in,
+ int inOff)
+ {
+ X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8)
+ | ((in[inOff + 2] & 0xff) << 16) | ((in[inOff + 3] & 0xff) << 24);
+
+ if (xOff == 16)
+ {
+ processBlock();
+ }
+ }
+
+ protected void processLength(
+ long bitLength)
+ {
+ if (xOff > 14)
+ {
+ processBlock();
+ }
+
+ X[14] = (int)(bitLength & 0xffffffff);
+ X[15] = (int)(bitLength >>> 32);
+ }
+
+ private void unpackWord(
+ int word,
+ byte[] out,
+ int outOff)
+ {
+ out[outOff] = (byte)word;
+ out[outOff + 1] = (byte)(word >>> 8);
+ out[outOff + 2] = (byte)(word >>> 16);
+ out[outOff + 3] = (byte)(word >>> 24);
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ finish();
+
+ unpackWord(H0, out, outOff);
+ unpackWord(H1, out, outOff + 4);
+ unpackWord(H2, out, outOff + 8);
+ unpackWord(H3, out, outOff + 12);
+ unpackWord(H4, out, outOff + 16);
+ unpackWord(H5, out, outOff + 20);
+ unpackWord(H6, out, outOff + 24);
+ unpackWord(H7, out, outOff + 28);
+
+ reset();
+
+ return DIGEST_LENGTH;
+ }
+
+ /**
+ * reset the chaining variables to the IV values.
+ */
+ public void reset()
+ {
+ super.reset();
+
+ H0 = 0x67452301;
+ H1 = 0xefcdab89;
+ H2 = 0x98badcfe;
+ H3 = 0x10325476;
+ H4 = 0x76543210;
+ H5 = 0xFEDCBA98;
+ H6 = 0x89ABCDEF;
+ H7 = 0x01234567;
+
+ xOff = 0;
+
+ for (int i = 0; i != X.length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ /*
+ * rotate int x left n bits.
+ */
+ private int RL(
+ int x,
+ int n)
+ {
+ return (x << n) | (x >>> (32 - n));
+ }
+
+ /*
+ * f1,f2,f3,f4 are the basic RIPEMD128 functions.
+ */
+
+ /*
+ * F
+ */
+ private int f1(
+ int x,
+ int y,
+ int z)
+ {
+ return x ^ y ^ z;
+ }
+
+ /*
+ * G
+ */
+ private int f2(
+ int x,
+ int y,
+ int z)
+ {
+ return (x & y) | (~x & z);
+ }
+
+ /*
+ * H
+ */
+ private int f3(
+ int x,
+ int y,
+ int z)
+ {
+ return (x | ~y) ^ z;
+ }
+
+ /*
+ * I
+ */
+ private int f4(
+ int x,
+ int y,
+ int z)
+ {
+ return (x & z) | (y & ~z);
+ }
+
+ private int F1(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + f1(b, c, d) + x, s);
+ }
+
+ private int F2(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + f2(b, c, d) + x + 0x5a827999, s);
+ }
+
+ private int F3(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + f3(b, c, d) + x + 0x6ed9eba1, s);
+ }
+
+ private int F4(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + f4(b, c, d) + x + 0x8f1bbcdc, s);
+ }
+
+ private int FF1(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + f1(b, c, d) + x, s);
+ }
+
+ private int FF2(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + f2(b, c, d) + x + 0x6d703ef3, s);
+ }
+
+ private int FF3(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + f3(b, c, d) + x + 0x5c4dd124, s);
+ }
+
+ private int FF4(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + f4(b, c, d) + x + 0x50a28be6, s);
+ }
+
+ protected void processBlock()
+ {
+ int a, aa;
+ int b, bb;
+ int c, cc;
+ int d, dd;
+ int t;
+
+ a = H0;
+ b = H1;
+ c = H2;
+ d = H3;
+ aa = H4;
+ bb = H5;
+ cc = H6;
+ dd = H7;
+
+ //
+ // Round 1
+ //
+
+ a = F1(a, b, c, d, X[ 0], 11);
+ d = F1(d, a, b, c, X[ 1], 14);
+ c = F1(c, d, a, b, X[ 2], 15);
+ b = F1(b, c, d, a, X[ 3], 12);
+ a = F1(a, b, c, d, X[ 4], 5);
+ d = F1(d, a, b, c, X[ 5], 8);
+ c = F1(c, d, a, b, X[ 6], 7);
+ b = F1(b, c, d, a, X[ 7], 9);
+ a = F1(a, b, c, d, X[ 8], 11);
+ d = F1(d, a, b, c, X[ 9], 13);
+ c = F1(c, d, a, b, X[10], 14);
+ b = F1(b, c, d, a, X[11], 15);
+ a = F1(a, b, c, d, X[12], 6);
+ d = F1(d, a, b, c, X[13], 7);
+ c = F1(c, d, a, b, X[14], 9);
+ b = F1(b, c, d, a, X[15], 8);
+
+ aa = FF4(aa, bb, cc, dd, X[ 5], 8);
+ dd = FF4(dd, aa, bb, cc, X[14], 9);
+ cc = FF4(cc, dd, aa, bb, X[ 7], 9);
+ bb = FF4(bb, cc, dd, aa, X[ 0], 11);
+ aa = FF4(aa, bb, cc, dd, X[ 9], 13);
+ dd = FF4(dd, aa, bb, cc, X[ 2], 15);
+ cc = FF4(cc, dd, aa, bb, X[11], 15);
+ bb = FF4(bb, cc, dd, aa, X[ 4], 5);
+ aa = FF4(aa, bb, cc, dd, X[13], 7);
+ dd = FF4(dd, aa, bb, cc, X[ 6], 7);
+ cc = FF4(cc, dd, aa, bb, X[15], 8);
+ bb = FF4(bb, cc, dd, aa, X[ 8], 11);
+ aa = FF4(aa, bb, cc, dd, X[ 1], 14);
+ dd = FF4(dd, aa, bb, cc, X[10], 14);
+ cc = FF4(cc, dd, aa, bb, X[ 3], 12);
+ bb = FF4(bb, cc, dd, aa, X[12], 6);
+
+ t = a; a = aa; aa = t;
+
+ //
+ // Round 2
+ //
+ a = F2(a, b, c, d, X[ 7], 7);
+ d = F2(d, a, b, c, X[ 4], 6);
+ c = F2(c, d, a, b, X[13], 8);
+ b = F2(b, c, d, a, X[ 1], 13);
+ a = F2(a, b, c, d, X[10], 11);
+ d = F2(d, a, b, c, X[ 6], 9);
+ c = F2(c, d, a, b, X[15], 7);
+ b = F2(b, c, d, a, X[ 3], 15);
+ a = F2(a, b, c, d, X[12], 7);
+ d = F2(d, a, b, c, X[ 0], 12);
+ c = F2(c, d, a, b, X[ 9], 15);
+ b = F2(b, c, d, a, X[ 5], 9);
+ a = F2(a, b, c, d, X[ 2], 11);
+ d = F2(d, a, b, c, X[14], 7);
+ c = F2(c, d, a, b, X[11], 13);
+ b = F2(b, c, d, a, X[ 8], 12);
+
+ aa = FF3(aa, bb, cc, dd, X[ 6], 9);
+ dd = FF3(dd, aa, bb, cc, X[ 11], 13);
+ cc = FF3(cc, dd, aa, bb, X[3], 15);
+ bb = FF3(bb, cc, dd, aa, X[ 7], 7);
+ aa = FF3(aa, bb, cc, dd, X[0], 12);
+ dd = FF3(dd, aa, bb, cc, X[13], 8);
+ cc = FF3(cc, dd, aa, bb, X[5], 9);
+ bb = FF3(bb, cc, dd, aa, X[10], 11);
+ aa = FF3(aa, bb, cc, dd, X[14], 7);
+ dd = FF3(dd, aa, bb, cc, X[15], 7);
+ cc = FF3(cc, dd, aa, bb, X[ 8], 12);
+ bb = FF3(bb, cc, dd, aa, X[12], 7);
+ aa = FF3(aa, bb, cc, dd, X[ 4], 6);
+ dd = FF3(dd, aa, bb, cc, X[ 9], 15);
+ cc = FF3(cc, dd, aa, bb, X[ 1], 13);
+ bb = FF3(bb, cc, dd, aa, X[ 2], 11);
+
+ t = b; b = bb; bb = t;
+
+ //
+ // Round 3
+ //
+ a = F3(a, b, c, d, X[ 3], 11);
+ d = F3(d, a, b, c, X[10], 13);
+ c = F3(c, d, a, b, X[14], 6);
+ b = F3(b, c, d, a, X[ 4], 7);
+ a = F3(a, b, c, d, X[ 9], 14);
+ d = F3(d, a, b, c, X[15], 9);
+ c = F3(c, d, a, b, X[ 8], 13);
+ b = F3(b, c, d, a, X[ 1], 15);
+ a = F3(a, b, c, d, X[ 2], 14);
+ d = F3(d, a, b, c, X[ 7], 8);
+ c = F3(c, d, a, b, X[ 0], 13);
+ b = F3(b, c, d, a, X[ 6], 6);
+ a = F3(a, b, c, d, X[13], 5);
+ d = F3(d, a, b, c, X[11], 12);
+ c = F3(c, d, a, b, X[ 5], 7);
+ b = F3(b, c, d, a, X[12], 5);
+
+ aa = FF2(aa, bb, cc, dd, X[ 15], 9);
+ dd = FF2(dd, aa, bb, cc, X[5], 7);
+ cc = FF2(cc, dd, aa, bb, X[1], 15);
+ bb = FF2(bb, cc, dd, aa, X[ 3], 11);
+ aa = FF2(aa, bb, cc, dd, X[ 7], 8);
+ dd = FF2(dd, aa, bb, cc, X[14], 6);
+ cc = FF2(cc, dd, aa, bb, X[ 6], 6);
+ bb = FF2(bb, cc, dd, aa, X[ 9], 14);
+ aa = FF2(aa, bb, cc, dd, X[11], 12);
+ dd = FF2(dd, aa, bb, cc, X[ 8], 13);
+ cc = FF2(cc, dd, aa, bb, X[12], 5);
+ bb = FF2(bb, cc, dd, aa, X[ 2], 14);
+ aa = FF2(aa, bb, cc, dd, X[10], 13);
+ dd = FF2(dd, aa, bb, cc, X[ 0], 13);
+ cc = FF2(cc, dd, aa, bb, X[ 4], 7);
+ bb = FF2(bb, cc, dd, aa, X[13], 5);
+
+ t = c; c = cc; cc = t;
+
+ //
+ // Round 4
+ //
+ a = F4(a, b, c, d, X[ 1], 11);
+ d = F4(d, a, b, c, X[ 9], 12);
+ c = F4(c, d, a, b, X[11], 14);
+ b = F4(b, c, d, a, X[10], 15);
+ a = F4(a, b, c, d, X[ 0], 14);
+ d = F4(d, a, b, c, X[ 8], 15);
+ c = F4(c, d, a, b, X[12], 9);
+ b = F4(b, c, d, a, X[ 4], 8);
+ a = F4(a, b, c, d, X[13], 9);
+ d = F4(d, a, b, c, X[ 3], 14);
+ c = F4(c, d, a, b, X[ 7], 5);
+ b = F4(b, c, d, a, X[15], 6);
+ a = F4(a, b, c, d, X[14], 8);
+ d = F4(d, a, b, c, X[ 5], 6);
+ c = F4(c, d, a, b, X[ 6], 5);
+ b = F4(b, c, d, a, X[ 2], 12);
+
+ aa = FF1(aa, bb, cc, dd, X[ 8], 15);
+ dd = FF1(dd, aa, bb, cc, X[ 6], 5);
+ cc = FF1(cc, dd, aa, bb, X[ 4], 8);
+ bb = FF1(bb, cc, dd, aa, X[ 1], 11);
+ aa = FF1(aa, bb, cc, dd, X[ 3], 14);
+ dd = FF1(dd, aa, bb, cc, X[11], 14);
+ cc = FF1(cc, dd, aa, bb, X[15], 6);
+ bb = FF1(bb, cc, dd, aa, X[ 0], 14);
+ aa = FF1(aa, bb, cc, dd, X[ 5], 6);
+ dd = FF1(dd, aa, bb, cc, X[12], 9);
+ cc = FF1(cc, dd, aa, bb, X[ 2], 12);
+ bb = FF1(bb, cc, dd, aa, X[13], 9);
+ aa = FF1(aa, bb, cc, dd, X[ 9], 12);
+ dd = FF1(dd, aa, bb, cc, X[ 7], 5);
+ cc = FF1(cc, dd, aa, bb, X[10], 15);
+ bb = FF1(bb, cc, dd, aa, X[14], 8);
+
+ t = d; d = dd; dd = t;
+
+ H0 += a;
+ H1 += b;
+ H2 += c;
+ H3 += d;
+ H4 += aa;
+ H5 += bb;
+ H6 += cc;
+ H7 += dd;
+
+ //
+ // reset the offset and clean out the word buffer.
+ //
+ xOff = 0;
+ for (int i = 0; i != X.length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ public Memoable copy()
+ {
+ return new RIPEMD256Digest(this);
+ }
+
+ public void reset(Memoable other)
+ {
+ RIPEMD256Digest d = (RIPEMD256Digest)other;
+
+ copyIn(d);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/RIPEMD320Digest.java b/core/src/main/java/org/bouncycastle/crypto/digests/RIPEMD320Digest.java
new file mode 100644
index 00000000..32775e77
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/RIPEMD320Digest.java
@@ -0,0 +1,481 @@
+package org.bouncycastle.crypto.digests;
+
+
+import org.bouncycastle.util.Memoable;
+
+/**
+ * implementation of RIPEMD 320.
+ * <p>
+ * <b>Note:</b> this implementation offers the same level of security
+ * as RIPEMD 160.
+ */
+public class RIPEMD320Digest
+ extends GeneralDigest
+{
+ private static final int DIGEST_LENGTH = 40;
+
+ private int H0, H1, H2, H3, H4, H5, H6, H7, H8, H9; // IV's
+
+ private int[] X = new int[16];
+ private int xOff;
+
+ /**
+ * Standard constructor
+ */
+ public RIPEMD320Digest()
+ {
+ reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public RIPEMD320Digest(RIPEMD320Digest t)
+ {
+ super(t);
+
+ doCopy(t);
+ }
+
+ private void doCopy(RIPEMD320Digest t)
+ {
+ super.copyIn(t);
+ H0 = t.H0;
+ H1 = t.H1;
+ H2 = t.H2;
+ H3 = t.H3;
+ H4 = t.H4;
+ H5 = t.H5;
+ H6 = t.H6;
+ H7 = t.H7;
+ H8 = t.H8;
+ H9 = t.H9;
+
+ System.arraycopy(t.X, 0, X, 0, t.X.length);
+ xOff = t.xOff;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "RIPEMD320";
+ }
+
+ public int getDigestSize()
+ {
+ return DIGEST_LENGTH;
+ }
+
+ protected void processWord(
+ byte[] in,
+ int inOff)
+ {
+ X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8)
+ | ((in[inOff + 2] & 0xff) << 16) | ((in[inOff + 3] & 0xff) << 24);
+
+ if (xOff == 16)
+ {
+ processBlock();
+ }
+ }
+
+ protected void processLength(
+ long bitLength)
+ {
+ if (xOff > 14)
+ {
+ processBlock();
+ }
+
+ X[14] = (int)(bitLength & 0xffffffff);
+ X[15] = (int)(bitLength >>> 32);
+ }
+
+ private void unpackWord(
+ int word,
+ byte[] out,
+ int outOff)
+ {
+ out[outOff] = (byte)word;
+ out[outOff + 1] = (byte)(word >>> 8);
+ out[outOff + 2] = (byte)(word >>> 16);
+ out[outOff + 3] = (byte)(word >>> 24);
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ finish();
+
+ unpackWord(H0, out, outOff);
+ unpackWord(H1, out, outOff + 4);
+ unpackWord(H2, out, outOff + 8);
+ unpackWord(H3, out, outOff + 12);
+ unpackWord(H4, out, outOff + 16);
+ unpackWord(H5, out, outOff + 20);
+ unpackWord(H6, out, outOff + 24);
+ unpackWord(H7, out, outOff + 28);
+ unpackWord(H8, out, outOff + 32);
+ unpackWord(H9, out, outOff + 36);
+
+ reset();
+
+ return DIGEST_LENGTH;
+ }
+
+ /**
+ * reset the chaining variables to the IV values.
+ */
+ public void reset()
+ {
+ super.reset();
+
+ H0 = 0x67452301;
+ H1 = 0xefcdab89;
+ H2 = 0x98badcfe;
+ H3 = 0x10325476;
+ H4 = 0xc3d2e1f0;
+ H5 = 0x76543210;
+ H6 = 0xFEDCBA98;
+ H7 = 0x89ABCDEF;
+ H8 = 0x01234567;
+ H9 = 0x3C2D1E0F;
+
+ xOff = 0;
+
+ for (int i = 0; i != X.length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ /*
+ * rotate int x left n bits.
+ */
+ private int RL(
+ int x,
+ int n)
+ {
+ return (x << n) | (x >>> (32 - n));
+ }
+
+ /*
+ * f1,f2,f3,f4,f5 are the basic RIPEMD160 functions.
+ */
+
+ /*
+ * rounds 0-15
+ */
+ private int f1(
+ int x,
+ int y,
+ int z)
+ {
+ return x ^ y ^ z;
+ }
+
+ /*
+ * rounds 16-31
+ */
+ private int f2(
+ int x,
+ int y,
+ int z)
+ {
+ return (x & y) | (~x & z);
+ }
+
+ /*
+ * rounds 32-47
+ */
+ private int f3(
+ int x,
+ int y,
+ int z)
+ {
+ return (x | ~y) ^ z;
+ }
+
+ /*
+ * rounds 48-63
+ */
+ private int f4(
+ int x,
+ int y,
+ int z)
+ {
+ return (x & z) | (y & ~z);
+ }
+
+ /*
+ * rounds 64-79
+ */
+ private int f5(
+ int x,
+ int y,
+ int z)
+ {
+ return x ^ (y | ~z);
+ }
+
+ protected void processBlock()
+ {
+ int a, aa;
+ int b, bb;
+ int c, cc;
+ int d, dd;
+ int e, ee;
+ int t;
+
+ a = H0;
+ b = H1;
+ c = H2;
+ d = H3;
+ e = H4;
+ aa = H5;
+ bb = H6;
+ cc = H7;
+ dd = H8;
+ ee = H9;
+
+ //
+ // Rounds 1 - 16
+ //
+ // left
+ a = RL(a + f1(b,c,d) + X[ 0], 11) + e; c = RL(c, 10);
+ e = RL(e + f1(a,b,c) + X[ 1], 14) + d; b = RL(b, 10);
+ d = RL(d + f1(e,a,b) + X[ 2], 15) + c; a = RL(a, 10);
+ c = RL(c + f1(d,e,a) + X[ 3], 12) + b; e = RL(e, 10);
+ b = RL(b + f1(c,d,e) + X[ 4], 5) + a; d = RL(d, 10);
+ a = RL(a + f1(b,c,d) + X[ 5], 8) + e; c = RL(c, 10);
+ e = RL(e + f1(a,b,c) + X[ 6], 7) + d; b = RL(b, 10);
+ d = RL(d + f1(e,a,b) + X[ 7], 9) + c; a = RL(a, 10);
+ c = RL(c + f1(d,e,a) + X[ 8], 11) + b; e = RL(e, 10);
+ b = RL(b + f1(c,d,e) + X[ 9], 13) + a; d = RL(d, 10);
+ a = RL(a + f1(b,c,d) + X[10], 14) + e; c = RL(c, 10);
+ e = RL(e + f1(a,b,c) + X[11], 15) + d; b = RL(b, 10);
+ d = RL(d + f1(e,a,b) + X[12], 6) + c; a = RL(a, 10);
+ c = RL(c + f1(d,e,a) + X[13], 7) + b; e = RL(e, 10);
+ b = RL(b + f1(c,d,e) + X[14], 9) + a; d = RL(d, 10);
+ a = RL(a + f1(b,c,d) + X[15], 8) + e; c = RL(c, 10);
+
+ // right
+ aa = RL(aa + f5(bb,cc,dd) + X[ 5] + 0x50a28be6, 8) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f5(aa,bb,cc) + X[14] + 0x50a28be6, 9) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f5(ee,aa,bb) + X[ 7] + 0x50a28be6, 9) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f5(dd,ee,aa) + X[ 0] + 0x50a28be6, 11) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f5(cc,dd,ee) + X[ 9] + 0x50a28be6, 13) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f5(bb,cc,dd) + X[ 2] + 0x50a28be6, 15) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f5(aa,bb,cc) + X[11] + 0x50a28be6, 15) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f5(ee,aa,bb) + X[ 4] + 0x50a28be6, 5) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f5(dd,ee,aa) + X[13] + 0x50a28be6, 7) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f5(cc,dd,ee) + X[ 6] + 0x50a28be6, 7) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f5(bb,cc,dd) + X[15] + 0x50a28be6, 8) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f5(aa,bb,cc) + X[ 8] + 0x50a28be6, 11) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f5(ee,aa,bb) + X[ 1] + 0x50a28be6, 14) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f5(dd,ee,aa) + X[10] + 0x50a28be6, 14) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f5(cc,dd,ee) + X[ 3] + 0x50a28be6, 12) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f5(bb,cc,dd) + X[12] + 0x50a28be6, 6) + ee; cc = RL(cc, 10);
+
+ t = a; a = aa; aa = t;
+
+ //
+ // Rounds 16-31
+ //
+ // left
+ e = RL(e + f2(a,b,c) + X[ 7] + 0x5a827999, 7) + d; b = RL(b, 10);
+ d = RL(d + f2(e,a,b) + X[ 4] + 0x5a827999, 6) + c; a = RL(a, 10);
+ c = RL(c + f2(d,e,a) + X[13] + 0x5a827999, 8) + b; e = RL(e, 10);
+ b = RL(b + f2(c,d,e) + X[ 1] + 0x5a827999, 13) + a; d = RL(d, 10);
+ a = RL(a + f2(b,c,d) + X[10] + 0x5a827999, 11) + e; c = RL(c, 10);
+ e = RL(e + f2(a,b,c) + X[ 6] + 0x5a827999, 9) + d; b = RL(b, 10);
+ d = RL(d + f2(e,a,b) + X[15] + 0x5a827999, 7) + c; a = RL(a, 10);
+ c = RL(c + f2(d,e,a) + X[ 3] + 0x5a827999, 15) + b; e = RL(e, 10);
+ b = RL(b + f2(c,d,e) + X[12] + 0x5a827999, 7) + a; d = RL(d, 10);
+ a = RL(a + f2(b,c,d) + X[ 0] + 0x5a827999, 12) + e; c = RL(c, 10);
+ e = RL(e + f2(a,b,c) + X[ 9] + 0x5a827999, 15) + d; b = RL(b, 10);
+ d = RL(d + f2(e,a,b) + X[ 5] + 0x5a827999, 9) + c; a = RL(a, 10);
+ c = RL(c + f2(d,e,a) + X[ 2] + 0x5a827999, 11) + b; e = RL(e, 10);
+ b = RL(b + f2(c,d,e) + X[14] + 0x5a827999, 7) + a; d = RL(d, 10);
+ a = RL(a + f2(b,c,d) + X[11] + 0x5a827999, 13) + e; c = RL(c, 10);
+ e = RL(e + f2(a,b,c) + X[ 8] + 0x5a827999, 12) + d; b = RL(b, 10);
+
+ // right
+ ee = RL(ee + f4(aa,bb,cc) + X[ 6] + 0x5c4dd124, 9) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f4(ee,aa,bb) + X[11] + 0x5c4dd124, 13) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f4(dd,ee,aa) + X[ 3] + 0x5c4dd124, 15) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f4(cc,dd,ee) + X[ 7] + 0x5c4dd124, 7) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f4(bb,cc,dd) + X[ 0] + 0x5c4dd124, 12) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f4(aa,bb,cc) + X[13] + 0x5c4dd124, 8) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f4(ee,aa,bb) + X[ 5] + 0x5c4dd124, 9) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f4(dd,ee,aa) + X[10] + 0x5c4dd124, 11) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f4(cc,dd,ee) + X[14] + 0x5c4dd124, 7) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f4(bb,cc,dd) + X[15] + 0x5c4dd124, 7) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f4(aa,bb,cc) + X[ 8] + 0x5c4dd124, 12) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f4(ee,aa,bb) + X[12] + 0x5c4dd124, 7) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f4(dd,ee,aa) + X[ 4] + 0x5c4dd124, 6) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f4(cc,dd,ee) + X[ 9] + 0x5c4dd124, 15) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f4(bb,cc,dd) + X[ 1] + 0x5c4dd124, 13) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f4(aa,bb,cc) + X[ 2] + 0x5c4dd124, 11) + dd; bb = RL(bb, 10);
+
+ t = b; b = bb; bb = t;
+
+ //
+ // Rounds 32-47
+ //
+ // left
+ d = RL(d + f3(e,a,b) + X[ 3] + 0x6ed9eba1, 11) + c; a = RL(a, 10);
+ c = RL(c + f3(d,e,a) + X[10] + 0x6ed9eba1, 13) + b; e = RL(e, 10);
+ b = RL(b + f3(c,d,e) + X[14] + 0x6ed9eba1, 6) + a; d = RL(d, 10);
+ a = RL(a + f3(b,c,d) + X[ 4] + 0x6ed9eba1, 7) + e; c = RL(c, 10);
+ e = RL(e + f3(a,b,c) + X[ 9] + 0x6ed9eba1, 14) + d; b = RL(b, 10);
+ d = RL(d + f3(e,a,b) + X[15] + 0x6ed9eba1, 9) + c; a = RL(a, 10);
+ c = RL(c + f3(d,e,a) + X[ 8] + 0x6ed9eba1, 13) + b; e = RL(e, 10);
+ b = RL(b + f3(c,d,e) + X[ 1] + 0x6ed9eba1, 15) + a; d = RL(d, 10);
+ a = RL(a + f3(b,c,d) + X[ 2] + 0x6ed9eba1, 14) + e; c = RL(c, 10);
+ e = RL(e + f3(a,b,c) + X[ 7] + 0x6ed9eba1, 8) + d; b = RL(b, 10);
+ d = RL(d + f3(e,a,b) + X[ 0] + 0x6ed9eba1, 13) + c; a = RL(a, 10);
+ c = RL(c + f3(d,e,a) + X[ 6] + 0x6ed9eba1, 6) + b; e = RL(e, 10);
+ b = RL(b + f3(c,d,e) + X[13] + 0x6ed9eba1, 5) + a; d = RL(d, 10);
+ a = RL(a + f3(b,c,d) + X[11] + 0x6ed9eba1, 12) + e; c = RL(c, 10);
+ e = RL(e + f3(a,b,c) + X[ 5] + 0x6ed9eba1, 7) + d; b = RL(b, 10);
+ d = RL(d + f3(e,a,b) + X[12] + 0x6ed9eba1, 5) + c; a = RL(a, 10);
+
+ // right
+ dd = RL(dd + f3(ee,aa,bb) + X[15] + 0x6d703ef3, 9) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f3(dd,ee,aa) + X[ 5] + 0x6d703ef3, 7) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f3(cc,dd,ee) + X[ 1] + 0x6d703ef3, 15) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f3(bb,cc,dd) + X[ 3] + 0x6d703ef3, 11) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f3(aa,bb,cc) + X[ 7] + 0x6d703ef3, 8) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f3(ee,aa,bb) + X[14] + 0x6d703ef3, 6) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f3(dd,ee,aa) + X[ 6] + 0x6d703ef3, 6) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f3(cc,dd,ee) + X[ 9] + 0x6d703ef3, 14) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f3(bb,cc,dd) + X[11] + 0x6d703ef3, 12) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f3(aa,bb,cc) + X[ 8] + 0x6d703ef3, 13) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f3(ee,aa,bb) + X[12] + 0x6d703ef3, 5) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f3(dd,ee,aa) + X[ 2] + 0x6d703ef3, 14) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f3(cc,dd,ee) + X[10] + 0x6d703ef3, 13) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f3(bb,cc,dd) + X[ 0] + 0x6d703ef3, 13) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f3(aa,bb,cc) + X[ 4] + 0x6d703ef3, 7) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f3(ee,aa,bb) + X[13] + 0x6d703ef3, 5) + cc; aa = RL(aa, 10);
+
+ t = c; c = cc; cc = t;
+
+ //
+ // Rounds 48-63
+ //
+ // left
+ c = RL(c + f4(d,e,a) + X[ 1] + 0x8f1bbcdc, 11) + b; e = RL(e, 10);
+ b = RL(b + f4(c,d,e) + X[ 9] + 0x8f1bbcdc, 12) + a; d = RL(d, 10);
+ a = RL(a + f4(b,c,d) + X[11] + 0x8f1bbcdc, 14) + e; c = RL(c, 10);
+ e = RL(e + f4(a,b,c) + X[10] + 0x8f1bbcdc, 15) + d; b = RL(b, 10);
+ d = RL(d + f4(e,a,b) + X[ 0] + 0x8f1bbcdc, 14) + c; a = RL(a, 10);
+ c = RL(c + f4(d,e,a) + X[ 8] + 0x8f1bbcdc, 15) + b; e = RL(e, 10);
+ b = RL(b + f4(c,d,e) + X[12] + 0x8f1bbcdc, 9) + a; d = RL(d, 10);
+ a = RL(a + f4(b,c,d) + X[ 4] + 0x8f1bbcdc, 8) + e; c = RL(c, 10);
+ e = RL(e + f4(a,b,c) + X[13] + 0x8f1bbcdc, 9) + d; b = RL(b, 10);
+ d = RL(d + f4(e,a,b) + X[ 3] + 0x8f1bbcdc, 14) + c; a = RL(a, 10);
+ c = RL(c + f4(d,e,a) + X[ 7] + 0x8f1bbcdc, 5) + b; e = RL(e, 10);
+ b = RL(b + f4(c,d,e) + X[15] + 0x8f1bbcdc, 6) + a; d = RL(d, 10);
+ a = RL(a + f4(b,c,d) + X[14] + 0x8f1bbcdc, 8) + e; c = RL(c, 10);
+ e = RL(e + f4(a,b,c) + X[ 5] + 0x8f1bbcdc, 6) + d; b = RL(b, 10);
+ d = RL(d + f4(e,a,b) + X[ 6] + 0x8f1bbcdc, 5) + c; a = RL(a, 10);
+ c = RL(c + f4(d,e,a) + X[ 2] + 0x8f1bbcdc, 12) + b; e = RL(e, 10);
+
+ // right
+ cc = RL(cc + f2(dd,ee,aa) + X[ 8] + 0x7a6d76e9, 15) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f2(cc,dd,ee) + X[ 6] + 0x7a6d76e9, 5) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f2(bb,cc,dd) + X[ 4] + 0x7a6d76e9, 8) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f2(aa,bb,cc) + X[ 1] + 0x7a6d76e9, 11) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f2(ee,aa,bb) + X[ 3] + 0x7a6d76e9, 14) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f2(dd,ee,aa) + X[11] + 0x7a6d76e9, 14) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f2(cc,dd,ee) + X[15] + 0x7a6d76e9, 6) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f2(bb,cc,dd) + X[ 0] + 0x7a6d76e9, 14) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f2(aa,bb,cc) + X[ 5] + 0x7a6d76e9, 6) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f2(ee,aa,bb) + X[12] + 0x7a6d76e9, 9) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f2(dd,ee,aa) + X[ 2] + 0x7a6d76e9, 12) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f2(cc,dd,ee) + X[13] + 0x7a6d76e9, 9) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f2(bb,cc,dd) + X[ 9] + 0x7a6d76e9, 12) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f2(aa,bb,cc) + X[ 7] + 0x7a6d76e9, 5) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f2(ee,aa,bb) + X[10] + 0x7a6d76e9, 15) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f2(dd,ee,aa) + X[14] + 0x7a6d76e9, 8) + bb; ee = RL(ee, 10);
+
+ t = d; d = dd; dd = t;
+
+ //
+ // Rounds 64-79
+ //
+ // left
+ b = RL(b + f5(c,d,e) + X[ 4] + 0xa953fd4e, 9) + a; d = RL(d, 10);
+ a = RL(a + f5(b,c,d) + X[ 0] + 0xa953fd4e, 15) + e; c = RL(c, 10);
+ e = RL(e + f5(a,b,c) + X[ 5] + 0xa953fd4e, 5) + d; b = RL(b, 10);
+ d = RL(d + f5(e,a,b) + X[ 9] + 0xa953fd4e, 11) + c; a = RL(a, 10);
+ c = RL(c + f5(d,e,a) + X[ 7] + 0xa953fd4e, 6) + b; e = RL(e, 10);
+ b = RL(b + f5(c,d,e) + X[12] + 0xa953fd4e, 8) + a; d = RL(d, 10);
+ a = RL(a + f5(b,c,d) + X[ 2] + 0xa953fd4e, 13) + e; c = RL(c, 10);
+ e = RL(e + f5(a,b,c) + X[10] + 0xa953fd4e, 12) + d; b = RL(b, 10);
+ d = RL(d + f5(e,a,b) + X[14] + 0xa953fd4e, 5) + c; a = RL(a, 10);
+ c = RL(c + f5(d,e,a) + X[ 1] + 0xa953fd4e, 12) + b; e = RL(e, 10);
+ b = RL(b + f5(c,d,e) + X[ 3] + 0xa953fd4e, 13) + a; d = RL(d, 10);
+ a = RL(a + f5(b,c,d) + X[ 8] + 0xa953fd4e, 14) + e; c = RL(c, 10);
+ e = RL(e + f5(a,b,c) + X[11] + 0xa953fd4e, 11) + d; b = RL(b, 10);
+ d = RL(d + f5(e,a,b) + X[ 6] + 0xa953fd4e, 8) + c; a = RL(a, 10);
+ c = RL(c + f5(d,e,a) + X[15] + 0xa953fd4e, 5) + b; e = RL(e, 10);
+ b = RL(b + f5(c,d,e) + X[13] + 0xa953fd4e, 6) + a; d = RL(d, 10);
+
+ // right
+ bb = RL(bb + f1(cc,dd,ee) + X[12], 8) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f1(bb,cc,dd) + X[15], 5) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f1(aa,bb,cc) + X[10], 12) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f1(ee,aa,bb) + X[ 4], 9) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f1(dd,ee,aa) + X[ 1], 12) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f1(cc,dd,ee) + X[ 5], 5) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f1(bb,cc,dd) + X[ 8], 14) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f1(aa,bb,cc) + X[ 7], 6) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f1(ee,aa,bb) + X[ 6], 8) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f1(dd,ee,aa) + X[ 2], 13) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f1(cc,dd,ee) + X[13], 6) + aa; dd = RL(dd, 10);
+ aa = RL(aa + f1(bb,cc,dd) + X[14], 5) + ee; cc = RL(cc, 10);
+ ee = RL(ee + f1(aa,bb,cc) + X[ 0], 15) + dd; bb = RL(bb, 10);
+ dd = RL(dd + f1(ee,aa,bb) + X[ 3], 13) + cc; aa = RL(aa, 10);
+ cc = RL(cc + f1(dd,ee,aa) + X[ 9], 11) + bb; ee = RL(ee, 10);
+ bb = RL(bb + f1(cc,dd,ee) + X[11], 11) + aa; dd = RL(dd, 10);
+
+ //
+ // do (e, ee) swap as part of assignment.
+ //
+
+ H0 += a;
+ H1 += b;
+ H2 += c;
+ H3 += d;
+ H4 += ee;
+ H5 += aa;
+ H6 += bb;
+ H7 += cc;
+ H8 += dd;
+ H9 += e;
+
+ //
+ // reset the offset and clean out the word buffer.
+ //
+ xOff = 0;
+ for (int i = 0; i != X.length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ public Memoable copy()
+ {
+ return new RIPEMD320Digest(this);
+ }
+
+ public void reset(Memoable other)
+ {
+ RIPEMD320Digest d = (RIPEMD320Digest)other;
+
+ doCopy(d);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java b/core/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java
new file mode 100644
index 00000000..21b1024e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java
@@ -0,0 +1,309 @@
+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.
+ *
+ * It is interesting to ponder why the, apart from the extra IV, the other difference here from MD5
+ * is the "endianness" of the word processing!
+ */
+public class SHA1Digest
+ extends GeneralDigest
+{
+ private static final int DIGEST_LENGTH = 20;
+
+ private int H1, H2, H3, H4, H5;
+
+ private int[] X = new int[80];
+ private int xOff;
+
+ /**
+ * Standard constructor
+ */
+ public SHA1Digest()
+ {
+ reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public SHA1Digest(SHA1Digest t)
+ {
+ super(t);
+
+ copyIn(t);
+ }
+
+ private void copyIn(SHA1Digest t)
+ {
+ H1 = t.H1;
+ H2 = t.H2;
+ H3 = t.H3;
+ H4 = t.H4;
+ H5 = t.H5;
+
+ System.arraycopy(t.X, 0, X, 0, t.X.length);
+ xOff = t.xOff;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "SHA-1";
+ }
+
+ public int getDigestSize()
+ {
+ return DIGEST_LENGTH;
+ }
+
+ protected void processWord(
+ byte[] in,
+ int inOff)
+ {
+ // Note: Inlined for performance
+// X[xOff] = Pack.bigEndianToInt(in, inOff);
+ int n = in[ inOff] << 24;
+ n |= (in[++inOff] & 0xff) << 16;
+ n |= (in[++inOff] & 0xff) << 8;
+ n |= (in[++inOff] & 0xff);
+ X[xOff] = n;
+
+ if (++xOff == 16)
+ {
+ processBlock();
+ }
+ }
+
+ protected void processLength(
+ long bitLength)
+ {
+ if (xOff > 14)
+ {
+ processBlock();
+ }
+
+ X[14] = (int)(bitLength >>> 32);
+ X[15] = (int)(bitLength & 0xffffffff);
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ finish();
+
+ Pack.intToBigEndian(H1, out, outOff);
+ Pack.intToBigEndian(H2, out, outOff + 4);
+ Pack.intToBigEndian(H3, out, outOff + 8);
+ Pack.intToBigEndian(H4, out, outOff + 12);
+ Pack.intToBigEndian(H5, out, outOff + 16);
+
+ reset();
+
+ return DIGEST_LENGTH;
+ }
+
+ /**
+ * reset the chaining variables
+ */
+ public void reset()
+ {
+ super.reset();
+
+ H1 = 0x67452301;
+ H2 = 0xefcdab89;
+ H3 = 0x98badcfe;
+ H4 = 0x10325476;
+ H5 = 0xc3d2e1f0;
+
+ xOff = 0;
+ for (int i = 0; i != X.length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ //
+ // Additive constants
+ //
+ private static final int Y1 = 0x5a827999;
+ private static final int Y2 = 0x6ed9eba1;
+ private static final int Y3 = 0x8f1bbcdc;
+ private static final int Y4 = 0xca62c1d6;
+
+ private int f(
+ int u,
+ int v,
+ int w)
+ {
+ return ((u & v) | ((~u) & w));
+ }
+
+ private int h(
+ int u,
+ int v,
+ int w)
+ {
+ return (u ^ v ^ w);
+ }
+
+ private int g(
+ int u,
+ int v,
+ int w)
+ {
+ return ((u & v) | (u & w) | (v & w));
+ }
+
+ protected void processBlock()
+ {
+ //
+ // expand 16 word block into 80 word block.
+ //
+ for (int i = 16; i < 80; i++)
+ {
+ int t = X[i - 3] ^ X[i - 8] ^ X[i - 14] ^ X[i - 16];
+ X[i] = t << 1 | t >>> 31;
+ }
+
+ //
+ // set up working variables.
+ //
+ int A = H1;
+ int B = H2;
+ int C = H3;
+ int D = H4;
+ int E = H5;
+
+ //
+ // round 1
+ //
+ int idx = 0;
+
+ for (int j = 0; j < 4; j++)
+ {
+ // E = rotateLeft(A, 5) + f(B, C, D) + E + X[idx++] + Y1
+ // B = rotateLeft(B, 30)
+ E += (A << 5 | A >>> 27) + f(B, C, D) + X[idx++] + Y1;
+ B = B << 30 | B >>> 2;
+
+ D += (E << 5 | E >>> 27) + f(A, B, C) + X[idx++] + Y1;
+ A = A << 30 | A >>> 2;
+
+ C += (D << 5 | D >>> 27) + f(E, A, B) + X[idx++] + Y1;
+ E = E << 30 | E >>> 2;
+
+ B += (C << 5 | C >>> 27) + f(D, E, A) + X[idx++] + Y1;
+ D = D << 30 | D >>> 2;
+
+ A += (B << 5 | B >>> 27) + f(C, D, E) + X[idx++] + Y1;
+ C = C << 30 | C >>> 2;
+ }
+
+ //
+ // round 2
+ //
+ for (int j = 0; j < 4; j++)
+ {
+ // E = rotateLeft(A, 5) + h(B, C, D) + E + X[idx++] + Y2
+ // B = rotateLeft(B, 30)
+ E += (A << 5 | A >>> 27) + h(B, C, D) + X[idx++] + Y2;
+ B = B << 30 | B >>> 2;
+
+ D += (E << 5 | E >>> 27) + h(A, B, C) + X[idx++] + Y2;
+ A = A << 30 | A >>> 2;
+
+ C += (D << 5 | D >>> 27) + h(E, A, B) + X[idx++] + Y2;
+ E = E << 30 | E >>> 2;
+
+ B += (C << 5 | C >>> 27) + h(D, E, A) + X[idx++] + Y2;
+ D = D << 30 | D >>> 2;
+
+ A += (B << 5 | B >>> 27) + h(C, D, E) + X[idx++] + Y2;
+ C = C << 30 | C >>> 2;
+ }
+
+ //
+ // round 3
+ //
+ for (int j = 0; j < 4; j++)
+ {
+ // E = rotateLeft(A, 5) + g(B, C, D) + E + X[idx++] + Y3
+ // B = rotateLeft(B, 30)
+ E += (A << 5 | A >>> 27) + g(B, C, D) + X[idx++] + Y3;
+ B = B << 30 | B >>> 2;
+
+ D += (E << 5 | E >>> 27) + g(A, B, C) + X[idx++] + Y3;
+ A = A << 30 | A >>> 2;
+
+ C += (D << 5 | D >>> 27) + g(E, A, B) + X[idx++] + Y3;
+ E = E << 30 | E >>> 2;
+
+ B += (C << 5 | C >>> 27) + g(D, E, A) + X[idx++] + Y3;
+ D = D << 30 | D >>> 2;
+
+ A += (B << 5 | B >>> 27) + g(C, D, E) + X[idx++] + Y3;
+ C = C << 30 | C >>> 2;
+ }
+
+ //
+ // round 4
+ //
+ for (int j = 0; j <= 3; j++)
+ {
+ // E = rotateLeft(A, 5) + h(B, C, D) + E + X[idx++] + Y4
+ // B = rotateLeft(B, 30)
+ E += (A << 5 | A >>> 27) + h(B, C, D) + X[idx++] + Y4;
+ B = B << 30 | B >>> 2;
+
+ D += (E << 5 | E >>> 27) + h(A, B, C) + X[idx++] + Y4;
+ A = A << 30 | A >>> 2;
+
+ C += (D << 5 | D >>> 27) + h(E, A, B) + X[idx++] + Y4;
+ E = E << 30 | E >>> 2;
+
+ B += (C << 5 | C >>> 27) + h(D, E, A) + X[idx++] + Y4;
+ D = D << 30 | D >>> 2;
+
+ A += (B << 5 | B >>> 27) + h(C, D, E) + X[idx++] + Y4;
+ C = C << 30 | C >>> 2;
+ }
+
+
+ H1 += A;
+ H2 += B;
+ H3 += C;
+ H4 += D;
+ H5 += E;
+
+ //
+ // reset start of the buffer.
+ //
+ xOff = 0;
+ for (int i = 0; i < 16; i++)
+ {
+ 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/core/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java b/core/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java
new file mode 100644
index 00000000..d430321b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java
@@ -0,0 +1,311 @@
+package org.bouncycastle.crypto.digests;
+
+
+import org.bouncycastle.crypto.util.Pack;
+import org.bouncycastle.util.Memoable;
+
+
+/**
+ * SHA-224 as described in RFC 3874
+ * <pre>
+ * block word digest
+ * SHA-1 512 32 160
+ * SHA-224 512 32 224
+ * SHA-256 512 32 256
+ * SHA-384 1024 64 384
+ * SHA-512 1024 64 512
+ * </pre>
+ */
+public class SHA224Digest
+ extends GeneralDigest
+{
+ private static final int DIGEST_LENGTH = 28;
+
+ private int H1, H2, H3, H4, H5, H6, H7, H8;
+
+ private int[] X = new int[64];
+ private int xOff;
+
+ /**
+ * Standard constructor
+ */
+ public SHA224Digest()
+ {
+ reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public SHA224Digest(SHA224Digest t)
+ {
+ super(t);
+
+ doCopy(t);
+ }
+
+ private void doCopy(SHA224Digest t)
+ {
+ super.copyIn(t);
+
+ H1 = t.H1;
+ H2 = t.H2;
+ H3 = t.H3;
+ H4 = t.H4;
+ H5 = t.H5;
+ H6 = t.H6;
+ H7 = t.H7;
+ H8 = t.H8;
+
+ System.arraycopy(t.X, 0, X, 0, t.X.length);
+ xOff = t.xOff;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "SHA-224";
+ }
+
+ public int getDigestSize()
+ {
+ return DIGEST_LENGTH;
+ }
+
+ protected void processWord(
+ byte[] in,
+ int inOff)
+ {
+ // Note: Inlined for performance
+// X[xOff] = Pack.bigEndianToInt(in, inOff);
+ int n = in[ inOff] << 24;
+ n |= (in[++inOff] & 0xff) << 16;
+ n |= (in[++inOff] & 0xff) << 8;
+ n |= (in[++inOff] & 0xff);
+ X[xOff] = n;
+
+ if (++xOff == 16)
+ {
+ processBlock();
+ }
+ }
+
+ protected void processLength(
+ long bitLength)
+ {
+ if (xOff > 14)
+ {
+ processBlock();
+ }
+
+ X[14] = (int)(bitLength >>> 32);
+ X[15] = (int)(bitLength & 0xffffffff);
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ finish();
+
+ Pack.intToBigEndian(H1, out, outOff);
+ Pack.intToBigEndian(H2, out, outOff + 4);
+ Pack.intToBigEndian(H3, out, outOff + 8);
+ Pack.intToBigEndian(H4, out, outOff + 12);
+ Pack.intToBigEndian(H5, out, outOff + 16);
+ Pack.intToBigEndian(H6, out, outOff + 20);
+ Pack.intToBigEndian(H7, out, outOff + 24);
+
+ reset();
+
+ return DIGEST_LENGTH;
+ }
+
+ /**
+ * reset the chaining variables
+ */
+ public void reset()
+ {
+ super.reset();
+
+ /* SHA-224 initial hash value
+ */
+
+ H1 = 0xc1059ed8;
+ H2 = 0x367cd507;
+ H3 = 0x3070dd17;
+ H4 = 0xf70e5939;
+ H5 = 0xffc00b31;
+ H6 = 0x68581511;
+ H7 = 0x64f98fa7;
+ H8 = 0xbefa4fa4;
+
+ xOff = 0;
+ for (int i = 0; i != X.length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ protected void processBlock()
+ {
+ //
+ // expand 16 word block into 64 word blocks.
+ //
+ for (int t = 16; t <= 63; t++)
+ {
+ X[t] = Theta1(X[t - 2]) + X[t - 7] + Theta0(X[t - 15]) + X[t - 16];
+ }
+
+ //
+ // set up working variables.
+ //
+ int a = H1;
+ int b = H2;
+ int c = H3;
+ int d = H4;
+ int e = H5;
+ int f = H6;
+ int g = H7;
+ int h = H8;
+
+
+ int t = 0;
+ for(int i = 0; i < 8; i ++)
+ {
+ // t = 8 * i
+ h += Sum1(e) + Ch(e, f, g) + K[t] + X[t];
+ d += h;
+ h += Sum0(a) + Maj(a, b, c);
+ ++t;
+
+ // t = 8 * i + 1
+ g += Sum1(d) + Ch(d, e, f) + K[t] + X[t];
+ c += g;
+ g += Sum0(h) + Maj(h, a, b);
+ ++t;
+
+ // t = 8 * i + 2
+ f += Sum1(c) + Ch(c, d, e) + K[t] + X[t];
+ b += f;
+ f += Sum0(g) + Maj(g, h, a);
+ ++t;
+
+ // t = 8 * i + 3
+ e += Sum1(b) + Ch(b, c, d) + K[t] + X[t];
+ a += e;
+ e += Sum0(f) + Maj(f, g, h);
+ ++t;
+
+ // t = 8 * i + 4
+ d += Sum1(a) + Ch(a, b, c) + K[t] + X[t];
+ h += d;
+ d += Sum0(e) + Maj(e, f, g);
+ ++t;
+
+ // t = 8 * i + 5
+ c += Sum1(h) + Ch(h, a, b) + K[t] + X[t];
+ g += c;
+ c += Sum0(d) + Maj(d, e, f);
+ ++t;
+
+ // t = 8 * i + 6
+ b += Sum1(g) + Ch(g, h, a) + K[t] + X[t];
+ f += b;
+ b += Sum0(c) + Maj(c, d, e);
+ ++t;
+
+ // t = 8 * i + 7
+ a += Sum1(f) + Ch(f, g, h) + K[t] + X[t];
+ e += a;
+ a += Sum0(b) + Maj(b, c, d);
+ ++t;
+ }
+
+ H1 += a;
+ H2 += b;
+ H3 += c;
+ H4 += d;
+ H5 += e;
+ H6 += f;
+ H7 += g;
+ H8 += h;
+
+ //
+ // reset the offset and clean out the word buffer.
+ //
+ xOff = 0;
+ for (int i = 0; i < 16; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ /* SHA-224 functions */
+ private int Ch(
+ int x,
+ int y,
+ int z)
+ {
+ return ((x & y) ^ ((~x) & z));
+ }
+
+ private int Maj(
+ int x,
+ int y,
+ int z)
+ {
+ return ((x & y) ^ (x & z) ^ (y & z));
+ }
+
+ private int Sum0(
+ int x)
+ {
+ return ((x >>> 2) | (x << 30)) ^ ((x >>> 13) | (x << 19)) ^ ((x >>> 22) | (x << 10));
+ }
+
+ private int Sum1(
+ int x)
+ {
+ return ((x >>> 6) | (x << 26)) ^ ((x >>> 11) | (x << 21)) ^ ((x >>> 25) | (x << 7));
+ }
+
+ private int Theta0(
+ int x)
+ {
+ return ((x >>> 7) | (x << 25)) ^ ((x >>> 18) | (x << 14)) ^ (x >>> 3);
+ }
+
+ private int Theta1(
+ int x)
+ {
+ return ((x >>> 17) | (x << 15)) ^ ((x >>> 19) | (x << 13)) ^ (x >>> 10);
+ }
+
+ /* SHA-224 Constants
+ * (represent the first 32 bits of the fractional parts of the
+ * cube roots of the first sixty-four prime numbers)
+ */
+ static final int K[] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 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/core/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java b/core/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java
new file mode 100644
index 00000000..a2ceda3d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java
@@ -0,0 +1,314 @@
+package org.bouncycastle.crypto.digests;
+
+
+import org.bouncycastle.crypto.util.Pack;
+import org.bouncycastle.util.Memoable;
+
+
+/**
+ * FIPS 180-2 implementation of SHA-256.
+ *
+ * <pre>
+ * block word digest
+ * SHA-1 512 32 160
+ * SHA-256 512 32 256
+ * SHA-384 1024 64 384
+ * SHA-512 1024 64 512
+ * </pre>
+ */
+public class SHA256Digest
+ extends GeneralDigest
+{
+ private static final int DIGEST_LENGTH = 32;
+
+ private int H1, H2, H3, H4, H5, H6, H7, H8;
+
+ private int[] X = new int[64];
+ private int xOff;
+
+ /**
+ * Standard constructor
+ */
+ public SHA256Digest()
+ {
+ reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public SHA256Digest(SHA256Digest t)
+ {
+ super(t);
+
+ copyIn(t);
+ }
+
+ private void copyIn(SHA256Digest t)
+ {
+ super.copyIn(t);
+
+ H1 = t.H1;
+ H2 = t.H2;
+ H3 = t.H3;
+ H4 = t.H4;
+ H5 = t.H5;
+ H6 = t.H6;
+ H7 = t.H7;
+ H8 = t.H8;
+
+ System.arraycopy(t.X, 0, X, 0, t.X.length);
+ xOff = t.xOff;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "SHA-256";
+ }
+
+ public int getDigestSize()
+ {
+ return DIGEST_LENGTH;
+ }
+
+ protected void processWord(
+ byte[] in,
+ int inOff)
+ {
+ // Note: Inlined for performance
+// X[xOff] = Pack.bigEndianToInt(in, inOff);
+ int n = in[inOff] << 24;
+ n |= (in[++inOff] & 0xff) << 16;
+ n |= (in[++inOff] & 0xff) << 8;
+ n |= (in[++inOff] & 0xff);
+ X[xOff] = n;
+
+ if (++xOff == 16)
+ {
+ processBlock();
+ }
+ }
+
+ protected void processLength(
+ long bitLength)
+ {
+ if (xOff > 14)
+ {
+ processBlock();
+ }
+
+ X[14] = (int)(bitLength >>> 32);
+ X[15] = (int)(bitLength & 0xffffffff);
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ finish();
+
+ Pack.intToBigEndian(H1, out, outOff);
+ Pack.intToBigEndian(H2, out, outOff + 4);
+ Pack.intToBigEndian(H3, out, outOff + 8);
+ Pack.intToBigEndian(H4, out, outOff + 12);
+ Pack.intToBigEndian(H5, out, outOff + 16);
+ Pack.intToBigEndian(H6, out, outOff + 20);
+ Pack.intToBigEndian(H7, out, outOff + 24);
+ Pack.intToBigEndian(H8, out, outOff + 28);
+
+ reset();
+
+ return DIGEST_LENGTH;
+ }
+
+ /**
+ * reset the chaining variables
+ */
+ public void reset()
+ {
+ super.reset();
+
+ /* SHA-256 initial hash value
+ * The first 32 bits of the fractional parts of the square roots
+ * of the first eight prime numbers
+ */
+
+ H1 = 0x6a09e667;
+ H2 = 0xbb67ae85;
+ H3 = 0x3c6ef372;
+ H4 = 0xa54ff53a;
+ H5 = 0x510e527f;
+ H6 = 0x9b05688c;
+ H7 = 0x1f83d9ab;
+ H8 = 0x5be0cd19;
+
+ xOff = 0;
+ for (int i = 0; i != X.length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ protected void processBlock()
+ {
+ //
+ // expand 16 word block into 64 word blocks.
+ //
+ for (int t = 16; t <= 63; t++)
+ {
+ X[t] = Theta1(X[t - 2]) + X[t - 7] + Theta0(X[t - 15]) + X[t - 16];
+ }
+
+ //
+ // set up working variables.
+ //
+ int a = H1;
+ int b = H2;
+ int c = H3;
+ int d = H4;
+ int e = H5;
+ int f = H6;
+ int g = H7;
+ int h = H8;
+
+ int t = 0;
+ for(int i = 0; i < 8; i ++)
+ {
+ // t = 8 * i
+ h += Sum1(e) + Ch(e, f, g) + K[t] + X[t];
+ d += h;
+ h += Sum0(a) + Maj(a, b, c);
+ ++t;
+
+ // t = 8 * i + 1
+ g += Sum1(d) + Ch(d, e, f) + K[t] + X[t];
+ c += g;
+ g += Sum0(h) + Maj(h, a, b);
+ ++t;
+
+ // t = 8 * i + 2
+ f += Sum1(c) + Ch(c, d, e) + K[t] + X[t];
+ b += f;
+ f += Sum0(g) + Maj(g, h, a);
+ ++t;
+
+ // t = 8 * i + 3
+ e += Sum1(b) + Ch(b, c, d) + K[t] + X[t];
+ a += e;
+ e += Sum0(f) + Maj(f, g, h);
+ ++t;
+
+ // t = 8 * i + 4
+ d += Sum1(a) + Ch(a, b, c) + K[t] + X[t];
+ h += d;
+ d += Sum0(e) + Maj(e, f, g);
+ ++t;
+
+ // t = 8 * i + 5
+ c += Sum1(h) + Ch(h, a, b) + K[t] + X[t];
+ g += c;
+ c += Sum0(d) + Maj(d, e, f);
+ ++t;
+
+ // t = 8 * i + 6
+ b += Sum1(g) + Ch(g, h, a) + K[t] + X[t];
+ f += b;
+ b += Sum0(c) + Maj(c, d, e);
+ ++t;
+
+ // t = 8 * i + 7
+ a += Sum1(f) + Ch(f, g, h) + K[t] + X[t];
+ e += a;
+ a += Sum0(b) + Maj(b, c, d);
+ ++t;
+ }
+
+ H1 += a;
+ H2 += b;
+ H3 += c;
+ H4 += d;
+ H5 += e;
+ H6 += f;
+ H7 += g;
+ H8 += h;
+
+ //
+ // reset the offset and clean out the word buffer.
+ //
+ xOff = 0;
+ for (int i = 0; i < 16; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ /* SHA-256 functions */
+ private int Ch(
+ int x,
+ int y,
+ int z)
+ {
+ return (x & y) ^ ((~x) & z);
+ }
+
+ private int Maj(
+ int x,
+ int y,
+ int z)
+ {
+ return (x & y) ^ (x & z) ^ (y & z);
+ }
+
+ private int Sum0(
+ int x)
+ {
+ return ((x >>> 2) | (x << 30)) ^ ((x >>> 13) | (x << 19)) ^ ((x >>> 22) | (x << 10));
+ }
+
+ private int Sum1(
+ int x)
+ {
+ return ((x >>> 6) | (x << 26)) ^ ((x >>> 11) | (x << 21)) ^ ((x >>> 25) | (x << 7));
+ }
+
+ private int Theta0(
+ int x)
+ {
+ return ((x >>> 7) | (x << 25)) ^ ((x >>> 18) | (x << 14)) ^ (x >>> 3);
+ }
+
+ private int Theta1(
+ int x)
+ {
+ return ((x >>> 17) | (x << 15)) ^ ((x >>> 19) | (x << 13)) ^ (x >>> 10);
+ }
+
+ /* SHA-256 Constants
+ * (represent the first 32 bits of the fractional parts of the
+ * cube roots of the first sixty-four prime numbers)
+ */
+ static final int K[] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 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 SHA256Digest(this);
+ }
+
+ public void reset(Memoable other)
+ {
+ SHA256Digest d = (SHA256Digest)other;
+
+ copyIn(d);
+ }
+}
+
diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java b/core/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java
new file mode 100644
index 00000000..75d195d4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java
@@ -0,0 +1,99 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.util.Pack;
+import org.bouncycastle.util.Memoable;
+
+
+/**
+ * FIPS 180-2 implementation of SHA-384.
+ *
+ * <pre>
+ * block word digest
+ * SHA-1 512 32 160
+ * SHA-256 512 32 256
+ * SHA-384 1024 64 384
+ * SHA-512 1024 64 512
+ * </pre>
+ */
+public class SHA384Digest
+ extends LongDigest
+{
+ private static final int DIGEST_LENGTH = 48;
+
+ /**
+ * Standard constructor
+ */
+ public SHA384Digest()
+ {
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public SHA384Digest(SHA384Digest t)
+ {
+ super(t);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "SHA-384";
+ }
+
+ public int getDigestSize()
+ {
+ return DIGEST_LENGTH;
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ finish();
+
+ Pack.longToBigEndian(H1, out, outOff);
+ Pack.longToBigEndian(H2, out, outOff + 8);
+ Pack.longToBigEndian(H3, out, outOff + 16);
+ Pack.longToBigEndian(H4, out, outOff + 24);
+ Pack.longToBigEndian(H5, out, outOff + 32);
+ Pack.longToBigEndian(H6, out, outOff + 40);
+
+ reset();
+
+ return DIGEST_LENGTH;
+ }
+
+ /**
+ * reset the chaining variables
+ */
+ public void reset()
+ {
+ super.reset();
+
+ /* SHA-384 initial hash value
+ * The first 64 bits of the fractional parts of the square roots
+ * of the 9th through 16th prime numbers
+ */
+ H1 = 0xcbbb9d5dc1059ed8l;
+ H2 = 0x629a292a367cd507l;
+ H3 = 0x9159015a3070dd17l;
+ H4 = 0x152fecd8f70e5939l;
+ H5 = 0x67332667ffc00b31l;
+ H6 = 0x8eb44a8768581511l;
+ 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/core/src/main/java/org/bouncycastle/crypto/digests/SHA3Digest.java b/core/src/main/java/org/bouncycastle/crypto/digests/SHA3Digest.java
new file mode 100644
index 00000000..15eb77ce
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/SHA3Digest.java
@@ -0,0 +1,547 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.ExtendedDigest;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * implementation of SHA-3 based on following KeccakNISTInterface.c from http://keccak.noekeon.org/
+ * <p/>
+ * Following the naming conventions used in the C source code to enable easy review of the implementation.
+ */
+public class SHA3Digest
+ implements ExtendedDigest
+{
+ private static long[] KeccakRoundConstants = keccakInitializeRoundConstants();
+
+ private static int[] KeccakRhoOffsets = keccakInitializeRhoOffsets();
+
+ private static long[] keccakInitializeRoundConstants()
+ {
+ long[] keccakRoundConstants = new long[24];
+ byte[] LFSRstate = new byte[1];
+
+ LFSRstate[0] = 0x01;
+ int i, j, bitPosition;
+
+ for (i = 0; i < 24; i++)
+ {
+ keccakRoundConstants[i] = 0;
+ for (j = 0; j < 7; j++)
+ {
+ bitPosition = (1 << j) - 1;
+ if (LFSR86540(LFSRstate))
+ {
+ keccakRoundConstants[i] ^= 1L << bitPosition;
+ }
+ }
+ }
+
+ return keccakRoundConstants;
+ }
+
+ private static boolean LFSR86540(byte[] LFSR)
+ {
+ boolean result = (((LFSR[0]) & 0x01) != 0);
+ if (((LFSR[0]) & 0x80) != 0)
+ {
+ LFSR[0] = (byte)(((LFSR[0]) << 1) ^ 0x71);
+ }
+ else
+ {
+ LFSR[0] <<= 1;
+ }
+
+ return result;
+ }
+
+ private static int[] keccakInitializeRhoOffsets()
+ {
+ int[] keccakRhoOffsets = new int[25];
+ int x, y, t, newX, newY;
+
+ keccakRhoOffsets[(((0) % 5) + 5 * ((0) % 5))] = 0;
+ x = 1;
+ y = 0;
+ for (t = 0; t < 24; t++)
+ {
+ keccakRhoOffsets[(((x) % 5) + 5 * ((y) % 5))] = ((t + 1) * (t + 2) / 2) % 64;
+ newX = (0 * x + 1 * y) % 5;
+ newY = (2 * x + 3 * y) % 5;
+ x = newX;
+ y = newY;
+ }
+
+ return keccakRhoOffsets;
+ }
+
+ private byte[] state = new byte[(1600 / 8)];
+ private byte[] dataQueue = new byte[(1536 / 8)];
+ private int rate;
+ private int bitsInQueue;
+ private int fixedOutputLength;
+ private boolean squeezing;
+ private int bitsAvailableForSqueezing;
+ private byte[] chunk;
+ private byte[] oneByte;
+
+ private void clearDataQueueSection(int off, int len)
+ {
+ for (int i = off; i != off + len; i++)
+ {
+ dataQueue[i] = 0;
+ }
+ }
+
+ public SHA3Digest()
+ {
+ init(0);
+ }
+
+ public SHA3Digest(int bitLength)
+ {
+ init(bitLength);
+ }
+
+ public SHA3Digest(SHA3Digest source) {
+ System.arraycopy(source.state, 0, this.state, 0, source.state.length);
+ System.arraycopy(source.dataQueue, 0, this.dataQueue, 0, source.dataQueue.length);
+ this.rate = source.rate;
+ this.bitsInQueue = source.bitsInQueue;
+ this.fixedOutputLength = source.fixedOutputLength;
+ this.squeezing = source.squeezing;
+ this.bitsAvailableForSqueezing = source.bitsAvailableForSqueezing;
+ this.chunk = Arrays.clone(source.chunk);
+ this.oneByte = Arrays.clone(source.oneByte);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "SHA3-" + fixedOutputLength;
+ }
+
+ public int getDigestSize()
+ {
+ return fixedOutputLength / 8;
+ }
+
+ public void update(byte in)
+ {
+ oneByte[0] = in;
+
+ doUpdate(oneByte, 0, 8L);
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ {
+ doUpdate(in, inOff, len * 8L);
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ {
+ squeeze(out, outOff, fixedOutputLength);
+
+ reset();
+
+ return getDigestSize();
+ }
+
+ public void reset()
+ {
+ init(fixedOutputLength);
+ }
+
+ /**
+ * Return the size of block that the compression function is applied to in bytes.
+ *
+ * @return internal byte length of a block.
+ */
+ public int getByteLength()
+ {
+ return rate / 8;
+ }
+
+ private void init(int bitLength)
+ {
+ switch (bitLength)
+ {
+ case 0:
+ case 288:
+ initSponge(1024, 576);
+ break;
+ case 224:
+ initSponge(1152, 448);
+ break;
+ case 256:
+ initSponge(1088, 512);
+ break;
+ case 384:
+ initSponge(832, 768);
+ break;
+ case 512:
+ initSponge(576, 1024);
+ break;
+ default:
+ throw new IllegalArgumentException("bitLength must be one of 224, 256, 384, or 512.");
+ }
+ }
+
+ private void doUpdate(byte[] data, int off, long databitlen)
+ {
+ if ((databitlen % 8) == 0)
+ {
+ absorb(data, off, databitlen);
+ }
+ else
+ {
+ absorb(data, off, databitlen - (databitlen % 8));
+
+ byte[] lastByte = new byte[1];
+
+ lastByte[0] = (byte)(data[off + (int)(databitlen / 8)] >> (8 - (databitlen % 8)));
+ absorb(lastByte, off, databitlen % 8);
+ }
+ }
+
+ private void initSponge(int rate, int capacity)
+ {
+ if (rate + capacity != 1600)
+ {
+ throw new IllegalStateException("rate + capacity != 1600");
+ }
+ if ((rate <= 0) || (rate >= 1600) || ((rate % 64) != 0))
+ {
+ throw new IllegalStateException("invalid rate value");
+ }
+
+ this.rate = rate;
+ // this is never read, need to check to see why we want to save it
+ // this.capacity = capacity;
+ this.fixedOutputLength = 0;
+ Arrays.fill(this.state, (byte)0);
+ Arrays.fill(this.dataQueue, (byte)0);
+ this.bitsInQueue = 0;
+ this.squeezing = false;
+ this.bitsAvailableForSqueezing = 0;
+ this.fixedOutputLength = capacity / 2;
+ this.chunk = new byte[rate / 8];
+ this.oneByte = new byte[1];
+ }
+
+ private void absorbQueue()
+ {
+ KeccakAbsorb(state, dataQueue, rate / 8);
+
+ bitsInQueue = 0;
+ }
+
+ private void absorb(byte[] data, int off, long databitlen)
+ {
+ long i, j, wholeBlocks;
+
+ if ((bitsInQueue % 8) != 0)
+ {
+ throw new IllegalStateException("attempt to absorb with odd length queue.");
+ }
+ if (squeezing)
+ {
+ throw new IllegalStateException("attempt to absorb while squeezing.");
+ }
+
+ i = 0;
+ while (i < databitlen)
+ {
+ if ((bitsInQueue == 0) && (databitlen >= rate) && (i <= (databitlen - rate)))
+ {
+ wholeBlocks = (databitlen - i) / rate;
+
+ for (j = 0; j < wholeBlocks; j++)
+ {
+ System.arraycopy(data, (int)(off + (i / 8) + (j * chunk.length)), chunk, 0, chunk.length);
+
+// displayIntermediateValues.displayBytes(1, "Block to be absorbed", curData, rate / 8);
+
+ KeccakAbsorb(state, chunk, chunk.length);
+ }
+
+ i += wholeBlocks * rate;
+ }
+ else
+ {
+ int partialBlock = (int)(databitlen - i);
+ if (partialBlock + bitsInQueue > rate)
+ {
+ partialBlock = rate - bitsInQueue;
+ }
+ int partialByte = partialBlock % 8;
+ partialBlock -= partialByte;
+ System.arraycopy(data, off + (int)(i / 8), dataQueue, bitsInQueue / 8, partialBlock / 8);
+
+ bitsInQueue += partialBlock;
+ i += partialBlock;
+ if (bitsInQueue == rate)
+ {
+ absorbQueue();
+ }
+ if (partialByte > 0)
+ {
+ int mask = (1 << partialByte) - 1;
+ dataQueue[bitsInQueue / 8] = (byte)(data[off + ((int)(i / 8))] & mask);
+ bitsInQueue += partialByte;
+ i += partialByte;
+ }
+ }
+ }
+ }
+
+ private void padAndSwitchToSqueezingPhase()
+ {
+ if (bitsInQueue + 1 == rate)
+ {
+ dataQueue[bitsInQueue / 8] |= 1 << (bitsInQueue % 8);
+ absorbQueue();
+ clearDataQueueSection(0, rate / 8);
+ }
+ else
+ {
+ clearDataQueueSection((bitsInQueue + 7) / 8, rate / 8 - (bitsInQueue + 7) / 8);
+ dataQueue[bitsInQueue / 8] |= 1 << (bitsInQueue % 8);
+ }
+ dataQueue[(rate - 1) / 8] |= 1 << ((rate - 1) % 8);
+ absorbQueue();
+
+
+// displayIntermediateValues.displayText(1, "--- Switching to squeezing phase ---");
+
+
+ if (rate == 1024)
+ {
+ KeccakExtract1024bits(state, dataQueue);
+ bitsAvailableForSqueezing = 1024;
+ }
+ else
+
+ {
+ KeccakExtract(state, dataQueue, rate / 64);
+ bitsAvailableForSqueezing = rate;
+ }
+
+// displayIntermediateValues.displayBytes(1, "Block available for squeezing", dataQueue, bitsAvailableForSqueezing / 8);
+
+ squeezing = true;
+ }
+
+ private void squeeze(byte[] output, int offset, long outputLength)
+ {
+ long i;
+ int partialBlock;
+
+ if (!squeezing)
+ {
+ padAndSwitchToSqueezingPhase();
+ }
+ if ((outputLength % 8) != 0)
+ {
+ throw new IllegalStateException("outputLength not a multiple of 8");
+ }
+
+ i = 0;
+ while (i < outputLength)
+ {
+ if (bitsAvailableForSqueezing == 0)
+ {
+ keccakPermutation(state);
+
+ if (rate == 1024)
+ {
+ KeccakExtract1024bits(state, dataQueue);
+ bitsAvailableForSqueezing = 1024;
+ }
+ else
+
+ {
+ KeccakExtract(state, dataQueue, rate / 64);
+ bitsAvailableForSqueezing = rate;
+ }
+
+// displayIntermediateValues.displayBytes(1, "Block available for squeezing", dataQueue, bitsAvailableForSqueezing / 8);
+
+ }
+ partialBlock = bitsAvailableForSqueezing;
+ if ((long)partialBlock > outputLength - i)
+ {
+ partialBlock = (int)(outputLength - i);
+ }
+
+ System.arraycopy(dataQueue, (rate - bitsAvailableForSqueezing) / 8, output, offset + (int)(i / 8), partialBlock / 8);
+ bitsAvailableForSqueezing -= partialBlock;
+ i += partialBlock;
+ }
+ }
+
+ private void fromBytesToWords(long[] stateAsWords, byte[] state)
+ {
+ for (int i = 0; i < (1600 / 64); i++)
+ {
+ stateAsWords[i] = 0;
+ int index = i * (64 / 8);
+ for (int j = 0; j < (64 / 8); j++)
+ {
+ stateAsWords[i] |= ((long)state[index + j] & 0xff) << ((8 * j));
+ }
+ }
+ }
+
+ private void fromWordsToBytes(byte[] state, long[] stateAsWords)
+ {
+ for (int i = 0; i < (1600 / 64); i++)
+ {
+ int index = i * (64 / 8);
+ for (int j = 0; j < (64 / 8); j++)
+ {
+ state[index + j] = (byte)((stateAsWords[i] >>> ((8 * j))) & 0xFF);
+ }
+ }
+ }
+
+ private void keccakPermutation(byte[] state)
+ {
+ long[] longState = new long[state.length / 8];
+
+ fromBytesToWords(longState, state);
+
+// displayIntermediateValues.displayStateAsBytes(1, "Input of permutation", longState);
+
+ keccakPermutationOnWords(longState);
+
+// displayIntermediateValues.displayStateAsBytes(1, "State after permutation", longState);
+
+ fromWordsToBytes(state, longState);
+ }
+
+ private void keccakPermutationAfterXor(byte[] state, byte[] data, int dataLengthInBytes)
+ {
+ int i;
+
+ for (i = 0; i < dataLengthInBytes; i++)
+ {
+ state[i] ^= data[i];
+ }
+
+ keccakPermutation(state);
+ }
+
+ private void keccakPermutationOnWords(long[] state)
+ {
+ int i;
+
+// displayIntermediateValues.displayStateAs64bitWords(3, "Same, with lanes as 64-bit words", state);
+
+ for (i = 0; i < 24; i++)
+ {
+// displayIntermediateValues.displayRoundNumber(3, i);
+
+ theta(state);
+// displayIntermediateValues.displayStateAs64bitWords(3, "After theta", state);
+
+ rho(state);
+// displayIntermediateValues.displayStateAs64bitWords(3, "After rho", state);
+
+ pi(state);
+// displayIntermediateValues.displayStateAs64bitWords(3, "After pi", state);
+
+ chi(state);
+// displayIntermediateValues.displayStateAs64bitWords(3, "After chi", state);
+
+ iota(state, i);
+// displayIntermediateValues.displayStateAs64bitWords(3, "After iota", state);
+ }
+ }
+
+ long[] C = new long[5];
+
+ private void theta(long[] A)
+ {
+ for (int x = 0; x < 5; x++)
+ {
+ C[x] = 0;
+ for (int y = 0; y < 5; y++)
+ {
+ C[x] ^= A[x + 5 * y];
+ }
+ }
+ for (int x = 0; x < 5; x++)
+ {
+ long dX = ((((C[(x + 1) % 5]) << 1) ^ ((C[(x + 1) % 5]) >>> (64 - 1)))) ^ C[(x + 4) % 5];
+ for (int y = 0; y < 5; y++)
+ {
+ A[x + 5 * y] ^= dX;
+ }
+ }
+ }
+
+ private void rho(long[] A)
+ {
+ for (int x = 0; x < 5; x++)
+ {
+ for (int y = 0; y < 5; y++)
+ {
+ int index = x + 5 * y;
+ A[index] = ((KeccakRhoOffsets[index] != 0) ? (((A[index]) << KeccakRhoOffsets[index]) ^ ((A[index]) >>> (64 - KeccakRhoOffsets[index]))) : A[index]);
+ }
+ }
+ }
+
+ long[] tempA = new long[25];
+
+ private void pi(long[] A)
+ {
+ System.arraycopy(A, 0, tempA, 0, tempA.length);
+
+ for (int x = 0; x < 5; x++)
+ {
+ for (int y = 0; y < 5; y++)
+ {
+ A[y + 5 * ((2 * x + 3 * y) % 5)] = tempA[x + 5 * y];
+ }
+ }
+ }
+
+ long[] chiC = new long[5];
+
+ private void chi(long[] A)
+ {
+ for (int y = 0; y < 5; y++)
+ {
+ for (int x = 0; x < 5; x++)
+ {
+ chiC[x] = A[x + 5 * y] ^ ((~A[(((x + 1) % 5) + 5 * y)]) & A[(((x + 2) % 5) + 5 * y)]);
+ }
+ for (int x = 0; x < 5; x++)
+ {
+ A[x + 5 * y] = chiC[x];
+ }
+ }
+ }
+
+ private void iota(long[] A, int indexRound)
+ {
+ A[(((0) % 5) + 5 * ((0) % 5))] ^= KeccakRoundConstants[indexRound];
+ }
+
+ private void KeccakAbsorb(byte[] byteState, byte[] data, int dataInBytes)
+ {
+ keccakPermutationAfterXor(byteState, data, dataInBytes);
+ }
+
+
+ private void KeccakExtract1024bits(byte[] byteState, byte[] data)
+ {
+ System.arraycopy(byteState, 0, data, 0, 128);
+ }
+
+
+ private void KeccakExtract(byte[] byteState, byte[] data, int laneCount)
+ {
+ System.arraycopy(byteState, 0, data, 0, laneCount * 8);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java b/core/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java
new file mode 100644
index 00000000..7db63ad2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java
@@ -0,0 +1,102 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.util.Pack;
+import org.bouncycastle.util.Memoable;
+
+
+/**
+ * FIPS 180-2 implementation of SHA-512.
+ *
+ * <pre>
+ * block word digest
+ * SHA-1 512 32 160
+ * SHA-256 512 32 256
+ * SHA-384 1024 64 384
+ * SHA-512 1024 64 512
+ * </pre>
+ */
+public class SHA512Digest
+ extends LongDigest
+{
+ private static final int DIGEST_LENGTH = 64;
+
+ /**
+ * Standard constructor
+ */
+ public SHA512Digest()
+ {
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public SHA512Digest(SHA512Digest t)
+ {
+ super(t);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "SHA-512";
+ }
+
+ public int getDigestSize()
+ {
+ return DIGEST_LENGTH;
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ finish();
+
+ Pack.longToBigEndian(H1, out, outOff);
+ Pack.longToBigEndian(H2, out, outOff + 8);
+ Pack.longToBigEndian(H3, out, outOff + 16);
+ Pack.longToBigEndian(H4, out, outOff + 24);
+ Pack.longToBigEndian(H5, out, outOff + 32);
+ Pack.longToBigEndian(H6, out, outOff + 40);
+ Pack.longToBigEndian(H7, out, outOff + 48);
+ Pack.longToBigEndian(H8, out, outOff + 56);
+
+ reset();
+
+ return DIGEST_LENGTH;
+ }
+
+ /**
+ * reset the chaining variables
+ */
+ public void reset()
+ {
+ super.reset();
+
+ /* SHA-512 initial hash value
+ * The first 64 bits of the fractional parts of the square roots
+ * of the first eight prime numbers
+ */
+ H1 = 0x6a09e667f3bcc908L;
+ H2 = 0xbb67ae8584caa73bL;
+ H3 = 0x3c6ef372fe94f82bL;
+ H4 = 0xa54ff53a5f1d36f1L;
+ H5 = 0x510e527fade682d1L;
+ H6 = 0x9b05688c2b3e6c1fL;
+ 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/core/src/main/java/org/bouncycastle/crypto/digests/SHA512tDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/SHA512tDigest.java
new file mode 100644
index 00000000..46154618
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/SHA512tDigest.java
@@ -0,0 +1,205 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.util.Memoable;
+import org.bouncycastle.util.MemoableResetException;
+
+/**
+ * FIPS 180-4 implementation of SHA-512/t
+ */
+public class SHA512tDigest
+ extends LongDigest
+{
+ private final int digestLength;
+
+ private long H1t, H2t, H3t, H4t, H5t, H6t, H7t, H8t;
+
+ /**
+ * Standard constructor
+ */
+ public SHA512tDigest(int bitLength)
+ {
+ if (bitLength >= 512)
+ {
+ throw new IllegalArgumentException("bitLength cannot be >= 512");
+ }
+
+ if (bitLength % 8 != 0)
+ {
+ throw new IllegalArgumentException("bitLength needs to be a multiple of 8");
+ }
+
+ if (bitLength == 384)
+ {
+ throw new IllegalArgumentException("bitLength cannot be 384 use SHA384 instead");
+ }
+
+ this.digestLength = bitLength / 8;
+
+ tIvGenerate(digestLength * 8);
+
+ reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public SHA512tDigest(SHA512tDigest t)
+ {
+ super(t);
+
+ this.digestLength = t.digestLength;
+
+ reset(t);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "SHA-512/" + Integer.toString(digestLength * 8);
+ }
+
+ public int getDigestSize()
+ {
+ return digestLength;
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ finish();
+
+ longToBigEndian(H1, out, outOff, digestLength);
+ longToBigEndian(H2, out, outOff + 8, digestLength - 8);
+ longToBigEndian(H3, out, outOff + 16, digestLength - 16);
+ longToBigEndian(H4, out, outOff + 24, digestLength - 24);
+ longToBigEndian(H5, out, outOff + 32, digestLength - 32);
+ longToBigEndian(H6, out, outOff + 40, digestLength - 40);
+ longToBigEndian(H7, out, outOff + 48, digestLength - 48);
+ longToBigEndian(H8, out, outOff + 56, digestLength - 56);
+
+ reset();
+
+ return digestLength;
+ }
+
+ /**
+ * reset the chaining variables
+ */
+ public void reset()
+ {
+ super.reset();
+
+ /*
+ * initial hash values use the iv generation algorithm for t.
+ */
+ H1 = H1t;
+ H2 = H2t;
+ H3 = H3t;
+ H4 = H4t;
+ H5 = H5t;
+ H6 = H6t;
+ H7 = H7t;
+ H8 = H8t;
+ }
+
+ private void tIvGenerate(int bitLength)
+ {
+ H1 = 0x6a09e667f3bcc908L ^ 0xa5a5a5a5a5a5a5a5L;
+ H2 = 0xbb67ae8584caa73bL ^ 0xa5a5a5a5a5a5a5a5L;
+ H3 = 0x3c6ef372fe94f82bL ^ 0xa5a5a5a5a5a5a5a5L;
+ H4 = 0xa54ff53a5f1d36f1L ^ 0xa5a5a5a5a5a5a5a5L;
+ H5 = 0x510e527fade682d1L ^ 0xa5a5a5a5a5a5a5a5L;
+ H6 = 0x9b05688c2b3e6c1fL ^ 0xa5a5a5a5a5a5a5a5L;
+ H7 = 0x1f83d9abfb41bd6bL ^ 0xa5a5a5a5a5a5a5a5L;
+ H8 = 0x5be0cd19137e2179L ^ 0xa5a5a5a5a5a5a5a5L;
+
+ update((byte)0x53);
+ update((byte)0x48);
+ update((byte)0x41);
+ update((byte)0x2D);
+ update((byte)0x35);
+ update((byte)0x31);
+ update((byte)0x32);
+ update((byte)0x2F);
+
+ if (bitLength > 100)
+ {
+ update((byte)(bitLength / 100 + 0x30));
+ bitLength = bitLength % 100;
+ update((byte)(bitLength / 10 + 0x30));
+ bitLength = bitLength % 10;
+ update((byte)(bitLength + 0x30));
+ }
+ else if (bitLength > 10)
+ {
+ update((byte)(bitLength / 10 + 0x30));
+ bitLength = bitLength % 10;
+ update((byte)(bitLength + 0x30));
+ }
+ else
+ {
+ update((byte)(bitLength + 0x30));
+ }
+
+ finish();
+
+ H1t = H1;
+ H2t = H2;
+ H3t = H3;
+ H4t = H4;
+ H5t = H5;
+ H6t = H6;
+ H7t = H7;
+ H8t = H8;
+ }
+
+ private static void longToBigEndian(long n, byte[] bs, int off, int max)
+ {
+ if (max > 0)
+ {
+ intToBigEndian((int)(n >>> 32), bs, off, max);
+
+ if (max > 4)
+ {
+ intToBigEndian((int)(n & 0xffffffffL), bs, off + 4, max - 4);
+ }
+ }
+ }
+
+ private static void intToBigEndian(int n, byte[] bs, int off, int max)
+ {
+ int num = Math.min(4, max);
+ while (--num >= 0)
+ {
+ int shift = 8 * (3 - num);
+ 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/core/src/main/java/org/bouncycastle/crypto/digests/ShortenedDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/ShortenedDigest.java
new file mode 100644
index 00000000..89033e80
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/ShortenedDigest.java
@@ -0,0 +1,80 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.ExtendedDigest;
+
+/**
+ * Wrapper class that reduces the output length of a particular digest to
+ * only the first n bytes of the digest function.
+ */
+public class ShortenedDigest
+ implements ExtendedDigest
+{
+ private ExtendedDigest baseDigest;
+ private int length;
+
+ /**
+ * Base constructor.
+ *
+ * @param baseDigest underlying digest to use.
+ * @param length length in bytes of the output of doFinal.
+ * @exception IllegalArgumentException if baseDigest is null, or length is greater than baseDigest.getDigestSize().
+ */
+ public ShortenedDigest(
+ ExtendedDigest baseDigest,
+ int length)
+ {
+ if (baseDigest == null)
+ {
+ throw new IllegalArgumentException("baseDigest must not be null");
+ }
+
+ if (length > baseDigest.getDigestSize())
+ {
+ throw new IllegalArgumentException("baseDigest output not large enough to support length");
+ }
+
+ this.baseDigest = baseDigest;
+ this.length = length;
+ }
+
+ public String getAlgorithmName()
+ {
+ return baseDigest.getAlgorithmName() + "(" + length * 8 + ")";
+ }
+
+ public int getDigestSize()
+ {
+ return length;
+ }
+
+ 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)
+ {
+ byte[] tmp = new byte[baseDigest.getDigestSize()];
+
+ baseDigest.doFinal(tmp, 0);
+
+ System.arraycopy(tmp, 0, out, outOff, length);
+
+ return length;
+ }
+
+ public void reset()
+ {
+ baseDigest.reset();
+ }
+
+ public int getByteLength()
+ {
+ return baseDigest.getByteLength();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/TigerDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/TigerDigest.java
new file mode 100644
index 00000000..2899e305
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/TigerDigest.java
@@ -0,0 +1,879 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.ExtendedDigest;
+import org.bouncycastle.util.Memoable;
+
+/**
+ * implementation of Tiger based on:
+ * <a href="http://www.cs.technion.ac.il/~biham/Reports/Tiger">
+ * http://www.cs.technion.ac.il/~biham/Reports/Tiger</a>
+ */
+public class TigerDigest
+ implements ExtendedDigest, Memoable
+{
+ private static final int BYTE_LENGTH = 64;
+
+ /*
+ * S-Boxes.
+ */
+ private static final long[] t1 = {
+ 0x02AAB17CF7E90C5EL /* 0 */, 0xAC424B03E243A8ECL /* 1 */,
+ 0x72CD5BE30DD5FCD3L /* 2 */, 0x6D019B93F6F97F3AL /* 3 */,
+ 0xCD9978FFD21F9193L /* 4 */, 0x7573A1C9708029E2L /* 5 */,
+ 0xB164326B922A83C3L /* 6 */, 0x46883EEE04915870L /* 7 */,
+ 0xEAACE3057103ECE6L /* 8 */, 0xC54169B808A3535CL /* 9 */,
+ 0x4CE754918DDEC47CL /* 10 */, 0x0AA2F4DFDC0DF40CL /* 11 */,
+ 0x10B76F18A74DBEFAL /* 12 */, 0xC6CCB6235AD1AB6AL /* 13 */,
+ 0x13726121572FE2FFL /* 14 */, 0x1A488C6F199D921EL /* 15 */,
+ 0x4BC9F9F4DA0007CAL /* 16 */, 0x26F5E6F6E85241C7L /* 17 */,
+ 0x859079DBEA5947B6L /* 18 */, 0x4F1885C5C99E8C92L /* 19 */,
+ 0xD78E761EA96F864BL /* 20 */, 0x8E36428C52B5C17DL /* 21 */,
+ 0x69CF6827373063C1L /* 22 */, 0xB607C93D9BB4C56EL /* 23 */,
+ 0x7D820E760E76B5EAL /* 24 */, 0x645C9CC6F07FDC42L /* 25 */,
+ 0xBF38A078243342E0L /* 26 */, 0x5F6B343C9D2E7D04L /* 27 */,
+ 0xF2C28AEB600B0EC6L /* 28 */, 0x6C0ED85F7254BCACL /* 29 */,
+ 0x71592281A4DB4FE5L /* 30 */, 0x1967FA69CE0FED9FL /* 31 */,
+ 0xFD5293F8B96545DBL /* 32 */, 0xC879E9D7F2A7600BL /* 33 */,
+ 0x860248920193194EL /* 34 */, 0xA4F9533B2D9CC0B3L /* 35 */,
+ 0x9053836C15957613L /* 36 */, 0xDB6DCF8AFC357BF1L /* 37 */,
+ 0x18BEEA7A7A370F57L /* 38 */, 0x037117CA50B99066L /* 39 */,
+ 0x6AB30A9774424A35L /* 40 */, 0xF4E92F02E325249BL /* 41 */,
+ 0x7739DB07061CCAE1L /* 42 */, 0xD8F3B49CECA42A05L /* 43 */,
+ 0xBD56BE3F51382F73L /* 44 */, 0x45FAED5843B0BB28L /* 45 */,
+ 0x1C813D5C11BF1F83L /* 46 */, 0x8AF0E4B6D75FA169L /* 47 */,
+ 0x33EE18A487AD9999L /* 48 */, 0x3C26E8EAB1C94410L /* 49 */,
+ 0xB510102BC0A822F9L /* 50 */, 0x141EEF310CE6123BL /* 51 */,
+ 0xFC65B90059DDB154L /* 52 */, 0xE0158640C5E0E607L /* 53 */,
+ 0x884E079826C3A3CFL /* 54 */, 0x930D0D9523C535FDL /* 55 */,
+ 0x35638D754E9A2B00L /* 56 */, 0x4085FCCF40469DD5L /* 57 */,
+ 0xC4B17AD28BE23A4CL /* 58 */, 0xCAB2F0FC6A3E6A2EL /* 59 */,
+ 0x2860971A6B943FCDL /* 60 */, 0x3DDE6EE212E30446L /* 61 */,
+ 0x6222F32AE01765AEL /* 62 */, 0x5D550BB5478308FEL /* 63 */,
+ 0xA9EFA98DA0EDA22AL /* 64 */, 0xC351A71686C40DA7L /* 65 */,
+ 0x1105586D9C867C84L /* 66 */, 0xDCFFEE85FDA22853L /* 67 */,
+ 0xCCFBD0262C5EEF76L /* 68 */, 0xBAF294CB8990D201L /* 69 */,
+ 0xE69464F52AFAD975L /* 70 */, 0x94B013AFDF133E14L /* 71 */,
+ 0x06A7D1A32823C958L /* 72 */, 0x6F95FE5130F61119L /* 73 */,
+ 0xD92AB34E462C06C0L /* 74 */, 0xED7BDE33887C71D2L /* 75 */,
+ 0x79746D6E6518393EL /* 76 */, 0x5BA419385D713329L /* 77 */,
+ 0x7C1BA6B948A97564L /* 78 */, 0x31987C197BFDAC67L /* 79 */,
+ 0xDE6C23C44B053D02L /* 80 */, 0x581C49FED002D64DL /* 81 */,
+ 0xDD474D6338261571L /* 82 */, 0xAA4546C3E473D062L /* 83 */,
+ 0x928FCE349455F860L /* 84 */, 0x48161BBACAAB94D9L /* 85 */,
+ 0x63912430770E6F68L /* 86 */, 0x6EC8A5E602C6641CL /* 87 */,
+ 0x87282515337DDD2BL /* 88 */, 0x2CDA6B42034B701BL /* 89 */,
+ 0xB03D37C181CB096DL /* 90 */, 0xE108438266C71C6FL /* 91 */,
+ 0x2B3180C7EB51B255L /* 92 */, 0xDF92B82F96C08BBCL /* 93 */,
+ 0x5C68C8C0A632F3BAL /* 94 */, 0x5504CC861C3D0556L /* 95 */,
+ 0xABBFA4E55FB26B8FL /* 96 */, 0x41848B0AB3BACEB4L /* 97 */,
+ 0xB334A273AA445D32L /* 98 */, 0xBCA696F0A85AD881L /* 99 */,
+ 0x24F6EC65B528D56CL /* 100 */, 0x0CE1512E90F4524AL /* 101 */,
+ 0x4E9DD79D5506D35AL /* 102 */, 0x258905FAC6CE9779L /* 103 */,
+ 0x2019295B3E109B33L /* 104 */, 0xF8A9478B73A054CCL /* 105 */,
+ 0x2924F2F934417EB0L /* 106 */, 0x3993357D536D1BC4L /* 107 */,
+ 0x38A81AC21DB6FF8BL /* 108 */, 0x47C4FBF17D6016BFL /* 109 */,
+ 0x1E0FAADD7667E3F5L /* 110 */, 0x7ABCFF62938BEB96L /* 111 */,
+ 0xA78DAD948FC179C9L /* 112 */, 0x8F1F98B72911E50DL /* 113 */,
+ 0x61E48EAE27121A91L /* 114 */, 0x4D62F7AD31859808L /* 115 */,
+ 0xECEBA345EF5CEAEBL /* 116 */, 0xF5CEB25EBC9684CEL /* 117 */,
+ 0xF633E20CB7F76221L /* 118 */, 0xA32CDF06AB8293E4L /* 119 */,
+ 0x985A202CA5EE2CA4L /* 120 */, 0xCF0B8447CC8A8FB1L /* 121 */,
+ 0x9F765244979859A3L /* 122 */, 0xA8D516B1A1240017L /* 123 */,
+ 0x0BD7BA3EBB5DC726L /* 124 */, 0xE54BCA55B86ADB39L /* 125 */,
+ 0x1D7A3AFD6C478063L /* 126 */, 0x519EC608E7669EDDL /* 127 */,
+ 0x0E5715A2D149AA23L /* 128 */, 0x177D4571848FF194L /* 129 */,
+ 0xEEB55F3241014C22L /* 130 */, 0x0F5E5CA13A6E2EC2L /* 131 */,
+ 0x8029927B75F5C361L /* 132 */, 0xAD139FABC3D6E436L /* 133 */,
+ 0x0D5DF1A94CCF402FL /* 134 */, 0x3E8BD948BEA5DFC8L /* 135 */,
+ 0xA5A0D357BD3FF77EL /* 136 */, 0xA2D12E251F74F645L /* 137 */,
+ 0x66FD9E525E81A082L /* 138 */, 0x2E0C90CE7F687A49L /* 139 */,
+ 0xC2E8BCBEBA973BC5L /* 140 */, 0x000001BCE509745FL /* 141 */,
+ 0x423777BBE6DAB3D6L /* 142 */, 0xD1661C7EAEF06EB5L /* 143 */,
+ 0xA1781F354DAACFD8L /* 144 */, 0x2D11284A2B16AFFCL /* 145 */,
+ 0xF1FC4F67FA891D1FL /* 146 */, 0x73ECC25DCB920ADAL /* 147 */,
+ 0xAE610C22C2A12651L /* 148 */, 0x96E0A810D356B78AL /* 149 */,
+ 0x5A9A381F2FE7870FL /* 150 */, 0xD5AD62EDE94E5530L /* 151 */,
+ 0xD225E5E8368D1427L /* 152 */, 0x65977B70C7AF4631L /* 153 */,
+ 0x99F889B2DE39D74FL /* 154 */, 0x233F30BF54E1D143L /* 155 */,
+ 0x9A9675D3D9A63C97L /* 156 */, 0x5470554FF334F9A8L /* 157 */,
+ 0x166ACB744A4F5688L /* 158 */, 0x70C74CAAB2E4AEADL /* 159 */,
+ 0xF0D091646F294D12L /* 160 */, 0x57B82A89684031D1L /* 161 */,
+ 0xEFD95A5A61BE0B6BL /* 162 */, 0x2FBD12E969F2F29AL /* 163 */,
+ 0x9BD37013FEFF9FE8L /* 164 */, 0x3F9B0404D6085A06L /* 165 */,
+ 0x4940C1F3166CFE15L /* 166 */, 0x09542C4DCDF3DEFBL /* 167 */,
+ 0xB4C5218385CD5CE3L /* 168 */, 0xC935B7DC4462A641L /* 169 */,
+ 0x3417F8A68ED3B63FL /* 170 */, 0xB80959295B215B40L /* 171 */,
+ 0xF99CDAEF3B8C8572L /* 172 */, 0x018C0614F8FCB95DL /* 173 */,
+ 0x1B14ACCD1A3ACDF3L /* 174 */, 0x84D471F200BB732DL /* 175 */,
+ 0xC1A3110E95E8DA16L /* 176 */, 0x430A7220BF1A82B8L /* 177 */,
+ 0xB77E090D39DF210EL /* 178 */, 0x5EF4BD9F3CD05E9DL /* 179 */,
+ 0x9D4FF6DA7E57A444L /* 180 */, 0xDA1D60E183D4A5F8L /* 181 */,
+ 0xB287C38417998E47L /* 182 */, 0xFE3EDC121BB31886L /* 183 */,
+ 0xC7FE3CCC980CCBEFL /* 184 */, 0xE46FB590189BFD03L /* 185 */,
+ 0x3732FD469A4C57DCL /* 186 */, 0x7EF700A07CF1AD65L /* 187 */,
+ 0x59C64468A31D8859L /* 188 */, 0x762FB0B4D45B61F6L /* 189 */,
+ 0x155BAED099047718L /* 190 */, 0x68755E4C3D50BAA6L /* 191 */,
+ 0xE9214E7F22D8B4DFL /* 192 */, 0x2ADDBF532EAC95F4L /* 193 */,
+ 0x32AE3909B4BD0109L /* 194 */, 0x834DF537B08E3450L /* 195 */,
+ 0xFA209DA84220728DL /* 196 */, 0x9E691D9B9EFE23F7L /* 197 */,
+ 0x0446D288C4AE8D7FL /* 198 */, 0x7B4CC524E169785BL /* 199 */,
+ 0x21D87F0135CA1385L /* 200 */, 0xCEBB400F137B8AA5L /* 201 */,
+ 0x272E2B66580796BEL /* 202 */, 0x3612264125C2B0DEL /* 203 */,
+ 0x057702BDAD1EFBB2L /* 204 */, 0xD4BABB8EACF84BE9L /* 205 */,
+ 0x91583139641BC67BL /* 206 */, 0x8BDC2DE08036E024L /* 207 */,
+ 0x603C8156F49F68EDL /* 208 */, 0xF7D236F7DBEF5111L /* 209 */,
+ 0x9727C4598AD21E80L /* 210 */, 0xA08A0896670A5FD7L /* 211 */,
+ 0xCB4A8F4309EBA9CBL /* 212 */, 0x81AF564B0F7036A1L /* 213 */,
+ 0xC0B99AA778199ABDL /* 214 */, 0x959F1EC83FC8E952L /* 215 */,
+ 0x8C505077794A81B9L /* 216 */, 0x3ACAAF8F056338F0L /* 217 */,
+ 0x07B43F50627A6778L /* 218 */, 0x4A44AB49F5ECCC77L /* 219 */,
+ 0x3BC3D6E4B679EE98L /* 220 */, 0x9CC0D4D1CF14108CL /* 221 */,
+ 0x4406C00B206BC8A0L /* 222 */, 0x82A18854C8D72D89L /* 223 */,
+ 0x67E366B35C3C432CL /* 224 */, 0xB923DD61102B37F2L /* 225 */,
+ 0x56AB2779D884271DL /* 226 */, 0xBE83E1B0FF1525AFL /* 227 */,
+ 0xFB7C65D4217E49A9L /* 228 */, 0x6BDBE0E76D48E7D4L /* 229 */,
+ 0x08DF828745D9179EL /* 230 */, 0x22EA6A9ADD53BD34L /* 231 */,
+ 0xE36E141C5622200AL /* 232 */, 0x7F805D1B8CB750EEL /* 233 */,
+ 0xAFE5C7A59F58E837L /* 234 */, 0xE27F996A4FB1C23CL /* 235 */,
+ 0xD3867DFB0775F0D0L /* 236 */, 0xD0E673DE6E88891AL /* 237 */,
+ 0x123AEB9EAFB86C25L /* 238 */, 0x30F1D5D5C145B895L /* 239 */,
+ 0xBB434A2DEE7269E7L /* 240 */, 0x78CB67ECF931FA38L /* 241 */,
+ 0xF33B0372323BBF9CL /* 242 */, 0x52D66336FB279C74L /* 243 */,
+ 0x505F33AC0AFB4EAAL /* 244 */, 0xE8A5CD99A2CCE187L /* 245 */,
+ 0x534974801E2D30BBL /* 246 */, 0x8D2D5711D5876D90L /* 247 */,
+ 0x1F1A412891BC038EL /* 248 */, 0xD6E2E71D82E56648L /* 249 */,
+ 0x74036C3A497732B7L /* 250 */, 0x89B67ED96361F5ABL /* 251 */,
+ 0xFFED95D8F1EA02A2L /* 252 */, 0xE72B3BD61464D43DL /* 253 */,
+ 0xA6300F170BDC4820L /* 254 */, 0xEBC18760ED78A77AL /* 255 */,
+ };
+
+ private static final long[] t2 = {
+ 0xE6A6BE5A05A12138L /* 256 */, 0xB5A122A5B4F87C98L /* 257 */,
+ 0x563C6089140B6990L /* 258 */, 0x4C46CB2E391F5DD5L /* 259 */,
+ 0xD932ADDBC9B79434L /* 260 */, 0x08EA70E42015AFF5L /* 261 */,
+ 0xD765A6673E478CF1L /* 262 */, 0xC4FB757EAB278D99L /* 263 */,
+ 0xDF11C6862D6E0692L /* 264 */, 0xDDEB84F10D7F3B16L /* 265 */,
+ 0x6F2EF604A665EA04L /* 266 */, 0x4A8E0F0FF0E0DFB3L /* 267 */,
+ 0xA5EDEEF83DBCBA51L /* 268 */, 0xFC4F0A2A0EA4371EL /* 269 */,
+ 0xE83E1DA85CB38429L /* 270 */, 0xDC8FF882BA1B1CE2L /* 271 */,
+ 0xCD45505E8353E80DL /* 272 */, 0x18D19A00D4DB0717L /* 273 */,
+ 0x34A0CFEDA5F38101L /* 274 */, 0x0BE77E518887CAF2L /* 275 */,
+ 0x1E341438B3C45136L /* 276 */, 0xE05797F49089CCF9L /* 277 */,
+ 0xFFD23F9DF2591D14L /* 278 */, 0x543DDA228595C5CDL /* 279 */,
+ 0x661F81FD99052A33L /* 280 */, 0x8736E641DB0F7B76L /* 281 */,
+ 0x15227725418E5307L /* 282 */, 0xE25F7F46162EB2FAL /* 283 */,
+ 0x48A8B2126C13D9FEL /* 284 */, 0xAFDC541792E76EEAL /* 285 */,
+ 0x03D912BFC6D1898FL /* 286 */, 0x31B1AAFA1B83F51BL /* 287 */,
+ 0xF1AC2796E42AB7D9L /* 288 */, 0x40A3A7D7FCD2EBACL /* 289 */,
+ 0x1056136D0AFBBCC5L /* 290 */, 0x7889E1DD9A6D0C85L /* 291 */,
+ 0xD33525782A7974AAL /* 292 */, 0xA7E25D09078AC09BL /* 293 */,
+ 0xBD4138B3EAC6EDD0L /* 294 */, 0x920ABFBE71EB9E70L /* 295 */,
+ 0xA2A5D0F54FC2625CL /* 296 */, 0xC054E36B0B1290A3L /* 297 */,
+ 0xF6DD59FF62FE932BL /* 298 */, 0x3537354511A8AC7DL /* 299 */,
+ 0xCA845E9172FADCD4L /* 300 */, 0x84F82B60329D20DCL /* 301 */,
+ 0x79C62CE1CD672F18L /* 302 */, 0x8B09A2ADD124642CL /* 303 */,
+ 0xD0C1E96A19D9E726L /* 304 */, 0x5A786A9B4BA9500CL /* 305 */,
+ 0x0E020336634C43F3L /* 306 */, 0xC17B474AEB66D822L /* 307 */,
+ 0x6A731AE3EC9BAAC2L /* 308 */, 0x8226667AE0840258L /* 309 */,
+ 0x67D4567691CAECA5L /* 310 */, 0x1D94155C4875ADB5L /* 311 */,
+ 0x6D00FD985B813FDFL /* 312 */, 0x51286EFCB774CD06L /* 313 */,
+ 0x5E8834471FA744AFL /* 314 */, 0xF72CA0AEE761AE2EL /* 315 */,
+ 0xBE40E4CDAEE8E09AL /* 316 */, 0xE9970BBB5118F665L /* 317 */,
+ 0x726E4BEB33DF1964L /* 318 */, 0x703B000729199762L /* 319 */,
+ 0x4631D816F5EF30A7L /* 320 */, 0xB880B5B51504A6BEL /* 321 */,
+ 0x641793C37ED84B6CL /* 322 */, 0x7B21ED77F6E97D96L /* 323 */,
+ 0x776306312EF96B73L /* 324 */, 0xAE528948E86FF3F4L /* 325 */,
+ 0x53DBD7F286A3F8F8L /* 326 */, 0x16CADCE74CFC1063L /* 327 */,
+ 0x005C19BDFA52C6DDL /* 328 */, 0x68868F5D64D46AD3L /* 329 */,
+ 0x3A9D512CCF1E186AL /* 330 */, 0x367E62C2385660AEL /* 331 */,
+ 0xE359E7EA77DCB1D7L /* 332 */, 0x526C0773749ABE6EL /* 333 */,
+ 0x735AE5F9D09F734BL /* 334 */, 0x493FC7CC8A558BA8L /* 335 */,
+ 0xB0B9C1533041AB45L /* 336 */, 0x321958BA470A59BDL /* 337 */,
+ 0x852DB00B5F46C393L /* 338 */, 0x91209B2BD336B0E5L /* 339 */,
+ 0x6E604F7D659EF19FL /* 340 */, 0xB99A8AE2782CCB24L /* 341 */,
+ 0xCCF52AB6C814C4C7L /* 342 */, 0x4727D9AFBE11727BL /* 343 */,
+ 0x7E950D0C0121B34DL /* 344 */, 0x756F435670AD471FL /* 345 */,
+ 0xF5ADD442615A6849L /* 346 */, 0x4E87E09980B9957AL /* 347 */,
+ 0x2ACFA1DF50AEE355L /* 348 */, 0xD898263AFD2FD556L /* 349 */,
+ 0xC8F4924DD80C8FD6L /* 350 */, 0xCF99CA3D754A173AL /* 351 */,
+ 0xFE477BACAF91BF3CL /* 352 */, 0xED5371F6D690C12DL /* 353 */,
+ 0x831A5C285E687094L /* 354 */, 0xC5D3C90A3708A0A4L /* 355 */,
+ 0x0F7F903717D06580L /* 356 */, 0x19F9BB13B8FDF27FL /* 357 */,
+ 0xB1BD6F1B4D502843L /* 358 */, 0x1C761BA38FFF4012L /* 359 */,
+ 0x0D1530C4E2E21F3BL /* 360 */, 0x8943CE69A7372C8AL /* 361 */,
+ 0xE5184E11FEB5CE66L /* 362 */, 0x618BDB80BD736621L /* 363 */,
+ 0x7D29BAD68B574D0BL /* 364 */, 0x81BB613E25E6FE5BL /* 365 */,
+ 0x071C9C10BC07913FL /* 366 */, 0xC7BEEB7909AC2D97L /* 367 */,
+ 0xC3E58D353BC5D757L /* 368 */, 0xEB017892F38F61E8L /* 369 */,
+ 0xD4EFFB9C9B1CC21AL /* 370 */, 0x99727D26F494F7ABL /* 371 */,
+ 0xA3E063A2956B3E03L /* 372 */, 0x9D4A8B9A4AA09C30L /* 373 */,
+ 0x3F6AB7D500090FB4L /* 374 */, 0x9CC0F2A057268AC0L /* 375 */,
+ 0x3DEE9D2DEDBF42D1L /* 376 */, 0x330F49C87960A972L /* 377 */,
+ 0xC6B2720287421B41L /* 378 */, 0x0AC59EC07C00369CL /* 379 */,
+ 0xEF4EAC49CB353425L /* 380 */, 0xF450244EEF0129D8L /* 381 */,
+ 0x8ACC46E5CAF4DEB6L /* 382 */, 0x2FFEAB63989263F7L /* 383 */,
+ 0x8F7CB9FE5D7A4578L /* 384 */, 0x5BD8F7644E634635L /* 385 */,
+ 0x427A7315BF2DC900L /* 386 */, 0x17D0C4AA2125261CL /* 387 */,
+ 0x3992486C93518E50L /* 388 */, 0xB4CBFEE0A2D7D4C3L /* 389 */,
+ 0x7C75D6202C5DDD8DL /* 390 */, 0xDBC295D8E35B6C61L /* 391 */,
+ 0x60B369D302032B19L /* 392 */, 0xCE42685FDCE44132L /* 393 */,
+ 0x06F3DDB9DDF65610L /* 394 */, 0x8EA4D21DB5E148F0L /* 395 */,
+ 0x20B0FCE62FCD496FL /* 396 */, 0x2C1B912358B0EE31L /* 397 */,
+ 0xB28317B818F5A308L /* 398 */, 0xA89C1E189CA6D2CFL /* 399 */,
+ 0x0C6B18576AAADBC8L /* 400 */, 0xB65DEAA91299FAE3L /* 401 */,
+ 0xFB2B794B7F1027E7L /* 402 */, 0x04E4317F443B5BEBL /* 403 */,
+ 0x4B852D325939D0A6L /* 404 */, 0xD5AE6BEEFB207FFCL /* 405 */,
+ 0x309682B281C7D374L /* 406 */, 0xBAE309A194C3B475L /* 407 */,
+ 0x8CC3F97B13B49F05L /* 408 */, 0x98A9422FF8293967L /* 409 */,
+ 0x244B16B01076FF7CL /* 410 */, 0xF8BF571C663D67EEL /* 411 */,
+ 0x1F0D6758EEE30DA1L /* 412 */, 0xC9B611D97ADEB9B7L /* 413 */,
+ 0xB7AFD5887B6C57A2L /* 414 */, 0x6290AE846B984FE1L /* 415 */,
+ 0x94DF4CDEACC1A5FDL /* 416 */, 0x058A5BD1C5483AFFL /* 417 */,
+ 0x63166CC142BA3C37L /* 418 */, 0x8DB8526EB2F76F40L /* 419 */,
+ 0xE10880036F0D6D4EL /* 420 */, 0x9E0523C9971D311DL /* 421 */,
+ 0x45EC2824CC7CD691L /* 422 */, 0x575B8359E62382C9L /* 423 */,
+ 0xFA9E400DC4889995L /* 424 */, 0xD1823ECB45721568L /* 425 */,
+ 0xDAFD983B8206082FL /* 426 */, 0xAA7D29082386A8CBL /* 427 */,
+ 0x269FCD4403B87588L /* 428 */, 0x1B91F5F728BDD1E0L /* 429 */,
+ 0xE4669F39040201F6L /* 430 */, 0x7A1D7C218CF04ADEL /* 431 */,
+ 0x65623C29D79CE5CEL /* 432 */, 0x2368449096C00BB1L /* 433 */,
+ 0xAB9BF1879DA503BAL /* 434 */, 0xBC23ECB1A458058EL /* 435 */,
+ 0x9A58DF01BB401ECCL /* 436 */, 0xA070E868A85F143DL /* 437 */,
+ 0x4FF188307DF2239EL /* 438 */, 0x14D565B41A641183L /* 439 */,
+ 0xEE13337452701602L /* 440 */, 0x950E3DCF3F285E09L /* 441 */,
+ 0x59930254B9C80953L /* 442 */, 0x3BF299408930DA6DL /* 443 */,
+ 0xA955943F53691387L /* 444 */, 0xA15EDECAA9CB8784L /* 445 */,
+ 0x29142127352BE9A0L /* 446 */, 0x76F0371FFF4E7AFBL /* 447 */,
+ 0x0239F450274F2228L /* 448 */, 0xBB073AF01D5E868BL /* 449 */,
+ 0xBFC80571C10E96C1L /* 450 */, 0xD267088568222E23L /* 451 */,
+ 0x9671A3D48E80B5B0L /* 452 */, 0x55B5D38AE193BB81L /* 453 */,
+ 0x693AE2D0A18B04B8L /* 454 */, 0x5C48B4ECADD5335FL /* 455 */,
+ 0xFD743B194916A1CAL /* 456 */, 0x2577018134BE98C4L /* 457 */,
+ 0xE77987E83C54A4ADL /* 458 */, 0x28E11014DA33E1B9L /* 459 */,
+ 0x270CC59E226AA213L /* 460 */, 0x71495F756D1A5F60L /* 461 */,
+ 0x9BE853FB60AFEF77L /* 462 */, 0xADC786A7F7443DBFL /* 463 */,
+ 0x0904456173B29A82L /* 464 */, 0x58BC7A66C232BD5EL /* 465 */,
+ 0xF306558C673AC8B2L /* 466 */, 0x41F639C6B6C9772AL /* 467 */,
+ 0x216DEFE99FDA35DAL /* 468 */, 0x11640CC71C7BE615L /* 469 */,
+ 0x93C43694565C5527L /* 470 */, 0xEA038E6246777839L /* 471 */,
+ 0xF9ABF3CE5A3E2469L /* 472 */, 0x741E768D0FD312D2L /* 473 */,
+ 0x0144B883CED652C6L /* 474 */, 0xC20B5A5BA33F8552L /* 475 */,
+ 0x1AE69633C3435A9DL /* 476 */, 0x97A28CA4088CFDECL /* 477 */,
+ 0x8824A43C1E96F420L /* 478 */, 0x37612FA66EEEA746L /* 479 */,
+ 0x6B4CB165F9CF0E5AL /* 480 */, 0x43AA1C06A0ABFB4AL /* 481 */,
+ 0x7F4DC26FF162796BL /* 482 */, 0x6CBACC8E54ED9B0FL /* 483 */,
+ 0xA6B7FFEFD2BB253EL /* 484 */, 0x2E25BC95B0A29D4FL /* 485 */,
+ 0x86D6A58BDEF1388CL /* 486 */, 0xDED74AC576B6F054L /* 487 */,
+ 0x8030BDBC2B45805DL /* 488 */, 0x3C81AF70E94D9289L /* 489 */,
+ 0x3EFF6DDA9E3100DBL /* 490 */, 0xB38DC39FDFCC8847L /* 491 */,
+ 0x123885528D17B87EL /* 492 */, 0xF2DA0ED240B1B642L /* 493 */,
+ 0x44CEFADCD54BF9A9L /* 494 */, 0x1312200E433C7EE6L /* 495 */,
+ 0x9FFCC84F3A78C748L /* 496 */, 0xF0CD1F72248576BBL /* 497 */,
+ 0xEC6974053638CFE4L /* 498 */, 0x2BA7B67C0CEC4E4CL /* 499 */,
+ 0xAC2F4DF3E5CE32EDL /* 500 */, 0xCB33D14326EA4C11L /* 501 */,
+ 0xA4E9044CC77E58BCL /* 502 */, 0x5F513293D934FCEFL /* 503 */,
+ 0x5DC9645506E55444L /* 504 */, 0x50DE418F317DE40AL /* 505 */,
+ 0x388CB31A69DDE259L /* 506 */, 0x2DB4A83455820A86L /* 507 */,
+ 0x9010A91E84711AE9L /* 508 */, 0x4DF7F0B7B1498371L /* 509 */,
+ 0xD62A2EABC0977179L /* 510 */, 0x22FAC097AA8D5C0EL /* 511 */,
+ };
+
+ private static final long[] t3 = {
+ 0xF49FCC2FF1DAF39BL /* 512 */, 0x487FD5C66FF29281L /* 513 */,
+ 0xE8A30667FCDCA83FL /* 514 */, 0x2C9B4BE3D2FCCE63L /* 515 */,
+ 0xDA3FF74B93FBBBC2L /* 516 */, 0x2FA165D2FE70BA66L /* 517 */,
+ 0xA103E279970E93D4L /* 518 */, 0xBECDEC77B0E45E71L /* 519 */,
+ 0xCFB41E723985E497L /* 520 */, 0xB70AAA025EF75017L /* 521 */,
+ 0xD42309F03840B8E0L /* 522 */, 0x8EFC1AD035898579L /* 523 */,
+ 0x96C6920BE2B2ABC5L /* 524 */, 0x66AF4163375A9172L /* 525 */,
+ 0x2174ABDCCA7127FBL /* 526 */, 0xB33CCEA64A72FF41L /* 527 */,
+ 0xF04A4933083066A5L /* 528 */, 0x8D970ACDD7289AF5L /* 529 */,
+ 0x8F96E8E031C8C25EL /* 530 */, 0xF3FEC02276875D47L /* 531 */,
+ 0xEC7BF310056190DDL /* 532 */, 0xF5ADB0AEBB0F1491L /* 533 */,
+ 0x9B50F8850FD58892L /* 534 */, 0x4975488358B74DE8L /* 535 */,
+ 0xA3354FF691531C61L /* 536 */, 0x0702BBE481D2C6EEL /* 537 */,
+ 0x89FB24057DEDED98L /* 538 */, 0xAC3075138596E902L /* 539 */,
+ 0x1D2D3580172772EDL /* 540 */, 0xEB738FC28E6BC30DL /* 541 */,
+ 0x5854EF8F63044326L /* 542 */, 0x9E5C52325ADD3BBEL /* 543 */,
+ 0x90AA53CF325C4623L /* 544 */, 0xC1D24D51349DD067L /* 545 */,
+ 0x2051CFEEA69EA624L /* 546 */, 0x13220F0A862E7E4FL /* 547 */,
+ 0xCE39399404E04864L /* 548 */, 0xD9C42CA47086FCB7L /* 549 */,
+ 0x685AD2238A03E7CCL /* 550 */, 0x066484B2AB2FF1DBL /* 551 */,
+ 0xFE9D5D70EFBF79ECL /* 552 */, 0x5B13B9DD9C481854L /* 553 */,
+ 0x15F0D475ED1509ADL /* 554 */, 0x0BEBCD060EC79851L /* 555 */,
+ 0xD58C6791183AB7F8L /* 556 */, 0xD1187C5052F3EEE4L /* 557 */,
+ 0xC95D1192E54E82FFL /* 558 */, 0x86EEA14CB9AC6CA2L /* 559 */,
+ 0x3485BEB153677D5DL /* 560 */, 0xDD191D781F8C492AL /* 561 */,
+ 0xF60866BAA784EBF9L /* 562 */, 0x518F643BA2D08C74L /* 563 */,
+ 0x8852E956E1087C22L /* 564 */, 0xA768CB8DC410AE8DL /* 565 */,
+ 0x38047726BFEC8E1AL /* 566 */, 0xA67738B4CD3B45AAL /* 567 */,
+ 0xAD16691CEC0DDE19L /* 568 */, 0xC6D4319380462E07L /* 569 */,
+ 0xC5A5876D0BA61938L /* 570 */, 0x16B9FA1FA58FD840L /* 571 */,
+ 0x188AB1173CA74F18L /* 572 */, 0xABDA2F98C99C021FL /* 573 */,
+ 0x3E0580AB134AE816L /* 574 */, 0x5F3B05B773645ABBL /* 575 */,
+ 0x2501A2BE5575F2F6L /* 576 */, 0x1B2F74004E7E8BA9L /* 577 */,
+ 0x1CD7580371E8D953L /* 578 */, 0x7F6ED89562764E30L /* 579 */,
+ 0xB15926FF596F003DL /* 580 */, 0x9F65293DA8C5D6B9L /* 581 */,
+ 0x6ECEF04DD690F84CL /* 582 */, 0x4782275FFF33AF88L /* 583 */,
+ 0xE41433083F820801L /* 584 */, 0xFD0DFE409A1AF9B5L /* 585 */,
+ 0x4325A3342CDB396BL /* 586 */, 0x8AE77E62B301B252L /* 587 */,
+ 0xC36F9E9F6655615AL /* 588 */, 0x85455A2D92D32C09L /* 589 */,
+ 0xF2C7DEA949477485L /* 590 */, 0x63CFB4C133A39EBAL /* 591 */,
+ 0x83B040CC6EBC5462L /* 592 */, 0x3B9454C8FDB326B0L /* 593 */,
+ 0x56F56A9E87FFD78CL /* 594 */, 0x2DC2940D99F42BC6L /* 595 */,
+ 0x98F7DF096B096E2DL /* 596 */, 0x19A6E01E3AD852BFL /* 597 */,
+ 0x42A99CCBDBD4B40BL /* 598 */, 0xA59998AF45E9C559L /* 599 */,
+ 0x366295E807D93186L /* 600 */, 0x6B48181BFAA1F773L /* 601 */,
+ 0x1FEC57E2157A0A1DL /* 602 */, 0x4667446AF6201AD5L /* 603 */,
+ 0xE615EBCACFB0F075L /* 604 */, 0xB8F31F4F68290778L /* 605 */,
+ 0x22713ED6CE22D11EL /* 606 */, 0x3057C1A72EC3C93BL /* 607 */,
+ 0xCB46ACC37C3F1F2FL /* 608 */, 0xDBB893FD02AAF50EL /* 609 */,
+ 0x331FD92E600B9FCFL /* 610 */, 0xA498F96148EA3AD6L /* 611 */,
+ 0xA8D8426E8B6A83EAL /* 612 */, 0xA089B274B7735CDCL /* 613 */,
+ 0x87F6B3731E524A11L /* 614 */, 0x118808E5CBC96749L /* 615 */,
+ 0x9906E4C7B19BD394L /* 616 */, 0xAFED7F7E9B24A20CL /* 617 */,
+ 0x6509EADEEB3644A7L /* 618 */, 0x6C1EF1D3E8EF0EDEL /* 619 */,
+ 0xB9C97D43E9798FB4L /* 620 */, 0xA2F2D784740C28A3L /* 621 */,
+ 0x7B8496476197566FL /* 622 */, 0x7A5BE3E6B65F069DL /* 623 */,
+ 0xF96330ED78BE6F10L /* 624 */, 0xEEE60DE77A076A15L /* 625 */,
+ 0x2B4BEE4AA08B9BD0L /* 626 */, 0x6A56A63EC7B8894EL /* 627 */,
+ 0x02121359BA34FEF4L /* 628 */, 0x4CBF99F8283703FCL /* 629 */,
+ 0x398071350CAF30C8L /* 630 */, 0xD0A77A89F017687AL /* 631 */,
+ 0xF1C1A9EB9E423569L /* 632 */, 0x8C7976282DEE8199L /* 633 */,
+ 0x5D1737A5DD1F7ABDL /* 634 */, 0x4F53433C09A9FA80L /* 635 */,
+ 0xFA8B0C53DF7CA1D9L /* 636 */, 0x3FD9DCBC886CCB77L /* 637 */,
+ 0xC040917CA91B4720L /* 638 */, 0x7DD00142F9D1DCDFL /* 639 */,
+ 0x8476FC1D4F387B58L /* 640 */, 0x23F8E7C5F3316503L /* 641 */,
+ 0x032A2244E7E37339L /* 642 */, 0x5C87A5D750F5A74BL /* 643 */,
+ 0x082B4CC43698992EL /* 644 */, 0xDF917BECB858F63CL /* 645 */,
+ 0x3270B8FC5BF86DDAL /* 646 */, 0x10AE72BB29B5DD76L /* 647 */,
+ 0x576AC94E7700362BL /* 648 */, 0x1AD112DAC61EFB8FL /* 649 */,
+ 0x691BC30EC5FAA427L /* 650 */, 0xFF246311CC327143L /* 651 */,
+ 0x3142368E30E53206L /* 652 */, 0x71380E31E02CA396L /* 653 */,
+ 0x958D5C960AAD76F1L /* 654 */, 0xF8D6F430C16DA536L /* 655 */,
+ 0xC8FFD13F1BE7E1D2L /* 656 */, 0x7578AE66004DDBE1L /* 657 */,
+ 0x05833F01067BE646L /* 658 */, 0xBB34B5AD3BFE586DL /* 659 */,
+ 0x095F34C9A12B97F0L /* 660 */, 0x247AB64525D60CA8L /* 661 */,
+ 0xDCDBC6F3017477D1L /* 662 */, 0x4A2E14D4DECAD24DL /* 663 */,
+ 0xBDB5E6D9BE0A1EEBL /* 664 */, 0x2A7E70F7794301ABL /* 665 */,
+ 0xDEF42D8A270540FDL /* 666 */, 0x01078EC0A34C22C1L /* 667 */,
+ 0xE5DE511AF4C16387L /* 668 */, 0x7EBB3A52BD9A330AL /* 669 */,
+ 0x77697857AA7D6435L /* 670 */, 0x004E831603AE4C32L /* 671 */,
+ 0xE7A21020AD78E312L /* 672 */, 0x9D41A70C6AB420F2L /* 673 */,
+ 0x28E06C18EA1141E6L /* 674 */, 0xD2B28CBD984F6B28L /* 675 */,
+ 0x26B75F6C446E9D83L /* 676 */, 0xBA47568C4D418D7FL /* 677 */,
+ 0xD80BADBFE6183D8EL /* 678 */, 0x0E206D7F5F166044L /* 679 */,
+ 0xE258A43911CBCA3EL /* 680 */, 0x723A1746B21DC0BCL /* 681 */,
+ 0xC7CAA854F5D7CDD3L /* 682 */, 0x7CAC32883D261D9CL /* 683 */,
+ 0x7690C26423BA942CL /* 684 */, 0x17E55524478042B8L /* 685 */,
+ 0xE0BE477656A2389FL /* 686 */, 0x4D289B5E67AB2DA0L /* 687 */,
+ 0x44862B9C8FBBFD31L /* 688 */, 0xB47CC8049D141365L /* 689 */,
+ 0x822C1B362B91C793L /* 690 */, 0x4EB14655FB13DFD8L /* 691 */,
+ 0x1ECBBA0714E2A97BL /* 692 */, 0x6143459D5CDE5F14L /* 693 */,
+ 0x53A8FBF1D5F0AC89L /* 694 */, 0x97EA04D81C5E5B00L /* 695 */,
+ 0x622181A8D4FDB3F3L /* 696 */, 0xE9BCD341572A1208L /* 697 */,
+ 0x1411258643CCE58AL /* 698 */, 0x9144C5FEA4C6E0A4L /* 699 */,
+ 0x0D33D06565CF620FL /* 700 */, 0x54A48D489F219CA1L /* 701 */,
+ 0xC43E5EAC6D63C821L /* 702 */, 0xA9728B3A72770DAFL /* 703 */,
+ 0xD7934E7B20DF87EFL /* 704 */, 0xE35503B61A3E86E5L /* 705 */,
+ 0xCAE321FBC819D504L /* 706 */, 0x129A50B3AC60BFA6L /* 707 */,
+ 0xCD5E68EA7E9FB6C3L /* 708 */, 0xB01C90199483B1C7L /* 709 */,
+ 0x3DE93CD5C295376CL /* 710 */, 0xAED52EDF2AB9AD13L /* 711 */,
+ 0x2E60F512C0A07884L /* 712 */, 0xBC3D86A3E36210C9L /* 713 */,
+ 0x35269D9B163951CEL /* 714 */, 0x0C7D6E2AD0CDB5FAL /* 715 */,
+ 0x59E86297D87F5733L /* 716 */, 0x298EF221898DB0E7L /* 717 */,
+ 0x55000029D1A5AA7EL /* 718 */, 0x8BC08AE1B5061B45L /* 719 */,
+ 0xC2C31C2B6C92703AL /* 720 */, 0x94CC596BAF25EF42L /* 721 */,
+ 0x0A1D73DB22540456L /* 722 */, 0x04B6A0F9D9C4179AL /* 723 */,
+ 0xEFFDAFA2AE3D3C60L /* 724 */, 0xF7C8075BB49496C4L /* 725 */,
+ 0x9CC5C7141D1CD4E3L /* 726 */, 0x78BD1638218E5534L /* 727 */,
+ 0xB2F11568F850246AL /* 728 */, 0xEDFABCFA9502BC29L /* 729 */,
+ 0x796CE5F2DA23051BL /* 730 */, 0xAAE128B0DC93537CL /* 731 */,
+ 0x3A493DA0EE4B29AEL /* 732 */, 0xB5DF6B2C416895D7L /* 733 */,
+ 0xFCABBD25122D7F37L /* 734 */, 0x70810B58105DC4B1L /* 735 */,
+ 0xE10FDD37F7882A90L /* 736 */, 0x524DCAB5518A3F5CL /* 737 */,
+ 0x3C9E85878451255BL /* 738 */, 0x4029828119BD34E2L /* 739 */,
+ 0x74A05B6F5D3CECCBL /* 740 */, 0xB610021542E13ECAL /* 741 */,
+ 0x0FF979D12F59E2ACL /* 742 */, 0x6037DA27E4F9CC50L /* 743 */,
+ 0x5E92975A0DF1847DL /* 744 */, 0xD66DE190D3E623FEL /* 745 */,
+ 0x5032D6B87B568048L /* 746 */, 0x9A36B7CE8235216EL /* 747 */,
+ 0x80272A7A24F64B4AL /* 748 */, 0x93EFED8B8C6916F7L /* 749 */,
+ 0x37DDBFF44CCE1555L /* 750 */, 0x4B95DB5D4B99BD25L /* 751 */,
+ 0x92D3FDA169812FC0L /* 752 */, 0xFB1A4A9A90660BB6L /* 753 */,
+ 0x730C196946A4B9B2L /* 754 */, 0x81E289AA7F49DA68L /* 755 */,
+ 0x64669A0F83B1A05FL /* 756 */, 0x27B3FF7D9644F48BL /* 757 */,
+ 0xCC6B615C8DB675B3L /* 758 */, 0x674F20B9BCEBBE95L /* 759 */,
+ 0x6F31238275655982L /* 760 */, 0x5AE488713E45CF05L /* 761 */,
+ 0xBF619F9954C21157L /* 762 */, 0xEABAC46040A8EAE9L /* 763 */,
+ 0x454C6FE9F2C0C1CDL /* 764 */, 0x419CF6496412691CL /* 765 */,
+ 0xD3DC3BEF265B0F70L /* 766 */, 0x6D0E60F5C3578A9EL /* 767 */,
+ };
+
+ private static final long[] t4 = {
+ 0x5B0E608526323C55L /* 768 */, 0x1A46C1A9FA1B59F5L /* 769 */,
+ 0xA9E245A17C4C8FFAL /* 770 */, 0x65CA5159DB2955D7L /* 771 */,
+ 0x05DB0A76CE35AFC2L /* 772 */, 0x81EAC77EA9113D45L /* 773 */,
+ 0x528EF88AB6AC0A0DL /* 774 */, 0xA09EA253597BE3FFL /* 775 */,
+ 0x430DDFB3AC48CD56L /* 776 */, 0xC4B3A67AF45CE46FL /* 777 */,
+ 0x4ECECFD8FBE2D05EL /* 778 */, 0x3EF56F10B39935F0L /* 779 */,
+ 0x0B22D6829CD619C6L /* 780 */, 0x17FD460A74DF2069L /* 781 */,
+ 0x6CF8CC8E8510ED40L /* 782 */, 0xD6C824BF3A6ECAA7L /* 783 */,
+ 0x61243D581A817049L /* 784 */, 0x048BACB6BBC163A2L /* 785 */,
+ 0xD9A38AC27D44CC32L /* 786 */, 0x7FDDFF5BAAF410ABL /* 787 */,
+ 0xAD6D495AA804824BL /* 788 */, 0xE1A6A74F2D8C9F94L /* 789 */,
+ 0xD4F7851235DEE8E3L /* 790 */, 0xFD4B7F886540D893L /* 791 */,
+ 0x247C20042AA4BFDAL /* 792 */, 0x096EA1C517D1327CL /* 793 */,
+ 0xD56966B4361A6685L /* 794 */, 0x277DA5C31221057DL /* 795 */,
+ 0x94D59893A43ACFF7L /* 796 */, 0x64F0C51CCDC02281L /* 797 */,
+ 0x3D33BCC4FF6189DBL /* 798 */, 0xE005CB184CE66AF1L /* 799 */,
+ 0xFF5CCD1D1DB99BEAL /* 800 */, 0xB0B854A7FE42980FL /* 801 */,
+ 0x7BD46A6A718D4B9FL /* 802 */, 0xD10FA8CC22A5FD8CL /* 803 */,
+ 0xD31484952BE4BD31L /* 804 */, 0xC7FA975FCB243847L /* 805 */,
+ 0x4886ED1E5846C407L /* 806 */, 0x28CDDB791EB70B04L /* 807 */,
+ 0xC2B00BE2F573417FL /* 808 */, 0x5C9590452180F877L /* 809 */,
+ 0x7A6BDDFFF370EB00L /* 810 */, 0xCE509E38D6D9D6A4L /* 811 */,
+ 0xEBEB0F00647FA702L /* 812 */, 0x1DCC06CF76606F06L /* 813 */,
+ 0xE4D9F28BA286FF0AL /* 814 */, 0xD85A305DC918C262L /* 815 */,
+ 0x475B1D8732225F54L /* 816 */, 0x2D4FB51668CCB5FEL /* 817 */,
+ 0xA679B9D9D72BBA20L /* 818 */, 0x53841C0D912D43A5L /* 819 */,
+ 0x3B7EAA48BF12A4E8L /* 820 */, 0x781E0E47F22F1DDFL /* 821 */,
+ 0xEFF20CE60AB50973L /* 822 */, 0x20D261D19DFFB742L /* 823 */,
+ 0x16A12B03062A2E39L /* 824 */, 0x1960EB2239650495L /* 825 */,
+ 0x251C16FED50EB8B8L /* 826 */, 0x9AC0C330F826016EL /* 827 */,
+ 0xED152665953E7671L /* 828 */, 0x02D63194A6369570L /* 829 */,
+ 0x5074F08394B1C987L /* 830 */, 0x70BA598C90B25CE1L /* 831 */,
+ 0x794A15810B9742F6L /* 832 */, 0x0D5925E9FCAF8C6CL /* 833 */,
+ 0x3067716CD868744EL /* 834 */, 0x910AB077E8D7731BL /* 835 */,
+ 0x6A61BBDB5AC42F61L /* 836 */, 0x93513EFBF0851567L /* 837 */,
+ 0xF494724B9E83E9D5L /* 838 */, 0xE887E1985C09648DL /* 839 */,
+ 0x34B1D3C675370CFDL /* 840 */, 0xDC35E433BC0D255DL /* 841 */,
+ 0xD0AAB84234131BE0L /* 842 */, 0x08042A50B48B7EAFL /* 843 */,
+ 0x9997C4EE44A3AB35L /* 844 */, 0x829A7B49201799D0L /* 845 */,
+ 0x263B8307B7C54441L /* 846 */, 0x752F95F4FD6A6CA6L /* 847 */,
+ 0x927217402C08C6E5L /* 848 */, 0x2A8AB754A795D9EEL /* 849 */,
+ 0xA442F7552F72943DL /* 850 */, 0x2C31334E19781208L /* 851 */,
+ 0x4FA98D7CEAEE6291L /* 852 */, 0x55C3862F665DB309L /* 853 */,
+ 0xBD0610175D53B1F3L /* 854 */, 0x46FE6CB840413F27L /* 855 */,
+ 0x3FE03792DF0CFA59L /* 856 */, 0xCFE700372EB85E8FL /* 857 */,
+ 0xA7BE29E7ADBCE118L /* 858 */, 0xE544EE5CDE8431DDL /* 859 */,
+ 0x8A781B1B41F1873EL /* 860 */, 0xA5C94C78A0D2F0E7L /* 861 */,
+ 0x39412E2877B60728L /* 862 */, 0xA1265EF3AFC9A62CL /* 863 */,
+ 0xBCC2770C6A2506C5L /* 864 */, 0x3AB66DD5DCE1CE12L /* 865 */,
+ 0xE65499D04A675B37L /* 866 */, 0x7D8F523481BFD216L /* 867 */,
+ 0x0F6F64FCEC15F389L /* 868 */, 0x74EFBE618B5B13C8L /* 869 */,
+ 0xACDC82B714273E1DL /* 870 */, 0xDD40BFE003199D17L /* 871 */,
+ 0x37E99257E7E061F8L /* 872 */, 0xFA52626904775AAAL /* 873 */,
+ 0x8BBBF63A463D56F9L /* 874 */, 0xF0013F1543A26E64L /* 875 */,
+ 0xA8307E9F879EC898L /* 876 */, 0xCC4C27A4150177CCL /* 877 */,
+ 0x1B432F2CCA1D3348L /* 878 */, 0xDE1D1F8F9F6FA013L /* 879 */,
+ 0x606602A047A7DDD6L /* 880 */, 0xD237AB64CC1CB2C7L /* 881 */,
+ 0x9B938E7225FCD1D3L /* 882 */, 0xEC4E03708E0FF476L /* 883 */,
+ 0xFEB2FBDA3D03C12DL /* 884 */, 0xAE0BCED2EE43889AL /* 885 */,
+ 0x22CB8923EBFB4F43L /* 886 */, 0x69360D013CF7396DL /* 887 */,
+ 0x855E3602D2D4E022L /* 888 */, 0x073805BAD01F784CL /* 889 */,
+ 0x33E17A133852F546L /* 890 */, 0xDF4874058AC7B638L /* 891 */,
+ 0xBA92B29C678AA14AL /* 892 */, 0x0CE89FC76CFAADCDL /* 893 */,
+ 0x5F9D4E0908339E34L /* 894 */, 0xF1AFE9291F5923B9L /* 895 */,
+ 0x6E3480F60F4A265FL /* 896 */, 0xEEBF3A2AB29B841CL /* 897 */,
+ 0xE21938A88F91B4ADL /* 898 */, 0x57DFEFF845C6D3C3L /* 899 */,
+ 0x2F006B0BF62CAAF2L /* 900 */, 0x62F479EF6F75EE78L /* 901 */,
+ 0x11A55AD41C8916A9L /* 902 */, 0xF229D29084FED453L /* 903 */,
+ 0x42F1C27B16B000E6L /* 904 */, 0x2B1F76749823C074L /* 905 */,
+ 0x4B76ECA3C2745360L /* 906 */, 0x8C98F463B91691BDL /* 907 */,
+ 0x14BCC93CF1ADE66AL /* 908 */, 0x8885213E6D458397L /* 909 */,
+ 0x8E177DF0274D4711L /* 910 */, 0xB49B73B5503F2951L /* 911 */,
+ 0x10168168C3F96B6BL /* 912 */, 0x0E3D963B63CAB0AEL /* 913 */,
+ 0x8DFC4B5655A1DB14L /* 914 */, 0xF789F1356E14DE5CL /* 915 */,
+ 0x683E68AF4E51DAC1L /* 916 */, 0xC9A84F9D8D4B0FD9L /* 917 */,
+ 0x3691E03F52A0F9D1L /* 918 */, 0x5ED86E46E1878E80L /* 919 */,
+ 0x3C711A0E99D07150L /* 920 */, 0x5A0865B20C4E9310L /* 921 */,
+ 0x56FBFC1FE4F0682EL /* 922 */, 0xEA8D5DE3105EDF9BL /* 923 */,
+ 0x71ABFDB12379187AL /* 924 */, 0x2EB99DE1BEE77B9CL /* 925 */,
+ 0x21ECC0EA33CF4523L /* 926 */, 0x59A4D7521805C7A1L /* 927 */,
+ 0x3896F5EB56AE7C72L /* 928 */, 0xAA638F3DB18F75DCL /* 929 */,
+ 0x9F39358DABE9808EL /* 930 */, 0xB7DEFA91C00B72ACL /* 931 */,
+ 0x6B5541FD62492D92L /* 932 */, 0x6DC6DEE8F92E4D5BL /* 933 */,
+ 0x353F57ABC4BEEA7EL /* 934 */, 0x735769D6DA5690CEL /* 935 */,
+ 0x0A234AA642391484L /* 936 */, 0xF6F9508028F80D9DL /* 937 */,
+ 0xB8E319A27AB3F215L /* 938 */, 0x31AD9C1151341A4DL /* 939 */,
+ 0x773C22A57BEF5805L /* 940 */, 0x45C7561A07968633L /* 941 */,
+ 0xF913DA9E249DBE36L /* 942 */, 0xDA652D9B78A64C68L /* 943 */,
+ 0x4C27A97F3BC334EFL /* 944 */, 0x76621220E66B17F4L /* 945 */,
+ 0x967743899ACD7D0BL /* 946 */, 0xF3EE5BCAE0ED6782L /* 947 */,
+ 0x409F753600C879FCL /* 948 */, 0x06D09A39B5926DB6L /* 949 */,
+ 0x6F83AEB0317AC588L /* 950 */, 0x01E6CA4A86381F21L /* 951 */,
+ 0x66FF3462D19F3025L /* 952 */, 0x72207C24DDFD3BFBL /* 953 */,
+ 0x4AF6B6D3E2ECE2EBL /* 954 */, 0x9C994DBEC7EA08DEL /* 955 */,
+ 0x49ACE597B09A8BC4L /* 956 */, 0xB38C4766CF0797BAL /* 957 */,
+ 0x131B9373C57C2A75L /* 958 */, 0xB1822CCE61931E58L /* 959 */,
+ 0x9D7555B909BA1C0CL /* 960 */, 0x127FAFDD937D11D2L /* 961 */,
+ 0x29DA3BADC66D92E4L /* 962 */, 0xA2C1D57154C2ECBCL /* 963 */,
+ 0x58C5134D82F6FE24L /* 964 */, 0x1C3AE3515B62274FL /* 965 */,
+ 0xE907C82E01CB8126L /* 966 */, 0xF8ED091913E37FCBL /* 967 */,
+ 0x3249D8F9C80046C9L /* 968 */, 0x80CF9BEDE388FB63L /* 969 */,
+ 0x1881539A116CF19EL /* 970 */, 0x5103F3F76BD52457L /* 971 */,
+ 0x15B7E6F5AE47F7A8L /* 972 */, 0xDBD7C6DED47E9CCFL /* 973 */,
+ 0x44E55C410228BB1AL /* 974 */, 0xB647D4255EDB4E99L /* 975 */,
+ 0x5D11882BB8AAFC30L /* 976 */, 0xF5098BBB29D3212AL /* 977 */,
+ 0x8FB5EA14E90296B3L /* 978 */, 0x677B942157DD025AL /* 979 */,
+ 0xFB58E7C0A390ACB5L /* 980 */, 0x89D3674C83BD4A01L /* 981 */,
+ 0x9E2DA4DF4BF3B93BL /* 982 */, 0xFCC41E328CAB4829L /* 983 */,
+ 0x03F38C96BA582C52L /* 984 */, 0xCAD1BDBD7FD85DB2L /* 985 */,
+ 0xBBB442C16082AE83L /* 986 */, 0xB95FE86BA5DA9AB0L /* 987 */,
+ 0xB22E04673771A93FL /* 988 */, 0x845358C9493152D8L /* 989 */,
+ 0xBE2A488697B4541EL /* 990 */, 0x95A2DC2DD38E6966L /* 991 */,
+ 0xC02C11AC923C852BL /* 992 */, 0x2388B1990DF2A87BL /* 993 */,
+ 0x7C8008FA1B4F37BEL /* 994 */, 0x1F70D0C84D54E503L /* 995 */,
+ 0x5490ADEC7ECE57D4L /* 996 */, 0x002B3C27D9063A3AL /* 997 */,
+ 0x7EAEA3848030A2BFL /* 998 */, 0xC602326DED2003C0L /* 999 */,
+ 0x83A7287D69A94086L /* 1000 */, 0xC57A5FCB30F57A8AL /* 1001 */,
+ 0xB56844E479EBE779L /* 1002 */, 0xA373B40F05DCBCE9L /* 1003 */,
+ 0xD71A786E88570EE2L /* 1004 */, 0x879CBACDBDE8F6A0L /* 1005 */,
+ 0x976AD1BCC164A32FL /* 1006 */, 0xAB21E25E9666D78BL /* 1007 */,
+ 0x901063AAE5E5C33CL /* 1008 */, 0x9818B34448698D90L /* 1009 */,
+ 0xE36487AE3E1E8ABBL /* 1010 */, 0xAFBDF931893BDCB4L /* 1011 */,
+ 0x6345A0DC5FBBD519L /* 1012 */, 0x8628FE269B9465CAL /* 1013 */,
+ 0x1E5D01603F9C51ECL /* 1014 */, 0x4DE44006A15049B7L /* 1015 */,
+ 0xBF6C70E5F776CBB1L /* 1016 */, 0x411218F2EF552BEDL /* 1017 */,
+ 0xCB0C0708705A36A3L /* 1018 */, 0xE74D14754F986044L /* 1019 */,
+ 0xCD56D9430EA8280EL /* 1020 */, 0xC12591D7535F5065L /* 1021 */,
+ 0xC83223F1720AEF96L /* 1022 */, 0xC3A0396F7363A51FL /* 1023 */
+ };
+
+ private static final int DIGEST_LENGTH = 24;
+
+ //
+ // registers
+ //
+ private long a, b, c;
+ private long byteCount;
+
+ //
+ // buffers
+ //
+ private byte[] buf = new byte[8];
+ private int bOff = 0;
+
+ private long[] x = new long[8];
+ private int xOff = 0;
+
+ /**
+ * Standard constructor
+ */
+ public TigerDigest()
+ {
+ reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public TigerDigest(TigerDigest t)
+ {
+ this.reset(t);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Tiger";
+ }
+
+ public int getDigestSize()
+ {
+ return DIGEST_LENGTH;
+ }
+
+ private void processWord(
+ byte[] b,
+ int off)
+ {
+ x[xOff++] = ((long)(b[off + 7] & 0xff) << 56)
+ | ((long)(b[off + 6] & 0xff) << 48)
+ | ((long)(b[off + 5] & 0xff) << 40)
+ | ((long)(b[off + 4] & 0xff) << 32)
+ | ((long)(b[off + 3] & 0xff) << 24)
+ | ((long)(b[off + 2] & 0xff) << 16)
+ | ((long)(b[off + 1] & 0xff) << 8)
+ | ((b[off + 0] & 0xff));
+
+ if (xOff == x.length)
+ {
+ processBlock();
+ }
+
+ bOff = 0;
+ }
+
+ public void update(
+ byte in)
+ {
+ buf[bOff++] = in;
+
+ if (bOff == buf.length)
+ {
+ processWord(buf, 0);
+ }
+
+ byteCount++;
+ }
+
+ public void update(
+ byte[] in,
+ int inOff,
+ int len)
+ {
+ //
+ // fill the current word
+ //
+ while ((bOff != 0) && (len > 0))
+ {
+ update(in[inOff]);
+
+ inOff++;
+ len--;
+ }
+
+ //
+ // process whole words.
+ //
+ while (len > 8)
+ {
+ processWord(in, inOff);
+
+ inOff += 8;
+ len -= 8;
+ byteCount += 8;
+ }
+
+ //
+ // load in the remainder.
+ //
+ while (len > 0)
+ {
+ update(in[inOff]);
+
+ inOff++;
+ len--;
+ }
+ }
+
+ private void roundABC(
+ long x,
+ long mul)
+ {
+ c ^= x ;
+ a -= t1[(int)c & 0xff] ^ t2[(int)(c >> 16) & 0xff]
+ ^ t3[(int)(c >> 32) & 0xff] ^ t4[(int)(c >> 48) & 0xff];
+ b += t4[(int)(c >> 8) & 0xff] ^ t3[(int)(c >> 24) & 0xff]
+ ^ t2[(int)(c >> 40) & 0xff] ^ t1[(int)(c >> 56) & 0xff];
+ b *= mul;
+ }
+
+ private void roundBCA(
+ long x,
+ long mul)
+ {
+ a ^= x ;
+ b -= t1[(int)a & 0xff] ^ t2[(int)(a >> 16) & 0xff]
+ ^ t3[(int)(a >> 32) & 0xff] ^ t4[(int)(a >> 48) & 0xff];
+ c += t4[(int)(a >> 8) & 0xff] ^ t3[(int)(a >> 24) & 0xff]
+ ^ t2[(int)(a >> 40) & 0xff] ^ t1[(int)(a >> 56) & 0xff];
+ c *= mul;
+ }
+
+ private void roundCAB(
+ long x,
+ long mul)
+ {
+ b ^= x ;
+ c -= t1[(int)b & 0xff] ^ t2[(int)(b >> 16) & 0xff]
+ ^ t3[(int)(b >> 32) & 0xff] ^ t4[(int)(b >> 48) & 0xff];
+ a += t4[(int)(b >> 8) & 0xff] ^ t3[(int)(b >> 24) & 0xff]
+ ^ t2[(int)(b >> 40) & 0xff] ^ t1[(int)(b >> 56) & 0xff];
+ a *= mul;
+ }
+
+ private void keySchedule()
+ {
+ x[0] -= x[7] ^ 0xA5A5A5A5A5A5A5A5L;
+ x[1] ^= x[0];
+ x[2] += x[1];
+ x[3] -= x[2] ^ ((~x[1]) << 19);
+ x[4] ^= x[3];
+ x[5] += x[4];
+ x[6] -= x[5] ^ ((~x[4]) >>> 23);
+ x[7] ^= x[6];
+ x[0] += x[7];
+ x[1] -= x[0] ^ ((~x[7]) << 19);
+ x[2] ^= x[1];
+ x[3] += x[2];
+ x[4] -= x[3] ^ ((~x[2]) >>> 23);
+ x[5] ^= x[4];
+ x[6] += x[5];
+ x[7] -= x[6] ^ 0x0123456789ABCDEFL;
+ }
+
+ private void processBlock()
+ {
+ //
+ // save abc
+ //
+ long aa = a;
+ long bb = b;
+ long cc = c;
+
+ //
+ // rounds and schedule
+ //
+ roundABC(x[0], 5);
+ roundBCA(x[1], 5);
+ roundCAB(x[2], 5);
+ roundABC(x[3], 5);
+ roundBCA(x[4], 5);
+ roundCAB(x[5], 5);
+ roundABC(x[6], 5);
+ roundBCA(x[7], 5);
+
+ keySchedule();
+
+ roundCAB(x[0], 7);
+ roundABC(x[1], 7);
+ roundBCA(x[2], 7);
+ roundCAB(x[3], 7);
+ roundABC(x[4], 7);
+ roundBCA(x[5], 7);
+ roundCAB(x[6], 7);
+ roundABC(x[7], 7);
+
+ keySchedule();
+
+ roundBCA(x[0], 9);
+ roundCAB(x[1], 9);
+ roundABC(x[2], 9);
+ roundBCA(x[3], 9);
+ roundCAB(x[4], 9);
+ roundABC(x[5], 9);
+ roundBCA(x[6], 9);
+ roundCAB(x[7], 9);
+
+ //
+ // feed forward
+ //
+ a ^= aa;
+ b -= bb;
+ c += cc;
+
+ //
+ // clear the x buffer
+ //
+ xOff = 0;
+ for (int i = 0; i != x.length; i++)
+ {
+ x[i] = 0;
+ }
+ }
+
+ public void unpackWord(
+ long r,
+ byte[] out,
+ int outOff)
+ {
+ out[outOff + 7] = (byte)(r >> 56);
+ out[outOff + 6] = (byte)(r >> 48);
+ out[outOff + 5] = (byte)(r >> 40);
+ out[outOff + 4] = (byte)(r >> 32);
+ out[outOff + 3] = (byte)(r >> 24);
+ out[outOff + 2] = (byte)(r >> 16);
+ out[outOff + 1] = (byte)(r >> 8);
+ out[outOff] = (byte)r;
+ }
+
+ private void processLength(
+ long bitLength)
+ {
+ x[7] = bitLength;
+ }
+
+ private void finish()
+ {
+ long bitLength = (byteCount << 3);
+
+ update((byte)0x01);
+
+ while (bOff != 0)
+ {
+ update((byte)0);
+ }
+
+ processLength(bitLength);
+
+ processBlock();
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ finish();
+
+ unpackWord(a, out, outOff);
+ unpackWord(b, out, outOff + 8);
+ unpackWord(c, out, outOff + 16);
+
+ reset();
+
+ return DIGEST_LENGTH;
+ }
+
+ /**
+ * reset the chaining variables
+ */
+ public void reset()
+ {
+ a = 0x0123456789ABCDEFL;
+ b = 0xFEDCBA9876543210L;
+ c = 0xF096A5B4C3B2E187L;
+
+ xOff = 0;
+ for (int i = 0; i != x.length; i++)
+ {
+ x[i] = 0;
+ }
+
+ bOff = 0;
+ for (int i = 0; i != buf.length; i++)
+ {
+ buf[i] = 0;
+ }
+
+ byteCount = 0;
+ }
+
+ public int getByteLength()
+ {
+ 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/core/src/main/java/org/bouncycastle/crypto/digests/WhirlpoolDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/WhirlpoolDigest.java
new file mode 100644
index 00000000..11e884cd
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/WhirlpoolDigest.java
@@ -0,0 +1,409 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.ExtendedDigest;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Memoable;
+
+
+/**
+ * Implementation of WhirlpoolDigest, based on Java source published by Barreto
+ * and Rijmen.
+ *
+ */
+public final class WhirlpoolDigest
+ implements ExtendedDigest, Memoable
+{
+ private static final int BYTE_LENGTH = 64;
+
+ private static final int DIGEST_LENGTH_BYTES = 512 / 8;
+ private static final int ROUNDS = 10;
+ private static final int REDUCTION_POLYNOMIAL = 0x011d; // 2^8 + 2^4 + 2^3 + 2 + 1;
+
+ private static final int[] SBOX = {
+ 0x18, 0x23, 0xc6, 0xe8, 0x87, 0xb8, 0x01, 0x4f, 0x36, 0xa6, 0xd2, 0xf5, 0x79, 0x6f, 0x91, 0x52,
+ 0x60, 0xbc, 0x9b, 0x8e, 0xa3, 0x0c, 0x7b, 0x35, 0x1d, 0xe0, 0xd7, 0xc2, 0x2e, 0x4b, 0xfe, 0x57,
+ 0x15, 0x77, 0x37, 0xe5, 0x9f, 0xf0, 0x4a, 0xda, 0x58, 0xc9, 0x29, 0x0a, 0xb1, 0xa0, 0x6b, 0x85,
+ 0xbd, 0x5d, 0x10, 0xf4, 0xcb, 0x3e, 0x05, 0x67, 0xe4, 0x27, 0x41, 0x8b, 0xa7, 0x7d, 0x95, 0xd8,
+ 0xfb, 0xee, 0x7c, 0x66, 0xdd, 0x17, 0x47, 0x9e, 0xca, 0x2d, 0xbf, 0x07, 0xad, 0x5a, 0x83, 0x33,
+ 0x63, 0x02, 0xaa, 0x71, 0xc8, 0x19, 0x49, 0xd9, 0xf2, 0xe3, 0x5b, 0x88, 0x9a, 0x26, 0x32, 0xb0,
+ 0xe9, 0x0f, 0xd5, 0x80, 0xbe, 0xcd, 0x34, 0x48, 0xff, 0x7a, 0x90, 0x5f, 0x20, 0x68, 0x1a, 0xae,
+ 0xb4, 0x54, 0x93, 0x22, 0x64, 0xf1, 0x73, 0x12, 0x40, 0x08, 0xc3, 0xec, 0xdb, 0xa1, 0x8d, 0x3d,
+ 0x97, 0x00, 0xcf, 0x2b, 0x76, 0x82, 0xd6, 0x1b, 0xb5, 0xaf, 0x6a, 0x50, 0x45, 0xf3, 0x30, 0xef,
+ 0x3f, 0x55, 0xa2, 0xea, 0x65, 0xba, 0x2f, 0xc0, 0xde, 0x1c, 0xfd, 0x4d, 0x92, 0x75, 0x06, 0x8a,
+ 0xb2, 0xe6, 0x0e, 0x1f, 0x62, 0xd4, 0xa8, 0x96, 0xf9, 0xc5, 0x25, 0x59, 0x84, 0x72, 0x39, 0x4c,
+ 0x5e, 0x78, 0x38, 0x8c, 0xd1, 0xa5, 0xe2, 0x61, 0xb3, 0x21, 0x9c, 0x1e, 0x43, 0xc7, 0xfc, 0x04,
+ 0x51, 0x99, 0x6d, 0x0d, 0xfa, 0xdf, 0x7e, 0x24, 0x3b, 0xab, 0xce, 0x11, 0x8f, 0x4e, 0xb7, 0xeb,
+ 0x3c, 0x81, 0x94, 0xf7, 0xb9, 0x13, 0x2c, 0xd3, 0xe7, 0x6e, 0xc4, 0x03, 0x56, 0x44, 0x7f, 0xa9,
+ 0x2a, 0xbb, 0xc1, 0x53, 0xdc, 0x0b, 0x9d, 0x6c, 0x31, 0x74, 0xf6, 0x46, 0xac, 0x89, 0x14, 0xe1,
+ 0x16, 0x3a, 0x69, 0x09, 0x70, 0xb6, 0xd0, 0xed, 0xcc, 0x42, 0x98, 0xa4, 0x28, 0x5c, 0xf8, 0x86
+ };
+
+ private static final long[] C0 = new long[256];
+ private static final long[] C1 = new long[256];
+ private static final long[] C2 = new long[256];
+ private static final long[] C3 = new long[256];
+ private static final long[] C4 = new long[256];
+ private static final long[] C5 = new long[256];
+ private static final long[] C6 = new long[256];
+ private static final long[] C7 = new long[256];
+
+ private final long[] _rc = new long[ROUNDS + 1];
+
+ public WhirlpoolDigest()
+ {
+ for (int i = 0; i < 256; i++)
+ {
+ int v1 = SBOX[i];
+ int v2 = maskWithReductionPolynomial(v1 << 1);
+ int v4 = maskWithReductionPolynomial(v2 << 1);
+ int v5 = v4 ^ v1;
+ int v8 = maskWithReductionPolynomial(v4 << 1);
+ int v9 = v8 ^ v1;
+
+ C0[i] = packIntoLong(v1, v1, v4, v1, v8, v5, v2, v9);
+ C1[i] = packIntoLong(v9, v1, v1, v4, v1, v8, v5, v2);
+ C2[i] = packIntoLong(v2, v9, v1, v1, v4, v1, v8, v5);
+ C3[i] = packIntoLong(v5, v2, v9, v1, v1, v4, v1, v8);
+ C4[i] = packIntoLong(v8, v5, v2, v9, v1, v1, v4, v1);
+ C5[i] = packIntoLong(v1, v8, v5, v2, v9, v1, v1, v4);
+ C6[i] = packIntoLong(v4, v1, v8, v5, v2, v9, v1, v1);
+ C7[i] = packIntoLong(v1, v4, v1, v8, v5, v2, v9, v1);
+
+ }
+
+ _rc[0] = 0L;
+ for (int r = 1; r <= ROUNDS; r++)
+ {
+ int i = 8 * (r - 1);
+ _rc[r] = (C0[i ] & 0xff00000000000000L) ^
+ (C1[i + 1] & 0x00ff000000000000L) ^
+ (C2[i + 2] & 0x0000ff0000000000L) ^
+ (C3[i + 3] & 0x000000ff00000000L) ^
+ (C4[i + 4] & 0x00000000ff000000L) ^
+ (C5[i + 5] & 0x0000000000ff0000L) ^
+ (C6[i + 6] & 0x000000000000ff00L) ^
+ (C7[i + 7] & 0x00000000000000ffL);
+ }
+
+ }
+
+ private long packIntoLong(int b7, int b6, int b5, int b4, int b3, int b2, int b1, int b0)
+ {
+ return
+ ((long)b7 << 56) ^
+ ((long)b6 << 48) ^
+ ((long)b5 << 40) ^
+ ((long)b4 << 32) ^
+ ((long)b3 << 24) ^
+ ((long)b2 << 16) ^
+ ((long)b1 << 8) ^
+ b0;
+ }
+
+ /*
+ * int's are used to prevent sign extension. The values that are really being used are
+ * actually just 0..255
+ */
+ private int maskWithReductionPolynomial(int input)
+ {
+ int rv = input;
+ if (rv >= 0x100L) // high bit set
+ {
+ rv ^= REDUCTION_POLYNOMIAL; // reduced by the polynomial
+ }
+ return rv;
+ }
+
+ // --------------------------------------------------------------------------------------//
+
+ // -- buffer information --
+ private static final int BITCOUNT_ARRAY_SIZE = 32;
+ private byte[] _buffer = new byte[64];
+ private int _bufferPos = 0;
+ private short[] _bitCount = new short[BITCOUNT_ARRAY_SIZE];
+
+ // -- internal hash state --
+ private long[] _hash = new long[8];
+ private long[] _K = new long[8]; // the round key
+ private long[] _L = new long[8];
+ private long[] _block = new long[8]; // mu (buffer)
+ private long[] _state = new long[8]; // the current "cipher" state
+
+
+
+ /**
+ * Copy constructor. This will copy the state of the provided message
+ * digest.
+ */
+ public WhirlpoolDigest(WhirlpoolDigest originalDigest)
+ {
+ reset(originalDigest);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Whirlpool";
+ }
+
+ public int getDigestSize()
+ {
+ return DIGEST_LENGTH_BYTES;
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ {
+ // sets out[outOff] .. out[outOff+DIGEST_LENGTH_BYTES]
+ finish();
+
+ for (int i = 0; i < 8; i++)
+ {
+ convertLongToByteArray(_hash[i], out, outOff + (i * 8));
+ }
+
+ reset();
+ return getDigestSize();
+ }
+
+ /**
+ * reset the chaining variables
+ */
+ public void reset()
+ {
+ // set variables to null, blank, whatever
+ _bufferPos = 0;
+ Arrays.fill(_bitCount, (short)0);
+ Arrays.fill(_buffer, (byte)0);
+ Arrays.fill(_hash, 0);
+ Arrays.fill(_K, 0);
+ Arrays.fill(_L, 0);
+ Arrays.fill(_block, 0);
+ Arrays.fill(_state, 0);
+ }
+
+ // this takes a buffer of information and fills the block
+ private void processFilledBuffer(byte[] in, int inOff)
+ {
+ // copies into the block...
+ for (int i = 0; i < _state.length; i++)
+ {
+ _block[i] = bytesToLongFromBuffer(_buffer, i * 8);
+ }
+ processBlock();
+ _bufferPos = 0;
+ Arrays.fill(_buffer, (byte)0);
+ }
+
+ private long bytesToLongFromBuffer(byte[] buffer, int startPos)
+ {
+ long rv = (((buffer[startPos + 0] & 0xffL) << 56) |
+ ((buffer[startPos + 1] & 0xffL) << 48) |
+ ((buffer[startPos + 2] & 0xffL) << 40) |
+ ((buffer[startPos + 3] & 0xffL) << 32) |
+ ((buffer[startPos + 4] & 0xffL) << 24) |
+ ((buffer[startPos + 5] & 0xffL) << 16) |
+ ((buffer[startPos + 6] & 0xffL) << 8) |
+ ((buffer[startPos + 7]) & 0xffL));
+
+ return rv;
+ }
+
+ private void convertLongToByteArray(long inputLong, byte[] outputArray, int offSet)
+ {
+ for (int i = 0; i < 8; i++)
+ {
+ outputArray[offSet + i] = (byte)((inputLong >> (56 - (i * 8))) & 0xff);
+ }
+ }
+
+ protected void processBlock()
+ {
+ // buffer contents have been transferred to the _block[] array via
+ // processFilledBuffer
+
+ // compute and apply K^0
+ for (int i = 0; i < 8; i++)
+ {
+ _state[i] = _block[i] ^ (_K[i] = _hash[i]);
+ }
+
+ // iterate over the rounds
+ for (int round = 1; round <= ROUNDS; round++)
+ {
+ for (int i = 0; i < 8; i++)
+ {
+ _L[i] = 0;
+ _L[i] ^= C0[(int)(_K[(i - 0) & 7] >>> 56) & 0xff];
+ _L[i] ^= C1[(int)(_K[(i - 1) & 7] >>> 48) & 0xff];
+ _L[i] ^= C2[(int)(_K[(i - 2) & 7] >>> 40) & 0xff];
+ _L[i] ^= C3[(int)(_K[(i - 3) & 7] >>> 32) & 0xff];
+ _L[i] ^= C4[(int)(_K[(i - 4) & 7] >>> 24) & 0xff];
+ _L[i] ^= C5[(int)(_K[(i - 5) & 7] >>> 16) & 0xff];
+ _L[i] ^= C6[(int)(_K[(i - 6) & 7] >>> 8) & 0xff];
+ _L[i] ^= C7[(int)(_K[(i - 7) & 7]) & 0xff];
+ }
+
+ System.arraycopy(_L, 0, _K, 0, _K.length);
+
+ _K[0] ^= _rc[round];
+
+ // apply the round transformation
+ for (int i = 0; i < 8; i++)
+ {
+ _L[i] = _K[i];
+
+ _L[i] ^= C0[(int)(_state[(i - 0) & 7] >>> 56) & 0xff];
+ _L[i] ^= C1[(int)(_state[(i - 1) & 7] >>> 48) & 0xff];
+ _L[i] ^= C2[(int)(_state[(i - 2) & 7] >>> 40) & 0xff];
+ _L[i] ^= C3[(int)(_state[(i - 3) & 7] >>> 32) & 0xff];
+ _L[i] ^= C4[(int)(_state[(i - 4) & 7] >>> 24) & 0xff];
+ _L[i] ^= C5[(int)(_state[(i - 5) & 7] >>> 16) & 0xff];
+ _L[i] ^= C6[(int)(_state[(i - 6) & 7] >>> 8) & 0xff];
+ _L[i] ^= C7[(int)(_state[(i - 7) & 7]) & 0xff];
+ }
+
+ // save the current state
+ System.arraycopy(_L, 0, _state, 0, _state.length);
+ }
+
+ // apply Miuaguchi-Preneel compression
+ for (int i = 0; i < 8; i++)
+ {
+ _hash[i] ^= _state[i] ^ _block[i];
+ }
+
+ }
+
+ public void update(byte in)
+ {
+ _buffer[_bufferPos] = in;
+
+ //System.out.println("adding to buffer = "+_buffer[_bufferPos]);
+
+ ++_bufferPos;
+
+ if (_bufferPos == _buffer.length)
+ {
+ processFilledBuffer(_buffer, 0);
+ }
+
+ increment();
+ }
+
+ /*
+ * increment() can be implemented in this way using 2 arrays or
+ * by having some temporary variables that are used to set the
+ * value provided by EIGHT[i] and carry within the loop.
+ *
+ * not having done any timing, this seems likely to be faster
+ * at the slight expense of 32*(sizeof short) bytes
+ */
+ private static final short[] EIGHT = new short[BITCOUNT_ARRAY_SIZE];
+ static
+ {
+ EIGHT[BITCOUNT_ARRAY_SIZE - 1] = 8;
+ }
+
+ private void increment()
+ {
+ int carry = 0;
+ for (int i = _bitCount.length - 1; i >= 0; i--)
+ {
+ int sum = (_bitCount[i] & 0xff) + EIGHT[i] + carry;
+
+ carry = sum >>> 8;
+ _bitCount[i] = (short)(sum & 0xff);
+ }
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ {
+ while (len > 0)
+ {
+ update(in[inOff]);
+ ++inOff;
+ --len;
+ }
+
+ }
+
+ private void finish()
+ {
+ /*
+ * this makes a copy of the current bit length. at the expense of an
+ * object creation of 32 bytes rather than providing a _stopCounting
+ * boolean which was the alternative I could think of.
+ */
+ byte[] bitLength = copyBitLength();
+
+ _buffer[_bufferPos++] |= 0x80;
+
+ if (_bufferPos == _buffer.length)
+ {
+ processFilledBuffer(_buffer, 0);
+ }
+
+ /*
+ * Final block contains
+ * [ ... data .... ][0][0][0][ length ]
+ *
+ * if [ length ] cannot fit. Need to create a new block.
+ */
+ if (_bufferPos > 32)
+ {
+ while (_bufferPos != 0)
+ {
+ update((byte)0);
+ }
+ }
+
+ while (_bufferPos <= 32)
+ {
+ update((byte)0);
+ }
+
+ // copy the length information to the final 32 bytes of the
+ // 64 byte block....
+ System.arraycopy(bitLength, 0, _buffer, 32, bitLength.length);
+
+ processFilledBuffer(_buffer, 0);
+ }
+
+ private byte[] copyBitLength()
+ {
+ byte[] rv = new byte[BITCOUNT_ARRAY_SIZE];
+ for (int i = 0; i < rv.length; i++)
+ {
+ rv[i] = (byte)(_bitCount[i] & 0xff);
+ }
+ return rv;
+ }
+
+ public int getByteLength()
+ {
+ 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/core/src/main/java/org/bouncycastle/crypto/ec/ECDecryptor.java b/core/src/main/java/org/bouncycastle/crypto/ec/ECDecryptor.java
new file mode 100644
index 00000000..c4faf4c2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/ec/ECDecryptor.java
@@ -0,0 +1,11 @@
+package org.bouncycastle.crypto.ec;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.math.ec.ECPoint;
+
+public interface ECDecryptor
+{
+ void init(CipherParameters params);
+
+ ECPoint decrypt(ECPair cipherText);
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/ec/ECElGamalDecryptor.java b/core/src/main/java/org/bouncycastle/crypto/ec/ECElGamalDecryptor.java
new file mode 100644
index 00000000..c8c548ec
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/ec/ECElGamalDecryptor.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.crypto.ec;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.math.ec.ECPoint;
+
+/**
+ * this does your basic decryption ElGamal style using EC
+ */
+public class ECElGamalDecryptor
+ implements ECDecryptor
+{
+ private ECPrivateKeyParameters key;
+
+ /**
+ * initialise the decryptor.
+ *
+ * @param param the necessary EC key parameters.
+ */
+ public void init(
+ CipherParameters param)
+ {
+ if (!(param instanceof ECPrivateKeyParameters))
+ {
+ throw new IllegalArgumentException("ECPrivateKeyParameters are required for decryption.");
+ }
+
+ this.key = (ECPrivateKeyParameters)param;
+ }
+
+ /**
+ * Decrypt an EC pair producing the original EC point.
+ *
+ * @param pair the EC point pair to process.
+ * @return the result of the Elgamal process.
+ */
+ public ECPoint decrypt(ECPair pair)
+ {
+ if (key == null)
+ {
+ throw new IllegalStateException("ECElGamalDecryptor not initialised");
+ }
+
+ ECPoint tmp = pair.getX().multiply(key.getD());
+
+ return pair.getY().add(tmp.negate());
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java b/core/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java
new file mode 100644
index 00000000..e5569a8d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java
@@ -0,0 +1,74 @@
+package org.bouncycastle.crypto.ec;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECPoint;
+
+/**
+ * this does your basic ElGamal encryption algorithm using EC
+ */
+public class ECElGamalEncryptor
+ implements ECEncryptor
+{
+ private ECPublicKeyParameters key;
+ private SecureRandom random;
+
+ /**
+ * initialise the encryptor.
+ *
+ * @param param the necessary EC key parameters.
+ */
+ public void init(
+ CipherParameters param)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom p = (ParametersWithRandom)param;
+
+ if (!(p.getParameters() instanceof ECPublicKeyParameters))
+ {
+ throw new IllegalArgumentException("ECPublicKeyParameters are required for encryption.");
+ }
+ this.key = (ECPublicKeyParameters)p.getParameters();
+ this.random = p.getRandom();
+ }
+ else
+ {
+ if (!(param instanceof ECPublicKeyParameters))
+ {
+ throw new IllegalArgumentException("ECPublicKeyParameters are required for encryption.");
+ }
+
+ this.key = (ECPublicKeyParameters)param;
+ this.random = new SecureRandom();
+ }
+ }
+
+ /**
+ * Process a single EC point using the basic ElGamal algorithm.
+ *
+ * @param point the EC point to process.
+ * @return the result of the Elgamal process.
+ */
+ public ECPair encrypt(ECPoint point)
+ {
+ if (key == null)
+ {
+ throw new IllegalStateException("ECElGamalEncryptor not initialised");
+ }
+
+ BigInteger n = key.getParameters().getN();
+ BigInteger k = ECUtil.generateK(n, random);
+
+ ECPoint g = key.getParameters().getG();
+ ECPoint gamma = g.multiply(k);
+ ECPoint phi = key.getQ().multiply(k).add(point);
+
+ return new ECPair(gamma, phi);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/ec/ECEncryptor.java b/core/src/main/java/org/bouncycastle/crypto/ec/ECEncryptor.java
new file mode 100644
index 00000000..39704b94
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/ec/ECEncryptor.java
@@ -0,0 +1,11 @@
+package org.bouncycastle.crypto.ec;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.math.ec.ECPoint;
+
+public interface ECEncryptor
+{
+ void init(CipherParameters params);
+
+ ECPair encrypt(ECPoint point);
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java b/core/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java
new file mode 100644
index 00000000..32ba0706
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java
@@ -0,0 +1,74 @@
+package org.bouncycastle.crypto.ec;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.math.ec.ECPoint;
+
+/**
+ * this does your basic Elgamal encryption algorithm using EC
+ */
+public class ECNewPublicKeyTransform
+ implements ECPairTransform
+{
+ private ECPublicKeyParameters key;
+ private SecureRandom random;
+
+ /**
+ * initialise the EC Elgamal engine.
+ *
+ * @param param the necessary EC key parameters.
+ */
+ public void init(
+ CipherParameters param)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom p = (ParametersWithRandom)param;
+
+ if (!(p.getParameters() instanceof ECPublicKeyParameters))
+ {
+ throw new IllegalArgumentException("ECPublicKeyParameters are required for new public key transform.");
+ }
+ this.key = (ECPublicKeyParameters)p.getParameters();
+ this.random = p.getRandom();
+ }
+ else
+ {
+ if (!(param instanceof ECPublicKeyParameters))
+ {
+ throw new IllegalArgumentException("ECPublicKeyParameters are required for new public key transform.");
+ }
+
+ this.key = (ECPublicKeyParameters)param;
+ this.random = new SecureRandom();
+ }
+ }
+
+ /**
+ * Transform an existing cipher test pair using the ElGamal algorithm. Note: the input cipherText will
+ * need to be preserved in order to complete the transformation to the new public key.
+ *
+ * @param cipherText the EC point to process.
+ * @return returns a new ECPair representing the result of the process.
+ */
+ public ECPair transform(ECPair cipherText)
+ {
+ if (key == null)
+ {
+ throw new IllegalStateException("ECNewPublicKeyTransform not initialised");
+ }
+
+ BigInteger n = key.getParameters().getN();
+ BigInteger k = ECUtil.generateK(n, random);
+
+ ECPoint g = key.getParameters().getG();
+ ECPoint gamma = g.multiply(k);
+ ECPoint phi = key.getQ().multiply(k).add(cipherText.getY());
+
+ return new ECPair(gamma, phi);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java b/core/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java
new file mode 100644
index 00000000..b0379841
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java
@@ -0,0 +1,76 @@
+package org.bouncycastle.crypto.ec;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.math.ec.ECPoint;
+
+/**
+ * this transforms the original randomness used for an ElGamal encryption.
+ */
+public class ECNewRandomnessTransform
+ implements ECPairTransform
+{
+ private ECPublicKeyParameters key;
+ private SecureRandom random;
+
+ /**
+ * initialise the underlying EC ElGamal engine.
+ *
+ * @param param the necessary EC key parameters.
+ */
+ public void init(
+ CipherParameters param)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom p = (ParametersWithRandom)param;
+
+ if (!(p.getParameters() instanceof ECPublicKeyParameters))
+ {
+ throw new IllegalArgumentException("ECPublicKeyParameters are required for new randomness transform.");
+ }
+
+ this.key = (ECPublicKeyParameters)p.getParameters();
+ this.random = p.getRandom();
+ }
+ else
+ {
+ if (!(param instanceof ECPublicKeyParameters))
+ {
+ throw new IllegalArgumentException("ECPublicKeyParameters are required for new randomness transform.");
+ }
+
+ this.key = (ECPublicKeyParameters)param;
+ this.random = new SecureRandom();
+ }
+ }
+
+ /**
+ * Transform an existing cipher test pair using the ElGamal algorithm. Note: it is assumed this
+ * transform has been initialised with the same public key that was used to create the original
+ * cipher text.
+ *
+ * @param cipherText the EC point to process.
+ * @return returns a new ECPair representing the result of the process.
+ */
+ public ECPair transform(ECPair cipherText)
+ {
+ if (key == null)
+ {
+ throw new IllegalStateException("ECNewRandomnessTransform not initialised");
+ }
+
+ BigInteger n = key.getParameters().getN();
+ BigInteger k = ECUtil.generateK(n, random);
+
+ ECPoint g = key.getParameters().getG();
+ ECPoint gamma = g.multiply(k);
+ ECPoint phi = key.getQ().multiply(k).add(cipherText.getY());
+
+ return new ECPair(cipherText.getX().add(gamma), phi);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/ec/ECPair.java b/core/src/main/java/org/bouncycastle/crypto/ec/ECPair.java
new file mode 100644
index 00000000..d910f3c7
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/ec/ECPair.java
@@ -0,0 +1,38 @@
+package org.bouncycastle.crypto.ec;
+
+import org.bouncycastle.math.ec.ECPoint;
+
+public class ECPair
+{
+ private final ECPoint x;
+ private final ECPoint y;
+
+ public ECPair(ECPoint x, ECPoint y)
+ {
+ this.x = x;
+ this.y = y;
+ }
+
+ public ECPoint getX()
+ {
+ return x;
+ }
+
+ public ECPoint getY()
+ {
+ 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/core/src/main/java/org/bouncycastle/crypto/ec/ECPairTransform.java b/core/src/main/java/org/bouncycastle/crypto/ec/ECPairTransform.java
new file mode 100644
index 00000000..e3f1787d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/ec/ECPairTransform.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.crypto.ec;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public interface ECPairTransform
+{
+ void init(CipherParameters params);
+
+ ECPair transform(ECPair cipherText);
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/ec/ECUtil.java b/core/src/main/java/org/bouncycastle/crypto/ec/ECUtil.java
new file mode 100644
index 00000000..d21d8fd3
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/ec/ECUtil.java
@@ -0,0 +1,22 @@
+package org.bouncycastle.crypto.ec;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.math.ec.ECConstants;
+
+class ECUtil
+{
+ static BigInteger generateK(BigInteger n, SecureRandom random)
+ {
+ int nBitLength = n.bitLength();
+ BigInteger k = new BigInteger(nBitLength, random);
+
+ while (k.equals(ECConstants.ZERO) || (k.compareTo(n) >= 0))
+ {
+ k = new BigInteger(nBitLength, random);
+ }
+
+ return k;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/encodings/ISO9796d1Encoding.java b/core/src/main/java/org/bouncycastle/crypto/encodings/ISO9796d1Encoding.java
new file mode 100644
index 00000000..ec91e1ac
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/encodings/ISO9796d1Encoding.java
@@ -0,0 +1,287 @@
+package org.bouncycastle.crypto.encodings;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+
+/**
+ * ISO 9796-1 padding. Note in the light of recent results you should
+ * only use this with RSA (rather than the "simpler" Rabin keys) and you
+ * should never use it with anything other than a hash (ie. even if the
+ * message is small don't sign the message, sign it's hash) or some "random"
+ * value. See your favorite search engine for details.
+ */
+public class ISO9796d1Encoding
+ implements AsymmetricBlockCipher
+{
+ private static final BigInteger SIXTEEN = BigInteger.valueOf(16L);
+ private static final BigInteger SIX = BigInteger.valueOf(6L);
+
+ private static byte[] shadows = { 0xe, 0x3, 0x5, 0x8, 0x9, 0x4, 0x2, 0xf,
+ 0x0, 0xd, 0xb, 0x6, 0x7, 0xa, 0xc, 0x1 };
+ private static byte[] inverse = { 0x8, 0xf, 0x6, 0x1, 0x5, 0x2, 0xb, 0xc,
+ 0x3, 0x4, 0xd, 0xa, 0xe, 0x9, 0x0, 0x7 };
+
+ private AsymmetricBlockCipher engine;
+ private boolean forEncryption;
+ private int bitSize;
+ private int padBits = 0;
+ private BigInteger modulus;
+
+ public ISO9796d1Encoding(
+ AsymmetricBlockCipher cipher)
+ {
+ this.engine = cipher;
+ }
+
+ public AsymmetricBlockCipher getUnderlyingCipher()
+ {
+ return engine;
+ }
+
+ public void init(
+ boolean forEncryption,
+ CipherParameters param)
+ {
+ RSAKeyParameters kParam = null;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ kParam = (RSAKeyParameters)rParam.getParameters();
+ }
+ else
+ {
+ kParam = (RSAKeyParameters)param;
+ }
+
+ engine.init(forEncryption, param);
+
+ modulus = kParam.getModulus();
+ bitSize = modulus.bitLength();
+
+ this.forEncryption = forEncryption;
+ }
+
+ /**
+ * return the input block size. The largest message we can process
+ * is (key_size_in_bits + 3)/16, which in our world comes to
+ * key_size_in_bytes / 2.
+ */
+ public int getInputBlockSize()
+ {
+ int baseBlockSize = engine.getInputBlockSize();
+
+ if (forEncryption)
+ {
+ return (baseBlockSize + 1) / 2;
+ }
+ else
+ {
+ return baseBlockSize;
+ }
+ }
+
+ /**
+ * return the maximum possible size for the output.
+ */
+ public int getOutputBlockSize()
+ {
+ int baseBlockSize = engine.getOutputBlockSize();
+
+ if (forEncryption)
+ {
+ return baseBlockSize;
+ }
+ else
+ {
+ return (baseBlockSize + 1) / 2;
+ }
+ }
+
+ /**
+ * set the number of bits in the next message to be treated as
+ * pad bits.
+ */
+ public void setPadBits(
+ int padBits)
+ {
+ if (padBits > 7)
+ {
+ throw new IllegalArgumentException("padBits > 7");
+ }
+
+ this.padBits = padBits;
+ }
+
+ /**
+ * retrieve the number of pad bits in the last decoded message.
+ */
+ public int getPadBits()
+ {
+ return padBits;
+ }
+
+ public byte[] processBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ if (forEncryption)
+ {
+ return encodeBlock(in, inOff, inLen);
+ }
+ else
+ {
+ return decodeBlock(in, inOff, inLen);
+ }
+ }
+
+ private byte[] encodeBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ byte[] block = new byte[(bitSize + 7) / 8];
+ int r = padBits + 1;
+ int z = inLen;
+ int t = (bitSize + 13) / 16;
+
+ for (int i = 0; i < t; i += z)
+ {
+ if (i > t - z)
+ {
+ System.arraycopy(in, inOff + inLen - (t - i),
+ block, block.length - t, t - i);
+ }
+ else
+ {
+ System.arraycopy(in, inOff, block, block.length - (i + z), z);
+ }
+ }
+
+ for (int i = block.length - 2 * t; i != block.length; i += 2)
+ {
+ byte val = block[block.length - t + i / 2];
+
+ block[i] = (byte)((shadows[(val & 0xff) >>> 4] << 4)
+ | shadows[val & 0x0f]);
+ block[i + 1] = val;
+ }
+
+ block[block.length - 2 * z] ^= r;
+ block[block.length - 1] = (byte)((block[block.length - 1] << 4) | 0x06);
+
+ int maxBit = (8 - (bitSize - 1) % 8);
+ int offSet = 0;
+
+ if (maxBit != 8)
+ {
+ block[0] &= 0xff >>> maxBit;
+ block[0] |= 0x80 >>> maxBit;
+ }
+ else
+ {
+ block[0] = 0x00;
+ block[1] |= 0x80;
+ offSet = 1;
+ }
+
+ return engine.processBlock(block, offSet, block.length - offSet);
+ }
+
+ /**
+ * @exception InvalidCipherTextException if the decrypted block is not a valid ISO 9796 bit string
+ */
+ private byte[] decodeBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ byte[] block = engine.processBlock(in, inOff, inLen);
+ int r = 1;
+ int t = (bitSize + 13) / 16;
+
+ BigInteger iS = new BigInteger(1, block);
+ BigInteger iR;
+ if (iS.mod(SIXTEEN).equals(SIX))
+ {
+ iR = iS;
+ }
+ else if ((modulus.subtract(iS)).mod(SIXTEEN).equals(SIX))
+ {
+ iR = modulus.subtract(iS);
+ }
+ else
+ {
+ throw new InvalidCipherTextException("resulting integer iS or (modulus - iS) is not congruent to 6 mod 16");
+ }
+
+ block = convertOutputDecryptOnly(iR);
+
+ if ((block[block.length - 1] & 0x0f) != 0x6 )
+ {
+ throw new InvalidCipherTextException("invalid forcing byte in block");
+ }
+
+ block[block.length - 1] = (byte)(((block[block.length - 1] & 0xff) >>> 4) | ((inverse[(block[block.length - 2] & 0xff) >> 4]) << 4));
+ block[0] = (byte)((shadows[(block[1] & 0xff) >>> 4] << 4)
+ | shadows[block[1] & 0x0f]);
+
+ boolean boundaryFound = false;
+ int boundary = 0;
+
+ for (int i = block.length - 1; i >= block.length - 2 * t; i -= 2)
+ {
+ int val = ((shadows[(block[i] & 0xff) >>> 4] << 4)
+ | shadows[block[i] & 0x0f]);
+
+ if (((block[i - 1] ^ val) & 0xff) != 0)
+ {
+ if (!boundaryFound)
+ {
+ boundaryFound = true;
+ r = (block[i - 1] ^ val) & 0xff;
+ boundary = i - 1;
+ }
+ else
+ {
+ throw new InvalidCipherTextException("invalid tsums in block");
+ }
+ }
+ }
+
+ block[boundary] = 0;
+
+ byte[] nblock = new byte[(block.length - boundary) / 2];
+
+ for (int i = 0; i < nblock.length; i++)
+ {
+ nblock[i] = block[2 * i + boundary + 1];
+ }
+
+ padBits = r - 1;
+
+ return nblock;
+ }
+
+ private static byte[] convertOutputDecryptOnly(BigInteger result)
+ {
+ byte[] output = result.toByteArray();
+ if (output[0] == 0) // have ended up with an extra zero byte, copy down.
+ {
+ byte[] tmp = new byte[output.length - 1];
+ System.arraycopy(output, 1, tmp, 0, tmp.length);
+ return tmp;
+ }
+ return output;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java b/core/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java
new file mode 100644
index 00000000..17d8e3b0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java
@@ -0,0 +1,357 @@
+package org.bouncycastle.crypto.encodings;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+
+/**
+ * Optimal Asymmetric Encryption Padding (OAEP) - see PKCS 1 V 2.
+ */
+public class OAEPEncoding
+ implements AsymmetricBlockCipher
+{
+ private byte[] defHash;
+ private Digest mgf1Hash;
+
+ private AsymmetricBlockCipher engine;
+ private SecureRandom random;
+ private boolean forEncryption;
+
+ public OAEPEncoding(
+ AsymmetricBlockCipher cipher)
+ {
+ this(cipher, new SHA1Digest(), null);
+ }
+
+ public OAEPEncoding(
+ AsymmetricBlockCipher cipher,
+ Digest hash)
+ {
+ this(cipher, hash, null);
+ }
+
+ public OAEPEncoding(
+ AsymmetricBlockCipher cipher,
+ Digest hash,
+ byte[] encodingParams)
+ {
+ this(cipher, hash, hash, encodingParams);
+ }
+
+ public OAEPEncoding(
+ AsymmetricBlockCipher cipher,
+ Digest hash,
+ Digest mgf1Hash,
+ byte[] encodingParams)
+ {
+ this.engine = cipher;
+ this.mgf1Hash = mgf1Hash;
+ this.defHash = new byte[hash.getDigestSize()];
+
+ hash.reset();
+
+ if (encodingParams != null)
+ {
+ hash.update(encodingParams, 0, encodingParams.length);
+ }
+
+ hash.doFinal(defHash, 0);
+ }
+
+ public AsymmetricBlockCipher getUnderlyingCipher()
+ {
+ return engine;
+ }
+
+ public void init(
+ boolean forEncryption,
+ CipherParameters param)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.random = rParam.getRandom();
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ }
+
+ engine.init(forEncryption, param);
+
+ this.forEncryption = forEncryption;
+ }
+
+ public int getInputBlockSize()
+ {
+ int baseBlockSize = engine.getInputBlockSize();
+
+ if (forEncryption)
+ {
+ return baseBlockSize - 1 - 2 * defHash.length;
+ }
+ else
+ {
+ return baseBlockSize;
+ }
+ }
+
+ public int getOutputBlockSize()
+ {
+ int baseBlockSize = engine.getOutputBlockSize();
+
+ if (forEncryption)
+ {
+ return baseBlockSize;
+ }
+ else
+ {
+ return baseBlockSize - 1 - 2 * defHash.length;
+ }
+ }
+
+ public byte[] processBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ if (forEncryption)
+ {
+ return encodeBlock(in, inOff, inLen);
+ }
+ else
+ {
+ return decodeBlock(in, inOff, inLen);
+ }
+ }
+
+ public byte[] encodeBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ byte[] block = new byte[getInputBlockSize() + 1 + 2 * defHash.length];
+
+ //
+ // copy in the message
+ //
+ System.arraycopy(in, inOff, block, block.length - inLen, inLen);
+
+ //
+ // add sentinel
+ //
+ block[block.length - inLen - 1] = 0x01;
+
+ //
+ // as the block is already zeroed - there's no need to add PS (the >= 0 pad of 0)
+ //
+
+ //
+ // add the hash of the encoding params.
+ //
+ System.arraycopy(defHash, 0, block, defHash.length, defHash.length);
+
+ //
+ // generate the seed.
+ //
+ byte[] seed = new byte[defHash.length];
+
+ random.nextBytes(seed);
+
+ //
+ // mask the message block.
+ //
+ byte[] mask = maskGeneratorFunction1(seed, 0, seed.length, block.length - defHash.length);
+
+ for (int i = defHash.length; i != block.length; i++)
+ {
+ block[i] ^= mask[i - defHash.length];
+ }
+
+ //
+ // add in the seed
+ //
+ System.arraycopy(seed, 0, block, 0, defHash.length);
+
+ //
+ // mask the seed.
+ //
+ mask = maskGeneratorFunction1(
+ block, defHash.length, block.length - defHash.length, defHash.length);
+
+ for (int i = 0; i != defHash.length; i++)
+ {
+ block[i] ^= mask[i];
+ }
+
+ return engine.processBlock(block, 0, block.length);
+ }
+
+ /**
+ * @exception InvalidCipherTextException if the decrypted block turns out to
+ * be badly formatted.
+ */
+ public byte[] decodeBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ byte[] data = engine.processBlock(in, inOff, inLen);
+ byte[] block;
+
+ //
+ // as we may have zeros in our leading bytes for the block we produced
+ // on encryption, we need to make sure our decrypted block comes back
+ // the same size.
+ //
+ if (data.length < engine.getOutputBlockSize())
+ {
+ block = new byte[engine.getOutputBlockSize()];
+
+ System.arraycopy(data, 0, block, block.length - data.length, data.length);
+ }
+ else
+ {
+ block = data;
+ }
+
+ if (block.length < (2 * defHash.length) + 1)
+ {
+ throw new InvalidCipherTextException("data too short");
+ }
+
+ //
+ // unmask the seed.
+ //
+ byte[] mask = maskGeneratorFunction1(
+ block, defHash.length, block.length - defHash.length, defHash.length);
+
+ for (int i = 0; i != defHash.length; i++)
+ {
+ block[i] ^= mask[i];
+ }
+
+ //
+ // unmask the message block.
+ //
+ mask = maskGeneratorFunction1(block, 0, defHash.length, block.length - defHash.length);
+
+ for (int i = defHash.length; i != block.length; i++)
+ {
+ block[i] ^= mask[i - defHash.length];
+ }
+
+ //
+ // check the hash of the encoding params.
+ // long check to try to avoid this been a source of a timing attack.
+ //
+ boolean defHashWrong = false;
+
+ for (int i = 0; i != defHash.length; i++)
+ {
+ if (defHash[i] != block[defHash.length + i])
+ {
+ defHashWrong = true;
+ }
+ }
+
+ if (defHashWrong)
+ {
+ throw new InvalidCipherTextException("data hash wrong");
+ }
+
+ //
+ // find the data block
+ //
+ int start;
+
+ for (start = 2 * defHash.length; start != block.length; start++)
+ {
+ if (block[start] != 0)
+ {
+ break;
+ }
+ }
+
+ if (start >= (block.length - 1) || block[start] != 1)
+ {
+ throw new InvalidCipherTextException("data start wrong " + start);
+ }
+
+ start++;
+
+ //
+ // extract the data block
+ //
+ byte[] output = new byte[block.length - start];
+
+ System.arraycopy(block, start, output, 0, output.length);
+
+ return output;
+ }
+
+ /**
+ * int to octet string.
+ */
+ private void ItoOSP(
+ int i,
+ byte[] sp)
+ {
+ sp[0] = (byte)(i >>> 24);
+ sp[1] = (byte)(i >>> 16);
+ sp[2] = (byte)(i >>> 8);
+ sp[3] = (byte)(i >>> 0);
+ }
+
+ /**
+ * mask generator function, as described in PKCS1v2.
+ */
+ private byte[] maskGeneratorFunction1(
+ byte[] Z,
+ int zOff,
+ int zLen,
+ int length)
+ {
+ byte[] mask = new byte[length];
+ byte[] hashBuf = new byte[mgf1Hash.getDigestSize()];
+ byte[] C = new byte[4];
+ int counter = 0;
+
+ mgf1Hash.reset();
+
+ while (counter < (length / hashBuf.length))
+ {
+ ItoOSP(counter, C);
+
+ mgf1Hash.update(Z, zOff, zLen);
+ mgf1Hash.update(C, 0, C.length);
+ mgf1Hash.doFinal(hashBuf, 0);
+
+ System.arraycopy(hashBuf, 0, mask, counter * hashBuf.length, hashBuf.length);
+
+ counter++;
+ }
+
+ if ((counter * hashBuf.length) < length)
+ {
+ ItoOSP(counter, C);
+
+ mgf1Hash.update(Z, zOff, zLen);
+ mgf1Hash.update(C, 0, C.length);
+ mgf1Hash.doFinal(hashBuf, 0);
+
+ System.arraycopy(hashBuf, 0, mask, counter * hashBuf.length, mask.length - (counter * hashBuf.length));
+ }
+
+ return mask;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java b/core/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
new file mode 100644
index 00000000..09f1537e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
@@ -0,0 +1,257 @@
+package org.bouncycastle.crypto.encodings;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+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;
+
+/**
+ * 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.
+ */
+public class PKCS1Encoding
+ implements AsymmetricBlockCipher
+{
+ /**
+ * some providers fail to include the leading zero in PKCS1 encoded blocks. If you need to
+ * work with one of these set the system property org.bouncycastle.pkcs1.strict to false.
+ * <p>
+ * The system property is checked during construction of the encoding object, it is set to
+ * true by default.
+ * </p>
+ */
+ public static final String STRICT_LENGTH_ENABLED_PROPERTY = "org.bouncycastle.pkcs1.strict";
+
+ private static final int HEADER_LENGTH = 10;
+
+ private SecureRandom random;
+ private AsymmetricBlockCipher engine;
+ private boolean forEncryption;
+ private boolean forPrivateKey;
+ private boolean useStrictLength;
+
+ /**
+ * Basic constructor.
+ * @param cipher
+ */
+ public PKCS1Encoding(
+ AsymmetricBlockCipher cipher)
+ {
+ this.engine = cipher;
+ this.useStrictLength = useStrict();
+ }
+
+ //
+ // for J2ME compatibility
+ //
+ private boolean useStrict()
+ {
+ // required if security manager has been installed.
+ String strict = (String)AccessController.doPrivileged(new PrivilegedAction()
+ {
+ public Object run()
+ {
+ return System.getProperty(STRICT_LENGTH_ENABLED_PROPERTY);
+ }
+ });
+
+ return strict == null || strict.equals("true");
+ }
+
+ public AsymmetricBlockCipher getUnderlyingCipher()
+ {
+ return engine;
+ }
+
+ public void init(
+ boolean forEncryption,
+ CipherParameters param)
+ {
+ AsymmetricKeyParameter kParam;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.random = rParam.getRandom();
+ kParam = (AsymmetricKeyParameter)rParam.getParameters();
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ kParam = (AsymmetricKeyParameter)param;
+ }
+
+ engine.init(forEncryption, param);
+
+ this.forPrivateKey = kParam.isPrivate();
+ this.forEncryption = forEncryption;
+ }
+
+ public int getInputBlockSize()
+ {
+ int baseBlockSize = engine.getInputBlockSize();
+
+ if (forEncryption)
+ {
+ return baseBlockSize - HEADER_LENGTH;
+ }
+ else
+ {
+ return baseBlockSize;
+ }
+ }
+
+ public int getOutputBlockSize()
+ {
+ int baseBlockSize = engine.getOutputBlockSize();
+
+ if (forEncryption)
+ {
+ return baseBlockSize;
+ }
+ else
+ {
+ return baseBlockSize - HEADER_LENGTH;
+ }
+ }
+
+ public byte[] processBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ if (forEncryption)
+ {
+ return encodeBlock(in, inOff, inLen);
+ }
+ else
+ {
+ return decodeBlock(in, inOff, inLen);
+ }
+ }
+
+ private byte[] encodeBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ if (inLen > getInputBlockSize())
+ {
+ throw new IllegalArgumentException("input data too large");
+ }
+
+ byte[] block = new byte[engine.getInputBlockSize()];
+
+ if (forPrivateKey)
+ {
+ block[0] = 0x01; // type code 1
+
+ for (int i = 1; i != block.length - inLen - 1; i++)
+ {
+ block[i] = (byte)0xFF;
+ }
+ }
+ else
+ {
+ random.nextBytes(block); // random fill
+
+ block[0] = 0x02; // type code 2
+
+ //
+ // a zero byte marks the end of the padding, so all
+ // the pad bytes must be non-zero.
+ //
+ for (int i = 1; i != block.length - inLen - 1; i++)
+ {
+ while (block[i] == 0)
+ {
+ block[i] = (byte)random.nextInt();
+ }
+ }
+ }
+
+ block[block.length - inLen - 1] = 0x00; // mark the end of the padding
+ System.arraycopy(in, inOff, block, block.length - inLen, inLen);
+
+ return engine.processBlock(block, 0, block.length);
+ }
+
+ /**
+ * @exception InvalidCipherTextException if the decrypted block is not in PKCS1 format.
+ */
+ private byte[] decodeBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ byte[] block = engine.processBlock(in, inOff, inLen);
+
+ if (block.length < getOutputBlockSize())
+ {
+ throw new InvalidCipherTextException("block truncated");
+ }
+
+ byte type = block[0];
+
+ if (forPrivateKey)
+ {
+ 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())
+ {
+ throw new InvalidCipherTextException("block incorrect size");
+ }
+
+ //
+ // find and extract the message block.
+ //
+ int start;
+
+ for (start = 1; start != block.length; start++)
+ {
+ byte pad = block[start];
+
+ if (pad == 0)
+ {
+ break;
+ }
+ if (type == 1 && pad != (byte)0xff)
+ {
+ throw new InvalidCipherTextException("block padding incorrect");
+ }
+ }
+
+ start++; // data should start at the next byte
+
+ if (start > block.length || start < HEADER_LENGTH)
+ {
+ throw new InvalidCipherTextException("no data in block");
+ }
+
+ byte[] result = new byte[block.length - start];
+
+ System.arraycopy(block, start, result, 0, result.length);
+
+ return result;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java
new file mode 100644
index 00000000..756197ce
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java
@@ -0,0 +1,546 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * an implementation of the AES (Rijndael), from FIPS-197.
+ * <p>
+ * For further details see: <a href="http://csrc.nist.gov/encryption/aes/">http://csrc.nist.gov/encryption/aes/</a>.
+ *
+ * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at
+ * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
+ *
+ * There are three levels of tradeoff of speed vs memory
+ * Because java has no preprocessor, they are written as three separate classes from which to choose
+ *
+ * The fastest uses 8Kbytes of static tables to precompute round calculations, 4 256 word tables for encryption
+ * and 4 for decryption.
+ *
+ * The middle performance version uses only one 256 word table for each, for a total of 2Kbytes,
+ * adding 12 rotate operations per round to compute the values contained in the other tables from
+ * the contents of the first.
+ *
+ * The slowest version uses no static tables at all and computes the values in each round.
+ * <p>
+ * This file contains the middle performance version with 2Kbytes of static tables for round precomputation.
+ *
+ */
+public class AESEngine
+ implements BlockCipher
+{
+ // The S box
+ private static final byte[] S = {
+ (byte)99, (byte)124, (byte)119, (byte)123, (byte)242, (byte)107, (byte)111, (byte)197,
+ (byte)48, (byte)1, (byte)103, (byte)43, (byte)254, (byte)215, (byte)171, (byte)118,
+ (byte)202, (byte)130, (byte)201, (byte)125, (byte)250, (byte)89, (byte)71, (byte)240,
+ (byte)173, (byte)212, (byte)162, (byte)175, (byte)156, (byte)164, (byte)114, (byte)192,
+ (byte)183, (byte)253, (byte)147, (byte)38, (byte)54, (byte)63, (byte)247, (byte)204,
+ (byte)52, (byte)165, (byte)229, (byte)241, (byte)113, (byte)216, (byte)49, (byte)21,
+ (byte)4, (byte)199, (byte)35, (byte)195, (byte)24, (byte)150, (byte)5, (byte)154,
+ (byte)7, (byte)18, (byte)128, (byte)226, (byte)235, (byte)39, (byte)178, (byte)117,
+ (byte)9, (byte)131, (byte)44, (byte)26, (byte)27, (byte)110, (byte)90, (byte)160,
+ (byte)82, (byte)59, (byte)214, (byte)179, (byte)41, (byte)227, (byte)47, (byte)132,
+ (byte)83, (byte)209, (byte)0, (byte)237, (byte)32, (byte)252, (byte)177, (byte)91,
+ (byte)106, (byte)203, (byte)190, (byte)57, (byte)74, (byte)76, (byte)88, (byte)207,
+ (byte)208, (byte)239, (byte)170, (byte)251, (byte)67, (byte)77, (byte)51, (byte)133,
+ (byte)69, (byte)249, (byte)2, (byte)127, (byte)80, (byte)60, (byte)159, (byte)168,
+ (byte)81, (byte)163, (byte)64, (byte)143, (byte)146, (byte)157, (byte)56, (byte)245,
+ (byte)188, (byte)182, (byte)218, (byte)33, (byte)16, (byte)255, (byte)243, (byte)210,
+ (byte)205, (byte)12, (byte)19, (byte)236, (byte)95, (byte)151, (byte)68, (byte)23,
+ (byte)196, (byte)167, (byte)126, (byte)61, (byte)100, (byte)93, (byte)25, (byte)115,
+ (byte)96, (byte)129, (byte)79, (byte)220, (byte)34, (byte)42, (byte)144, (byte)136,
+ (byte)70, (byte)238, (byte)184, (byte)20, (byte)222, (byte)94, (byte)11, (byte)219,
+ (byte)224, (byte)50, (byte)58, (byte)10, (byte)73, (byte)6, (byte)36, (byte)92,
+ (byte)194, (byte)211, (byte)172, (byte)98, (byte)145, (byte)149, (byte)228, (byte)121,
+ (byte)231, (byte)200, (byte)55, (byte)109, (byte)141, (byte)213, (byte)78, (byte)169,
+ (byte)108, (byte)86, (byte)244, (byte)234, (byte)101, (byte)122, (byte)174, (byte)8,
+ (byte)186, (byte)120, (byte)37, (byte)46, (byte)28, (byte)166, (byte)180, (byte)198,
+ (byte)232, (byte)221, (byte)116, (byte)31, (byte)75, (byte)189, (byte)139, (byte)138,
+ (byte)112, (byte)62, (byte)181, (byte)102, (byte)72, (byte)3, (byte)246, (byte)14,
+ (byte)97, (byte)53, (byte)87, (byte)185, (byte)134, (byte)193, (byte)29, (byte)158,
+ (byte)225, (byte)248, (byte)152, (byte)17, (byte)105, (byte)217, (byte)142, (byte)148,
+ (byte)155, (byte)30, (byte)135, (byte)233, (byte)206, (byte)85, (byte)40, (byte)223,
+ (byte)140, (byte)161, (byte)137, (byte)13, (byte)191, (byte)230, (byte)66, (byte)104,
+ (byte)65, (byte)153, (byte)45, (byte)15, (byte)176, (byte)84, (byte)187, (byte)22,
+ };
+
+ // The inverse S-box
+ private static final byte[] Si = {
+ (byte)82, (byte)9, (byte)106, (byte)213, (byte)48, (byte)54, (byte)165, (byte)56,
+ (byte)191, (byte)64, (byte)163, (byte)158, (byte)129, (byte)243, (byte)215, (byte)251,
+ (byte)124, (byte)227, (byte)57, (byte)130, (byte)155, (byte)47, (byte)255, (byte)135,
+ (byte)52, (byte)142, (byte)67, (byte)68, (byte)196, (byte)222, (byte)233, (byte)203,
+ (byte)84, (byte)123, (byte)148, (byte)50, (byte)166, (byte)194, (byte)35, (byte)61,
+ (byte)238, (byte)76, (byte)149, (byte)11, (byte)66, (byte)250, (byte)195, (byte)78,
+ (byte)8, (byte)46, (byte)161, (byte)102, (byte)40, (byte)217, (byte)36, (byte)178,
+ (byte)118, (byte)91, (byte)162, (byte)73, (byte)109, (byte)139, (byte)209, (byte)37,
+ (byte)114, (byte)248, (byte)246, (byte)100, (byte)134, (byte)104, (byte)152, (byte)22,
+ (byte)212, (byte)164, (byte)92, (byte)204, (byte)93, (byte)101, (byte)182, (byte)146,
+ (byte)108, (byte)112, (byte)72, (byte)80, (byte)253, (byte)237, (byte)185, (byte)218,
+ (byte)94, (byte)21, (byte)70, (byte)87, (byte)167, (byte)141, (byte)157, (byte)132,
+ (byte)144, (byte)216, (byte)171, (byte)0, (byte)140, (byte)188, (byte)211, (byte)10,
+ (byte)247, (byte)228, (byte)88, (byte)5, (byte)184, (byte)179, (byte)69, (byte)6,
+ (byte)208, (byte)44, (byte)30, (byte)143, (byte)202, (byte)63, (byte)15, (byte)2,
+ (byte)193, (byte)175, (byte)189, (byte)3, (byte)1, (byte)19, (byte)138, (byte)107,
+ (byte)58, (byte)145, (byte)17, (byte)65, (byte)79, (byte)103, (byte)220, (byte)234,
+ (byte)151, (byte)242, (byte)207, (byte)206, (byte)240, (byte)180, (byte)230, (byte)115,
+ (byte)150, (byte)172, (byte)116, (byte)34, (byte)231, (byte)173, (byte)53, (byte)133,
+ (byte)226, (byte)249, (byte)55, (byte)232, (byte)28, (byte)117, (byte)223, (byte)110,
+ (byte)71, (byte)241, (byte)26, (byte)113, (byte)29, (byte)41, (byte)197, (byte)137,
+ (byte)111, (byte)183, (byte)98, (byte)14, (byte)170, (byte)24, (byte)190, (byte)27,
+ (byte)252, (byte)86, (byte)62, (byte)75, (byte)198, (byte)210, (byte)121, (byte)32,
+ (byte)154, (byte)219, (byte)192, (byte)254, (byte)120, (byte)205, (byte)90, (byte)244,
+ (byte)31, (byte)221, (byte)168, (byte)51, (byte)136, (byte)7, (byte)199, (byte)49,
+ (byte)177, (byte)18, (byte)16, (byte)89, (byte)39, (byte)128, (byte)236, (byte)95,
+ (byte)96, (byte)81, (byte)127, (byte)169, (byte)25, (byte)181, (byte)74, (byte)13,
+ (byte)45, (byte)229, (byte)122, (byte)159, (byte)147, (byte)201, (byte)156, (byte)239,
+ (byte)160, (byte)224, (byte)59, (byte)77, (byte)174, (byte)42, (byte)245, (byte)176,
+ (byte)200, (byte)235, (byte)187, (byte)60, (byte)131, (byte)83, (byte)153, (byte)97,
+ (byte)23, (byte)43, (byte)4, (byte)126, (byte)186, (byte)119, (byte)214, (byte)38,
+ (byte)225, (byte)105, (byte)20, (byte)99, (byte)85, (byte)33, (byte)12, (byte)125,
+ };
+
+ // vector used in calculating key schedule (powers of x in GF(256))
+ private static final int[] rcon = {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
+ 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 };
+
+ // precomputation tables of calculations for rounds
+ private static final int[] T0 =
+ {
+ 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff,
+ 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102,
+ 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d,
+ 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa,
+ 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41,
+ 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453,
+ 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d,
+ 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83,
+ 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2,
+ 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795,
+ 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a,
+ 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df,
+ 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912,
+ 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc,
+ 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7,
+ 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413,
+ 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040,
+ 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d,
+ 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0,
+ 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed,
+ 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a,
+ 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78,
+ 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080,
+ 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1,
+ 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020,
+ 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18,
+ 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488,
+ 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a,
+ 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0,
+ 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54,
+ 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b,
+ 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad,
+ 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992,
+ 0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd,
+ 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3,
+ 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda,
+ 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8,
+ 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4,
+ 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a,
+ 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697,
+ 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96,
+ 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c,
+ 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7,
+ 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969,
+ 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9,
+ 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9,
+ 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715,
+ 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
+ 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65,
+ 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929,
+ 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d,
+ 0x3a16162c};
+
+private static final int[] Tinv0 =
+ {
+ 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b,
+ 0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad,
+ 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526,
+ 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d,
+ 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03,
+ 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458,
+ 0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899,
+ 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d,
+ 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1,
+ 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f,
+ 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3,
+ 0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3,
+ 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a,
+ 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506,
+ 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05,
+ 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd,
+ 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491,
+ 0x055dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6,
+ 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7,
+ 0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000,
+ 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd,
+ 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68,
+ 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793, 0xd296eeb4,
+ 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c,
+ 0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0x0b0d090e,
+ 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af,
+ 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644,
+ 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8,
+ 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85,
+ 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc,
+ 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411,
+ 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322,
+ 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6,
+ 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0x0d927850,
+ 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e,
+ 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf,
+ 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd,
+ 0xf418596e, 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa,
+ 0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea,
+ 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235,
+ 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1,
+ 0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43,
+ 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1,
+ 0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb,
+ 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a,
+ 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7,
+ 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418,
+ 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478,
+ 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16,
+ 0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08,
+ 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48,
+ 0x4257b8d0};
+
+ private static int shift(int r, int shift)
+ {
+ return (r >>> shift) | (r << -shift);
+ }
+
+ /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
+
+ private static final int m1 = 0x80808080;
+ private static final int m2 = 0x7f7f7f7f;
+ private static final int m3 = 0x0000001b;
+
+ private static int FFmulX(int x)
+ {
+ return (((x & m2) << 1) ^ (((x & m1) >>> 7) * m3));
+ }
+
+ /*
+ The following defines provide alternative definitions of FFmulX that might
+ give improved performance if a fast 32-bit multiply is not available.
+
+ private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); }
+ private static final int m4 = 0x1b1b1b1b;
+ private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); }
+
+ */
+
+ private static int inv_mcol(int x)
+ {
+ int f2 = FFmulX(x);
+ int f4 = FFmulX(f2);
+ int f8 = FFmulX(f4);
+ int f9 = x ^ f8;
+
+ return f2 ^ f4 ^ f8 ^ shift(f2 ^ f9, 8) ^ shift(f4 ^ f9, 16) ^ shift(f9, 24);
+ }
+
+ private static int subWord(int x)
+ {
+ return (S[x&255]&255 | ((S[(x>>8)&255]&255)<<8) | ((S[(x>>16)&255]&255)<<16) | S[(x>>24)&255]<<24);
+ }
+
+ /**
+ * Calculate the necessary round keys
+ * The number of calculations depends on key size and block size
+ * AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits
+ * This code is written assuming those are the only possible values
+ */
+ private int[][] generateWorkingKey(
+ byte[] key,
+ boolean forEncryption)
+ {
+ int KC = key.length / 4; // key length in words
+ int t;
+
+ if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.length))
+ {
+ throw new IllegalArgumentException("Key length not 128/192/256 bits.");
+ }
+
+ ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes
+ int[][] W = new int[ROUNDS+1][4]; // 4 words in a block
+
+ //
+ // copy the key into the round key array
+ //
+
+ t = 0;
+ int i = 0;
+ while (i < key.length)
+ {
+ W[t >> 2][t & 3] = (key[i]&0xff) | ((key[i+1]&0xff) << 8) | ((key[i+2]&0xff) << 16) | (key[i+3] << 24);
+ i+=4;
+ t++;
+ }
+
+ //
+ // while not enough round key material calculated
+ // calculate new values
+ //
+ int k = (ROUNDS + 1) << 2;
+ for (i = KC; (i < k); i++)
+ {
+ int temp = W[(i-1)>>2][(i-1)&3];
+ if ((i % KC) == 0)
+ {
+ temp = subWord(shift(temp, 8)) ^ rcon[(i / KC)-1];
+ }
+ else if ((KC > 6) && ((i % KC) == 4))
+ {
+ temp = subWord(temp);
+ }
+
+ W[i>>2][i&3] = W[(i - KC)>>2][(i-KC)&3] ^ temp;
+ }
+
+ if (!forEncryption)
+ {
+ for (int j = 1; j < ROUNDS; j++)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ W[j][i] = inv_mcol(W[j][i]);
+ }
+ }
+ }
+
+ return W;
+ }
+
+ private int ROUNDS;
+ private int[][] WorkingKey = null;
+ private int C0, C1, C2, C3;
+ private boolean forEncryption;
+
+ private static final int BLOCK_SIZE = 16;
+
+ /**
+ * default constructor - 128 bit block size.
+ */
+ public AESEngine()
+ {
+ }
+
+ /**
+ * initialise an AES cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ WorkingKey = generateWorkingKey(((KeyParameter)params).getKey(), forEncryption);
+ this.forEncryption = forEncryption;
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to AES init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "AES";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (WorkingKey == null)
+ {
+ throw new IllegalStateException("AES engine not initialised");
+ }
+
+ if ((inOff + (32 / 2)) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + (32 / 2)) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (forEncryption)
+ {
+ unpackBlock(in, inOff);
+ encryptBlock(WorkingKey);
+ packBlock(out, outOff);
+ }
+ else
+ {
+ unpackBlock(in, inOff);
+ decryptBlock(WorkingKey);
+ packBlock(out, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+
+ private void unpackBlock(
+ byte[] bytes,
+ int off)
+ {
+ int index = off;
+
+ C0 = (bytes[index++] & 0xff);
+ C0 |= (bytes[index++] & 0xff) << 8;
+ C0 |= (bytes[index++] & 0xff) << 16;
+ C0 |= bytes[index++] << 24;
+
+ C1 = (bytes[index++] & 0xff);
+ C1 |= (bytes[index++] & 0xff) << 8;
+ C1 |= (bytes[index++] & 0xff) << 16;
+ C1 |= bytes[index++] << 24;
+
+ C2 = (bytes[index++] & 0xff);
+ C2 |= (bytes[index++] & 0xff) << 8;
+ C2 |= (bytes[index++] & 0xff) << 16;
+ C2 |= bytes[index++] << 24;
+
+ C3 = (bytes[index++] & 0xff);
+ C3 |= (bytes[index++] & 0xff) << 8;
+ C3 |= (bytes[index++] & 0xff) << 16;
+ C3 |= bytes[index++] << 24;
+ }
+
+ private void packBlock(
+ byte[] bytes,
+ int off)
+ {
+ int index = off;
+
+ bytes[index++] = (byte)C0;
+ bytes[index++] = (byte)(C0 >> 8);
+ bytes[index++] = (byte)(C0 >> 16);
+ bytes[index++] = (byte)(C0 >> 24);
+
+ bytes[index++] = (byte)C1;
+ bytes[index++] = (byte)(C1 >> 8);
+ bytes[index++] = (byte)(C1 >> 16);
+ bytes[index++] = (byte)(C1 >> 24);
+
+ bytes[index++] = (byte)C2;
+ bytes[index++] = (byte)(C2 >> 8);
+ bytes[index++] = (byte)(C2 >> 16);
+ bytes[index++] = (byte)(C2 >> 24);
+
+ bytes[index++] = (byte)C3;
+ bytes[index++] = (byte)(C3 >> 8);
+ bytes[index++] = (byte)(C3 >> 16);
+ bytes[index++] = (byte)(C3 >> 24);
+ }
+
+
+ private void encryptBlock(int[][] KW)
+ {
+ int r, r0, r1, r2, r3;
+
+ C0 ^= KW[0][0];
+ C1 ^= KW[0][1];
+ C2 ^= KW[0][2];
+ C3 ^= KW[0][3];
+
+ r = 1;
+
+ while (r < ROUNDS - 1)
+ {
+ r0 = T0[C0&255] ^ shift(T0[(C1>>8)&255], 24) ^ shift(T0[(C2>>16)&255],16) ^ shift(T0[(C3>>24)&255],8) ^ KW[r][0];
+ r1 = T0[C1&255] ^ shift(T0[(C2>>8)&255], 24) ^ shift(T0[(C3>>16)&255], 16) ^ shift(T0[(C0>>24)&255], 8) ^ KW[r][1];
+ r2 = T0[C2&255] ^ shift(T0[(C3>>8)&255], 24) ^ shift(T0[(C0>>16)&255], 16) ^ shift(T0[(C1>>24)&255], 8) ^ KW[r][2];
+ r3 = T0[C3&255] ^ shift(T0[(C0>>8)&255], 24) ^ shift(T0[(C1>>16)&255], 16) ^ shift(T0[(C2>>24)&255], 8) ^ KW[r++][3];
+ C0 = T0[r0&255] ^ shift(T0[(r1>>8)&255], 24) ^ shift(T0[(r2>>16)&255], 16) ^ shift(T0[(r3>>24)&255], 8) ^ KW[r][0];
+ C1 = T0[r1&255] ^ shift(T0[(r2>>8)&255], 24) ^ shift(T0[(r3>>16)&255], 16) ^ shift(T0[(r0>>24)&255], 8) ^ KW[r][1];
+ C2 = T0[r2&255] ^ shift(T0[(r3>>8)&255], 24) ^ shift(T0[(r0>>16)&255], 16) ^ shift(T0[(r1>>24)&255], 8) ^ KW[r][2];
+ C3 = T0[r3&255] ^ shift(T0[(r0>>8)&255], 24) ^ shift(T0[(r1>>16)&255], 16) ^ shift(T0[(r2>>24)&255], 8) ^ KW[r++][3];
+ }
+
+ r0 = T0[C0&255] ^ shift(T0[(C1>>8)&255], 24) ^ shift(T0[(C2>>16)&255], 16) ^ shift(T0[(C3>>24)&255], 8) ^ KW[r][0];
+ r1 = T0[C1&255] ^ shift(T0[(C2>>8)&255], 24) ^ shift(T0[(C3>>16)&255], 16) ^ shift(T0[(C0>>24)&255], 8) ^ KW[r][1];
+ r2 = T0[C2&255] ^ shift(T0[(C3>>8)&255], 24) ^ shift(T0[(C0>>16)&255], 16) ^ shift(T0[(C1>>24)&255], 8) ^ KW[r][2];
+ r3 = T0[C3&255] ^ shift(T0[(C0>>8)&255], 24) ^ shift(T0[(C1>>16)&255], 16) ^ shift(T0[(C2>>24)&255], 8) ^ KW[r++][3];
+
+ // the final round's table is a simple function of S so we don't use a whole other four tables for it
+
+ C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24) ^ KW[r][0];
+ C1 = (S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24) ^ KW[r][1];
+ C2 = (S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r][2];
+ C3 = (S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r][3];
+
+ }
+
+ private void decryptBlock(int[][] KW)
+ {
+ int r, r0, r1, r2, r3;
+
+ C0 ^= KW[ROUNDS][0];
+ C1 ^= KW[ROUNDS][1];
+ C2 ^= KW[ROUNDS][2];
+ C3 ^= KW[ROUNDS][3];
+
+ r = ROUNDS-1;
+
+ while (r>1)
+ {
+ r0 = Tinv0[C0&255] ^ shift(Tinv0[(C3>>8)&255], 24) ^ shift(Tinv0[(C2>>16)&255], 16) ^ shift(Tinv0[(C1>>24)&255], 8) ^ KW[r][0];
+ r1 = Tinv0[C1&255] ^ shift(Tinv0[(C0>>8)&255], 24) ^ shift(Tinv0[(C3>>16)&255], 16) ^ shift(Tinv0[(C2>>24)&255], 8) ^ KW[r][1];
+ r2 = Tinv0[C2&255] ^ shift(Tinv0[(C1>>8)&255], 24) ^ shift(Tinv0[(C0>>16)&255], 16) ^ shift(Tinv0[(C3>>24)&255], 8) ^ KW[r][2];
+ r3 = Tinv0[C3&255] ^ shift(Tinv0[(C2>>8)&255], 24) ^ shift(Tinv0[(C1>>16)&255], 16) ^ shift(Tinv0[(C0>>24)&255], 8) ^ KW[r--][3];
+ C0 = Tinv0[r0&255] ^ shift(Tinv0[(r3>>8)&255], 24) ^ shift(Tinv0[(r2>>16)&255], 16) ^ shift(Tinv0[(r1>>24)&255], 8) ^ KW[r][0];
+ C1 = Tinv0[r1&255] ^ shift(Tinv0[(r0>>8)&255], 24) ^ shift(Tinv0[(r3>>16)&255], 16) ^ shift(Tinv0[(r2>>24)&255], 8) ^ KW[r][1];
+ C2 = Tinv0[r2&255] ^ shift(Tinv0[(r1>>8)&255], 24) ^ shift(Tinv0[(r0>>16)&255], 16) ^ shift(Tinv0[(r3>>24)&255], 8) ^ KW[r][2];
+ C3 = Tinv0[r3&255] ^ shift(Tinv0[(r2>>8)&255], 24) ^ shift(Tinv0[(r1>>16)&255], 16) ^ shift(Tinv0[(r0>>24)&255], 8) ^ KW[r--][3];
+ }
+
+ r0 = Tinv0[C0&255] ^ shift(Tinv0[(C3>>8)&255], 24) ^ shift(Tinv0[(C2>>16)&255], 16) ^ shift(Tinv0[(C1>>24)&255], 8) ^ KW[r][0];
+ r1 = Tinv0[C1&255] ^ shift(Tinv0[(C0>>8)&255], 24) ^ shift(Tinv0[(C3>>16)&255], 16) ^ shift(Tinv0[(C2>>24)&255], 8) ^ KW[r][1];
+ r2 = Tinv0[C2&255] ^ shift(Tinv0[(C1>>8)&255], 24) ^ shift(Tinv0[(C0>>16)&255], 16) ^ shift(Tinv0[(C3>>24)&255], 8) ^ KW[r][2];
+ r3 = Tinv0[C3&255] ^ shift(Tinv0[(C2>>8)&255], 24) ^ shift(Tinv0[(C1>>16)&255], 16) ^ shift(Tinv0[(C0>>24)&255], 8) ^ KW[r][3];
+
+ // the final round's table is a simple function of Si so we don't use a whole other four tables for it
+
+ C0 = (Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0][0];
+ C1 = (Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24) ^ KW[0][1];
+ C2 = (Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24) ^ KW[0][2];
+ C3 = (Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24) ^ KW[0][3];
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java
new file mode 100644
index 00000000..ff4b2f8f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java
@@ -0,0 +1,875 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * an implementation of the AES (Rijndael), from FIPS-197.
+ * <p>
+ * For further details see: <a href="http://csrc.nist.gov/encryption/aes/">http://csrc.nist.gov/encryption/aes/</a>.
+ *
+ * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at
+ * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
+ *
+ * There are three levels of tradeoff of speed vs memory
+ * Because java has no preprocessor, they are written as three separate classes from which to choose
+ *
+ * The fastest uses 8Kbytes of static tables to precompute round calculations, 4 256 word tables for encryption
+ * and 4 for decryption.
+ *
+ * The middle performance version uses only one 256 word table for each, for a total of 2Kbytes,
+ * adding 12 rotate operations per round to compute the values contained in the other tables from
+ * the contents of the first
+ *
+ * The slowest version uses no static tables at all and computes the values in each round
+ * <p>
+ * This file contains the fast version with 8Kbytes of static tables for round precomputation
+ *
+ */
+public class AESFastEngine
+ implements BlockCipher
+{
+ // The S box
+ private static final byte[] S = {
+ (byte)99, (byte)124, (byte)119, (byte)123, (byte)242, (byte)107, (byte)111, (byte)197,
+ (byte)48, (byte)1, (byte)103, (byte)43, (byte)254, (byte)215, (byte)171, (byte)118,
+ (byte)202, (byte)130, (byte)201, (byte)125, (byte)250, (byte)89, (byte)71, (byte)240,
+ (byte)173, (byte)212, (byte)162, (byte)175, (byte)156, (byte)164, (byte)114, (byte)192,
+ (byte)183, (byte)253, (byte)147, (byte)38, (byte)54, (byte)63, (byte)247, (byte)204,
+ (byte)52, (byte)165, (byte)229, (byte)241, (byte)113, (byte)216, (byte)49, (byte)21,
+ (byte)4, (byte)199, (byte)35, (byte)195, (byte)24, (byte)150, (byte)5, (byte)154,
+ (byte)7, (byte)18, (byte)128, (byte)226, (byte)235, (byte)39, (byte)178, (byte)117,
+ (byte)9, (byte)131, (byte)44, (byte)26, (byte)27, (byte)110, (byte)90, (byte)160,
+ (byte)82, (byte)59, (byte)214, (byte)179, (byte)41, (byte)227, (byte)47, (byte)132,
+ (byte)83, (byte)209, (byte)0, (byte)237, (byte)32, (byte)252, (byte)177, (byte)91,
+ (byte)106, (byte)203, (byte)190, (byte)57, (byte)74, (byte)76, (byte)88, (byte)207,
+ (byte)208, (byte)239, (byte)170, (byte)251, (byte)67, (byte)77, (byte)51, (byte)133,
+ (byte)69, (byte)249, (byte)2, (byte)127, (byte)80, (byte)60, (byte)159, (byte)168,
+ (byte)81, (byte)163, (byte)64, (byte)143, (byte)146, (byte)157, (byte)56, (byte)245,
+ (byte)188, (byte)182, (byte)218, (byte)33, (byte)16, (byte)255, (byte)243, (byte)210,
+ (byte)205, (byte)12, (byte)19, (byte)236, (byte)95, (byte)151, (byte)68, (byte)23,
+ (byte)196, (byte)167, (byte)126, (byte)61, (byte)100, (byte)93, (byte)25, (byte)115,
+ (byte)96, (byte)129, (byte)79, (byte)220, (byte)34, (byte)42, (byte)144, (byte)136,
+ (byte)70, (byte)238, (byte)184, (byte)20, (byte)222, (byte)94, (byte)11, (byte)219,
+ (byte)224, (byte)50, (byte)58, (byte)10, (byte)73, (byte)6, (byte)36, (byte)92,
+ (byte)194, (byte)211, (byte)172, (byte)98, (byte)145, (byte)149, (byte)228, (byte)121,
+ (byte)231, (byte)200, (byte)55, (byte)109, (byte)141, (byte)213, (byte)78, (byte)169,
+ (byte)108, (byte)86, (byte)244, (byte)234, (byte)101, (byte)122, (byte)174, (byte)8,
+ (byte)186, (byte)120, (byte)37, (byte)46, (byte)28, (byte)166, (byte)180, (byte)198,
+ (byte)232, (byte)221, (byte)116, (byte)31, (byte)75, (byte)189, (byte)139, (byte)138,
+ (byte)112, (byte)62, (byte)181, (byte)102, (byte)72, (byte)3, (byte)246, (byte)14,
+ (byte)97, (byte)53, (byte)87, (byte)185, (byte)134, (byte)193, (byte)29, (byte)158,
+ (byte)225, (byte)248, (byte)152, (byte)17, (byte)105, (byte)217, (byte)142, (byte)148,
+ (byte)155, (byte)30, (byte)135, (byte)233, (byte)206, (byte)85, (byte)40, (byte)223,
+ (byte)140, (byte)161, (byte)137, (byte)13, (byte)191, (byte)230, (byte)66, (byte)104,
+ (byte)65, (byte)153, (byte)45, (byte)15, (byte)176, (byte)84, (byte)187, (byte)22,
+ };
+
+ // The inverse S-box
+ private static final byte[] Si = {
+ (byte)82, (byte)9, (byte)106, (byte)213, (byte)48, (byte)54, (byte)165, (byte)56,
+ (byte)191, (byte)64, (byte)163, (byte)158, (byte)129, (byte)243, (byte)215, (byte)251,
+ (byte)124, (byte)227, (byte)57, (byte)130, (byte)155, (byte)47, (byte)255, (byte)135,
+ (byte)52, (byte)142, (byte)67, (byte)68, (byte)196, (byte)222, (byte)233, (byte)203,
+ (byte)84, (byte)123, (byte)148, (byte)50, (byte)166, (byte)194, (byte)35, (byte)61,
+ (byte)238, (byte)76, (byte)149, (byte)11, (byte)66, (byte)250, (byte)195, (byte)78,
+ (byte)8, (byte)46, (byte)161, (byte)102, (byte)40, (byte)217, (byte)36, (byte)178,
+ (byte)118, (byte)91, (byte)162, (byte)73, (byte)109, (byte)139, (byte)209, (byte)37,
+ (byte)114, (byte)248, (byte)246, (byte)100, (byte)134, (byte)104, (byte)152, (byte)22,
+ (byte)212, (byte)164, (byte)92, (byte)204, (byte)93, (byte)101, (byte)182, (byte)146,
+ (byte)108, (byte)112, (byte)72, (byte)80, (byte)253, (byte)237, (byte)185, (byte)218,
+ (byte)94, (byte)21, (byte)70, (byte)87, (byte)167, (byte)141, (byte)157, (byte)132,
+ (byte)144, (byte)216, (byte)171, (byte)0, (byte)140, (byte)188, (byte)211, (byte)10,
+ (byte)247, (byte)228, (byte)88, (byte)5, (byte)184, (byte)179, (byte)69, (byte)6,
+ (byte)208, (byte)44, (byte)30, (byte)143, (byte)202, (byte)63, (byte)15, (byte)2,
+ (byte)193, (byte)175, (byte)189, (byte)3, (byte)1, (byte)19, (byte)138, (byte)107,
+ (byte)58, (byte)145, (byte)17, (byte)65, (byte)79, (byte)103, (byte)220, (byte)234,
+ (byte)151, (byte)242, (byte)207, (byte)206, (byte)240, (byte)180, (byte)230, (byte)115,
+ (byte)150, (byte)172, (byte)116, (byte)34, (byte)231, (byte)173, (byte)53, (byte)133,
+ (byte)226, (byte)249, (byte)55, (byte)232, (byte)28, (byte)117, (byte)223, (byte)110,
+ (byte)71, (byte)241, (byte)26, (byte)113, (byte)29, (byte)41, (byte)197, (byte)137,
+ (byte)111, (byte)183, (byte)98, (byte)14, (byte)170, (byte)24, (byte)190, (byte)27,
+ (byte)252, (byte)86, (byte)62, (byte)75, (byte)198, (byte)210, (byte)121, (byte)32,
+ (byte)154, (byte)219, (byte)192, (byte)254, (byte)120, (byte)205, (byte)90, (byte)244,
+ (byte)31, (byte)221, (byte)168, (byte)51, (byte)136, (byte)7, (byte)199, (byte)49,
+ (byte)177, (byte)18, (byte)16, (byte)89, (byte)39, (byte)128, (byte)236, (byte)95,
+ (byte)96, (byte)81, (byte)127, (byte)169, (byte)25, (byte)181, (byte)74, (byte)13,
+ (byte)45, (byte)229, (byte)122, (byte)159, (byte)147, (byte)201, (byte)156, (byte)239,
+ (byte)160, (byte)224, (byte)59, (byte)77, (byte)174, (byte)42, (byte)245, (byte)176,
+ (byte)200, (byte)235, (byte)187, (byte)60, (byte)131, (byte)83, (byte)153, (byte)97,
+ (byte)23, (byte)43, (byte)4, (byte)126, (byte)186, (byte)119, (byte)214, (byte)38,
+ (byte)225, (byte)105, (byte)20, (byte)99, (byte)85, (byte)33, (byte)12, (byte)125,
+ };
+
+ // vector used in calculating key schedule (powers of x in GF(256))
+ private static final int[] rcon = {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
+ 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 };
+
+ // precomputation tables of calculations for rounds
+ private static final int[] T0 =
+ {
+ 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff,
+ 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102,
+ 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d,
+ 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa,
+ 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41,
+ 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453,
+ 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d,
+ 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83,
+ 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2,
+ 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795,
+ 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a,
+ 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df,
+ 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912,
+ 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc,
+ 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7,
+ 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413,
+ 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040,
+ 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d,
+ 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0,
+ 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed,
+ 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a,
+ 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78,
+ 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080,
+ 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1,
+ 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020,
+ 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18,
+ 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488,
+ 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a,
+ 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0,
+ 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54,
+ 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b,
+ 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad,
+ 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992,
+ 0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd,
+ 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3,
+ 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda,
+ 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8,
+ 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4,
+ 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a,
+ 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697,
+ 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96,
+ 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c,
+ 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7,
+ 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969,
+ 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9,
+ 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9,
+ 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715,
+ 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
+ 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65,
+ 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929,
+ 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d,
+ 0x3a16162c};
+
+ private static final int[] T1 =
+ {
+ 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d,
+ 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203,
+ 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6,
+ 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87,
+ 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, 0xadad41ec,
+ 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7,
+ 0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae,
+ 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f,
+ 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293,
+ 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x0404080c, 0xc7c79552,
+ 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x05050a0f,
+ 0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d,
+ 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b,
+ 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2,
+ 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761,
+ 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397,
+ 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060,
+ 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46,
+ 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8,
+ 0xcfcf854a, 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16,
+ 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf,
+ 0xf9f9e910, 0x02020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844,
+ 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, 0x404080c0,
+ 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104,
+ 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030,
+ 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814,
+ 0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc,
+ 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47,
+ 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0,
+ 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e,
+ 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3,
+ 0x1414283c, 0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76,
+ 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, 0x494992db,
+ 0x06060c0a, 0x2424486c, 0x5c5cb8e4, 0xc2c29f5d, 0xd3d3bd6e,
+ 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337,
+ 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7,
+ 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4,
+ 0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e,
+ 0xaeae47e9, 0x08081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f,
+ 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751,
+ 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd,
+ 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42,
+ 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x03030605, 0xf6f6f701,
+ 0x0e0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0,
+ 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938,
+ 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970,
+ 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592,
+ 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a,
+ 0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da,
+ 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0,
+ 0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6,
+ 0x16162c3a};
+
+ private static final int[] T2 =
+ {
+ 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2,
+ 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301,
+ 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab,
+ 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d,
+ 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, 0xad41ecad,
+ 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4,
+ 0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93,
+ 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc,
+ 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371,
+ 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x04080c04, 0xc79552c7,
+ 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x050a0f05,
+ 0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2,
+ 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09,
+ 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e,
+ 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6,
+ 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784,
+ 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, 0x20406020,
+ 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb,
+ 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858,
+ 0xcf854acf, 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb,
+ 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45,
+ 0xf9e910f9, 0x02040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c,
+ 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, 0x4080c040,
+ 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5,
+ 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010,
+ 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c,
+ 0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44,
+ 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d,
+ 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060,
+ 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a,
+ 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8,
+ 0x14283c14, 0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db,
+ 0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, 0x4992db49,
+ 0x060c0a06, 0x24486c24, 0x5cb8e45c, 0xc29f5dc2, 0xd3bd6ed3,
+ 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4,
+ 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d,
+ 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c,
+ 0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a,
+ 0xae47e9ae, 0x08101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25,
+ 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6,
+ 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b,
+ 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e,
+ 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x03060503, 0xf6f701f6,
+ 0x0e1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9,
+ 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, 0xe1d938e1,
+ 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9,
+ 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287,
+ 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf,
+ 0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf,
+ 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099,
+ 0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb,
+ 0x162c3a16};
+
+ private static final int[] T3 =
+ {
+ 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2,
+ 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101,
+ 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab,
+ 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d,
+ 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, 0x41ecadad,
+ 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4,
+ 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393,
+ 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc,
+ 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171,
+ 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x080c0404, 0x9552c7c7,
+ 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0x0a0f0505,
+ 0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2,
+ 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909,
+ 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e,
+ 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6,
+ 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484,
+ 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, 0x40602020,
+ 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb,
+ 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858,
+ 0x854acfcf, 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb,
+ 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545,
+ 0xe910f9f9, 0x04060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c,
+ 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, 0x80c04040,
+ 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5,
+ 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010,
+ 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c,
+ 0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444,
+ 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d,
+ 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060,
+ 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a,
+ 0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8,
+ 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb,
+ 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949,
+ 0x0c0a0606, 0x486c2424, 0xb8e45c5c, 0x9f5dc2c2, 0xbd6ed3d3,
+ 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4,
+ 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d,
+ 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c,
+ 0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a,
+ 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525,
+ 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6,
+ 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b,
+ 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e,
+ 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x06050303, 0xf701f6f6,
+ 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9,
+ 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, 0xd938e1e1,
+ 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9,
+ 0x07898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787,
+ 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf,
+ 0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf,
+ 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999,
+ 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb,
+ 0x2c3a1616};
+
+ private static final int[] Tinv0 =
+ {
+ 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b,
+ 0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad,
+ 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526,
+ 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d,
+ 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03,
+ 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458,
+ 0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899,
+ 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d,
+ 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1,
+ 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f,
+ 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3,
+ 0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3,
+ 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a,
+ 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506,
+ 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05,
+ 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd,
+ 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491,
+ 0x055dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6,
+ 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7,
+ 0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000,
+ 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd,
+ 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68,
+ 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793, 0xd296eeb4,
+ 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c,
+ 0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0x0b0d090e,
+ 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af,
+ 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644,
+ 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8,
+ 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85,
+ 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc,
+ 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411,
+ 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322,
+ 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6,
+ 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0x0d927850,
+ 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e,
+ 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf,
+ 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd,
+ 0xf418596e, 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa,
+ 0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea,
+ 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235,
+ 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1,
+ 0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43,
+ 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1,
+ 0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb,
+ 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a,
+ 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7,
+ 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418,
+ 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478,
+ 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16,
+ 0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08,
+ 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48,
+ 0x4257b8d0};
+
+ private static final int[] Tinv1 =
+ {
+ 0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb,
+ 0x459d1ff1, 0x58faacab, 0x03e34b93, 0xfa302055, 0x6d76adf6,
+ 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680,
+ 0xa362b58f, 0x5ab1de49, 0x1bba2567, 0x0eea4598, 0xc0fe5de1,
+ 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6, 0x5f8f03e7,
+ 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3,
+ 0x69e04929, 0xc8c98e44, 0x89c2756a, 0x798ef478, 0x3e58996b,
+ 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4,
+ 0x4adf6318, 0x311ae582, 0x33519760, 0x7f536245, 0x7764b1e0,
+ 0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19,
+ 0x6cde9487, 0xf87b52b7, 0xd373ab23, 0x024b72e2, 0x8f1fe357,
+ 0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x0837d3a5,
+ 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b,
+ 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd, 0xbe0506d5,
+ 0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532,
+ 0xebf6a475, 0xec830b39, 0xef6040aa, 0x9f715e06, 0x106ebd51,
+ 0x8a213ef9, 0x06dd963d, 0x053eddae, 0xbde64d46, 0x8d5491b5,
+ 0x5dc47105, 0xd406046f, 0x155060ff, 0xfb981924, 0xe9bdd697,
+ 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738,
+ 0xeec879db, 0x0a7ca147, 0x0f427ce9, 0x1e84f8c9, 0x00000000,
+ 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb,
+ 0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821,
+ 0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f, 0x96eeb4d2,
+ 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16,
+ 0xba93e20a, 0x2aa0c0e5, 0xe0223c43, 0x171b121d, 0x0d090e0b,
+ 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x0775af4c,
+ 0xdd99eebb, 0x607fa3fd, 0x2601f79f, 0xf5725cbc, 0x3b6644c5,
+ 0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863,
+ 0xdc31d7ca, 0x85634210, 0x22971340, 0x11c68420, 0x244a857d,
+ 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3,
+ 0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa,
+ 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, 0x903322ef,
+ 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0x0bd49836, 0x81f5a6cf,
+ 0xde7aa528, 0x8eb7da26, 0xbfad3fa4, 0x9d3a2ce4, 0x9278500d,
+ 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e,
+ 0xafc382f5, 0x805d9fbe, 0x93d0697c, 0x2dd56fa9, 0x1225cfb3,
+ 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09,
+ 0x18596ef4, 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e,
+ 0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x099fead4,
+ 0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0,
+ 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315, 0x9804f14a,
+ 0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d,
+ 0x4daacc54, 0x0496e4df, 0xb5d19ee3, 0x886a4c1b, 0x1f2cc1b8,
+ 0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e,
+ 0x1d67b35a, 0xd2db9252, 0x5610e933, 0x47d66d13, 0x61d79a8c,
+ 0x0ca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735,
+ 0xe51ce1ed, 0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879,
+ 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886,
+ 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672,
+ 0x25e2bc0c, 0x493c288b, 0x950dff41, 0x01a83971, 0xb30c08de,
+ 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874,
+ 0x57b8d042};
+
+ private static final int[] Tinv2 =
+ {
+ 0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b,
+ 0x9d1ff145, 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d,
+ 0xcc889176, 0x02f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044,
+ 0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0,
+ 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9, 0x8f03e75f,
+ 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321,
+ 0xe0492969, 0xc98e44c8, 0xc2756a89, 0x8ef47879, 0x58996b3e,
+ 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a,
+ 0xdf63184a, 0x1ae58231, 0x51976033, 0x5362457f, 0x64b1e077,
+ 0x6bbb84ae, 0x81fe1ca0, 0x08f9942b, 0x48705868, 0x458f19fd,
+ 0xde94876c, 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f,
+ 0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508,
+ 0x2830f287, 0xbf23b2a5, 0x0302ba6a, 0x16ed5c82, 0xcf8a2b1c,
+ 0x79a792b4, 0x07f3f0f2, 0x694ea1e2, 0xda65cdf4, 0x0506d5be,
+ 0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1,
+ 0xf6a475eb, 0x830b39ec, 0x6040aaef, 0x715e069f, 0x6ebd5110,
+ 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d,
+ 0xc471055d, 0x06046fd4, 0x5060ff15, 0x981924fb, 0xbdd697e9,
+ 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b,
+ 0xc879dbee, 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x00000000,
+ 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0x0efdfbff,
+ 0x850f5638, 0xae3d1ed5, 0x2d362739, 0x0f0a64d9, 0x5c6821a6,
+ 0x5b9bd154, 0x36243a2e, 0x0a0cb167, 0x57930fe7, 0xeeb4d296,
+ 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a,
+ 0x93e20aba, 0xa0c0e52a, 0x223c43e0, 0x1b121d17, 0x090e0b0d,
+ 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07,
+ 0x99eebbdd, 0x7fa3fd60, 0x01f79f26, 0x725cbcf5, 0x6644c53b,
+ 0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1,
+ 0x31d7cadc, 0x63421085, 0x97134022, 0xc6842011, 0x4a857d24,
+ 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330,
+ 0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48,
+ 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, 0x3322ef90,
+ 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81,
+ 0x7aa528de, 0xb7da268e, 0xad3fa4bf, 0x3a2ce49d, 0x78500d92,
+ 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7,
+ 0xc382f5af, 0x5d9fbe80, 0xd0697c93, 0xd56fa92d, 0x25cfb312,
+ 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978,
+ 0x596ef418, 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6,
+ 0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409,
+ 0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066,
+ 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8, 0x04f14a98,
+ 0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0,
+ 0xaacc544d, 0x96e4df04, 0xd19ee3b5, 0x6a4c1b88, 0x2cc1b81f,
+ 0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0x0bfb2e41,
+ 0x67b35a1d, 0xdb9252d2, 0x10e93356, 0xd66d1347, 0xd79a8c61,
+ 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9,
+ 0x1ce1ede5, 0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce,
+ 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db,
+ 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3,
+ 0xe2bc0c25, 0x3c288b49, 0x0dff4195, 0xa8397101, 0x0c08deb3,
+ 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c,
+ 0xb8d04257};
+
+ private static final int[] Tinv3 =
+ {
+ 0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab,
+ 0x1ff1459d, 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76,
+ 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435,
+ 0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe,
+ 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3, 0x03e75f8f,
+ 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174,
+ 0x492969e0, 0x8e44c8c9, 0x756a89c2, 0xf478798e, 0x996b3e58,
+ 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace,
+ 0x63184adf, 0xe582311a, 0x97603351, 0x62457f53, 0xb1e07764,
+ 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45,
+ 0x94876cde, 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f,
+ 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837,
+ 0x30f28728, 0x23b2a5bf, 0x02ba6a03, 0xed5c8216, 0x8a2b1ccf,
+ 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da, 0x06d5be05,
+ 0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x0532e18a,
+ 0xa475ebf6, 0x0b39ec83, 0x40aaef60, 0x5e069f71, 0xbd51106e,
+ 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54,
+ 0x71055dc4, 0x046fd406, 0x60ff1550, 0x1924fb98, 0xd697e9bd,
+ 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x07888b89, 0xe7385b19,
+ 0x79dbeec8, 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x00000000,
+ 0x09838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e,
+ 0x0f563885, 0x3d1ed5ae, 0x3627392d, 0x0a64d90f, 0x6821a65c,
+ 0x9bd1545b, 0x243a2e36, 0x0cb1670a, 0x930fe757, 0xb4d296ee,
+ 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12,
+ 0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b, 0x0e0b0d09,
+ 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775,
+ 0xeebbdd99, 0xa3fd607f, 0xf79f2601, 0x5cbcf572, 0x44c53b66,
+ 0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4,
+ 0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6, 0x857d244a,
+ 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2,
+ 0x0dec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894,
+ 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033,
+ 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5,
+ 0xa528de7a, 0xda268eb7, 0x3fa4bfad, 0x2ce49d3a, 0x500d9278,
+ 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739,
+ 0x82f5afc3, 0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225,
+ 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826,
+ 0x6ef41859, 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff,
+ 0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f,
+ 0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2,
+ 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7, 0xf14a9804,
+ 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef,
+ 0xcc544daa, 0xe4df0496, 0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c,
+ 0x467f5165, 0x9d04ea5e, 0x015d358c, 0xfa737487, 0xfb2e410b,
+ 0xb35a1d67, 0x9252d2db, 0xe9335610, 0x6d1347d6, 0x9a8c61d7,
+ 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961,
+ 0xe1ede51c, 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14,
+ 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44,
+ 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d,
+ 0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8, 0x08deb30c,
+ 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c,
+ 0xd04257b8};
+
+ private static int shift(int r, int shift)
+ {
+ return (r >>> shift) | (r << -shift);
+ }
+
+ /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
+
+ private static final int m1 = 0x80808080;
+ private static final int m2 = 0x7f7f7f7f;
+ private static final int m3 = 0x0000001b;
+
+ private static int FFmulX(int x)
+ {
+ return (((x & m2) << 1) ^ (((x & m1) >>> 7) * m3));
+ }
+
+ /*
+ The following defines provide alternative definitions of FFmulX that might
+ give improved performance if a fast 32-bit multiply is not available.
+
+ private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); }
+ private static final int m4 = 0x1b1b1b1b;
+ private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); }
+
+ */
+
+ private static int inv_mcol(int x)
+ {
+ int f2 = FFmulX(x);
+ int f4 = FFmulX(f2);
+ int f8 = FFmulX(f4);
+ int f9 = x ^ f8;
+
+ return f2 ^ f4 ^ f8 ^ shift(f2 ^ f9, 8) ^ shift(f4 ^ f9, 16) ^ shift(f9, 24);
+ }
+
+
+ private static int subWord(int x)
+ {
+ return (S[x&255]&255 | ((S[(x>>8)&255]&255)<<8) | ((S[(x>>16)&255]&255)<<16) | S[(x>>24)&255]<<24);
+ }
+
+ /**
+ * Calculate the necessary round keys
+ * The number of calculations depends on key size and block size
+ * AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits
+ * This code is written assuming those are the only possible values
+ */
+ private int[][] generateWorkingKey(
+ byte[] key,
+ boolean forEncryption)
+ {
+ int KC = key.length / 4; // key length in words
+ int t;
+
+ if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.length))
+ {
+ throw new IllegalArgumentException("Key length not 128/192/256 bits.");
+ }
+
+ ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes
+ int[][] W = new int[ROUNDS+1][4]; // 4 words in a block
+
+ //
+ // copy the key into the round key array
+ //
+
+ t = 0;
+ int i = 0;
+ while (i < key.length)
+ {
+ W[t >> 2][t & 3] = (key[i]&0xff) | ((key[i+1]&0xff) << 8) | ((key[i+2]&0xff) << 16) | (key[i+3] << 24);
+ i+=4;
+ t++;
+ }
+
+ //
+ // while not enough round key material calculated
+ // calculate new values
+ //
+ int k = (ROUNDS + 1) << 2;
+ for (i = KC; (i < k); i++)
+ {
+ int temp = W[(i - 1) >> 2][(i - 1) & 3];
+ if ((i % KC) == 0)
+ {
+ temp = subWord(shift(temp, 8)) ^ rcon[(i / KC) - 1];
+ }
+ else if ((KC > 6) && ((i % KC) == 4))
+ {
+ temp = subWord(temp);
+ }
+
+ W[i >> 2][i & 3] = W[(i - KC) >> 2][(i - KC) & 3] ^ temp;
+ }
+
+ if (!forEncryption)
+ {
+ for (int j = 1; j < ROUNDS; j++)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ W[j][i] = inv_mcol(W[j][i]);
+ }
+ }
+ }
+
+ return W;
+ }
+
+ private int ROUNDS;
+ private int[][] WorkingKey = null;
+ private int C0, C1, C2, C3;
+ private boolean forEncryption;
+
+ private static final int BLOCK_SIZE = 16;
+
+ /**
+ * default constructor - 128 bit block size.
+ */
+ public AESFastEngine()
+ {
+ }
+
+ /**
+ * initialise an AES cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ WorkingKey = generateWorkingKey(((KeyParameter)params).getKey(), forEncryption);
+ this.forEncryption = forEncryption;
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to AES init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "AES";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (WorkingKey == null)
+ {
+ throw new IllegalStateException("AES engine not initialised");
+ }
+
+ if ((inOff + (32 / 2)) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + (32 / 2)) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (forEncryption)
+ {
+ unpackBlock(in, inOff);
+ encryptBlock(WorkingKey);
+ packBlock(out, outOff);
+ }
+ else
+ {
+ unpackBlock(in, inOff);
+ decryptBlock(WorkingKey);
+ packBlock(out, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+
+ private void unpackBlock(
+ byte[] bytes,
+ int off)
+ {
+ int index = off;
+
+ C0 = (bytes[index++] & 0xff);
+ C0 |= (bytes[index++] & 0xff) << 8;
+ C0 |= (bytes[index++] & 0xff) << 16;
+ C0 |= bytes[index++] << 24;
+
+ C1 = (bytes[index++] & 0xff);
+ C1 |= (bytes[index++] & 0xff) << 8;
+ C1 |= (bytes[index++] & 0xff) << 16;
+ C1 |= bytes[index++] << 24;
+
+ C2 = (bytes[index++] & 0xff);
+ C2 |= (bytes[index++] & 0xff) << 8;
+ C2 |= (bytes[index++] & 0xff) << 16;
+ C2 |= bytes[index++] << 24;
+
+ C3 = (bytes[index++] & 0xff);
+ C3 |= (bytes[index++] & 0xff) << 8;
+ C3 |= (bytes[index++] & 0xff) << 16;
+ C3 |= bytes[index++] << 24;
+ }
+
+ private void packBlock(
+ byte[] bytes,
+ int off)
+ {
+ int index = off;
+
+ bytes[index++] = (byte)C0;
+ bytes[index++] = (byte)(C0 >> 8);
+ bytes[index++] = (byte)(C0 >> 16);
+ bytes[index++] = (byte)(C0 >> 24);
+
+ bytes[index++] = (byte)C1;
+ bytes[index++] = (byte)(C1 >> 8);
+ bytes[index++] = (byte)(C1 >> 16);
+ bytes[index++] = (byte)(C1 >> 24);
+
+ bytes[index++] = (byte)C2;
+ bytes[index++] = (byte)(C2 >> 8);
+ bytes[index++] = (byte)(C2 >> 16);
+ bytes[index++] = (byte)(C2 >> 24);
+
+ bytes[index++] = (byte)C3;
+ bytes[index++] = (byte)(C3 >> 8);
+ bytes[index++] = (byte)(C3 >> 16);
+ bytes[index++] = (byte)(C3 >> 24);
+ }
+
+ private void encryptBlock(int[][] KW)
+ {
+ int r, r0, r1, r2, r3;
+
+ C0 ^= KW[0][0];
+ C1 ^= KW[0][1];
+ C2 ^= KW[0][2];
+ C3 ^= KW[0][3];
+
+ r = 1;
+ while (r < ROUNDS - 1)
+ {
+ r0 = T0[C0&255] ^ T1[(C1>>8)&255] ^ T2[(C2>>16)&255] ^ T3[(C3>>24)&255] ^ KW[r][0];
+ r1 = T0[C1&255] ^ T1[(C2>>8)&255] ^ T2[(C3>>16)&255] ^ T3[(C0>>24)&255] ^ KW[r][1];
+ r2 = T0[C2&255] ^ T1[(C3>>8)&255] ^ T2[(C0>>16)&255] ^ T3[(C1>>24)&255] ^ KW[r][2];
+ r3 = T0[C3&255] ^ T1[(C0>>8)&255] ^ T2[(C1>>16)&255] ^ T3[(C2>>24)&255] ^ KW[r++][3];
+ C0 = T0[r0&255] ^ T1[(r1>>8)&255] ^ T2[(r2>>16)&255] ^ T3[(r3>>24)&255] ^ KW[r][0];
+ C1 = T0[r1&255] ^ T1[(r2>>8)&255] ^ T2[(r3>>16)&255] ^ T3[(r0>>24)&255] ^ KW[r][1];
+ C2 = T0[r2&255] ^ T1[(r3>>8)&255] ^ T2[(r0>>16)&255] ^ T3[(r1>>24)&255] ^ KW[r][2];
+ C3 = T0[r3&255] ^ T1[(r0>>8)&255] ^ T2[(r1>>16)&255] ^ T3[(r2>>24)&255] ^ KW[r++][3];
+ }
+
+ r0 = T0[C0&255] ^ T1[(C1>>8)&255] ^ T2[(C2>>16)&255] ^ T3[(C3>>24)&255] ^ KW[r][0];
+ r1 = T0[C1&255] ^ T1[(C2>>8)&255] ^ T2[(C3>>16)&255] ^ T3[(C0>>24)&255] ^ KW[r][1];
+ r2 = T0[C2&255] ^ T1[(C3>>8)&255] ^ T2[(C0>>16)&255] ^ T3[(C1>>24)&255] ^ KW[r][2];
+ r3 = T0[C3&255] ^ T1[(C0>>8)&255] ^ T2[(C1>>16)&255] ^ T3[(C2>>24)&255] ^ KW[r++][3];
+
+ // the final round's table is a simple function of S so we don't use a whole other four tables for it
+
+ C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24) ^ KW[r][0];
+ C1 = (S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24) ^ KW[r][1];
+ C2 = (S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r][2];
+ C3 = (S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r][3];
+
+ }
+
+ private void decryptBlock(int[][] KW)
+ {
+ int r0, r1, r2, r3;
+
+ C0 ^= KW[ROUNDS][0];
+ C1 ^= KW[ROUNDS][1];
+ C2 ^= KW[ROUNDS][2];
+ C3 ^= KW[ROUNDS][3];
+
+ int r = ROUNDS-1;
+
+ while (r>1)
+ {
+ r0 = Tinv0[C0&255] ^ Tinv1[(C3>>8)&255] ^ Tinv2[(C2>>16)&255] ^ Tinv3[(C1>>24)&255] ^ KW[r][0];
+ r1 = Tinv0[C1&255] ^ Tinv1[(C0>>8)&255] ^ Tinv2[(C3>>16)&255] ^ Tinv3[(C2>>24)&255] ^ KW[r][1];
+ r2 = Tinv0[C2&255] ^ Tinv1[(C1>>8)&255] ^ Tinv2[(C0>>16)&255] ^ Tinv3[(C3>>24)&255] ^ KW[r][2];
+ r3 = Tinv0[C3&255] ^ Tinv1[(C2>>8)&255] ^ Tinv2[(C1>>16)&255] ^ Tinv3[(C0>>24)&255] ^ KW[r--][3];
+ C0 = Tinv0[r0&255] ^ Tinv1[(r3>>8)&255] ^ Tinv2[(r2>>16)&255] ^ Tinv3[(r1>>24)&255] ^ KW[r][0];
+ C1 = Tinv0[r1&255] ^ Tinv1[(r0>>8)&255] ^ Tinv2[(r3>>16)&255] ^ Tinv3[(r2>>24)&255] ^ KW[r][1];
+ C2 = Tinv0[r2&255] ^ Tinv1[(r1>>8)&255] ^ Tinv2[(r0>>16)&255] ^ Tinv3[(r3>>24)&255] ^ KW[r][2];
+ C3 = Tinv0[r3&255] ^ Tinv1[(r2>>8)&255] ^ Tinv2[(r1>>16)&255] ^ Tinv3[(r0>>24)&255] ^ KW[r--][3];
+ }
+
+ r0 = Tinv0[C0&255] ^ Tinv1[(C3>>8)&255] ^ Tinv2[(C2>>16)&255] ^ Tinv3[(C1>>24)&255] ^ KW[r][0];
+ r1 = Tinv0[C1&255] ^ Tinv1[(C0>>8)&255] ^ Tinv2[(C3>>16)&255] ^ Tinv3[(C2>>24)&255] ^ KW[r][1];
+ r2 = Tinv0[C2&255] ^ Tinv1[(C1>>8)&255] ^ Tinv2[(C0>>16)&255] ^ Tinv3[(C3>>24)&255] ^ KW[r][2];
+ r3 = Tinv0[C3&255] ^ Tinv1[(C2>>8)&255] ^ Tinv2[(C1>>16)&255] ^ Tinv3[(C0>>24)&255] ^ KW[r][3];
+
+ // the final round's table is a simple function of Si so we don't use a whole other four tables for it
+
+ C0 = (Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0][0];
+ C1 = (Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24) ^ KW[0][1];
+ C2 = (Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24) ^ KW[0][2];
+ C3 = (Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24) ^ KW[0][3];
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AESLightEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AESLightEngine.java
new file mode 100644
index 00000000..df8444b9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/AESLightEngine.java
@@ -0,0 +1,439 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * an implementation of the AES (Rijndael), from FIPS-197.
+ * <p>
+ * For further details see: <a href="http://csrc.nist.gov/encryption/aes/">http://csrc.nist.gov/encryption/aes/</a>.
+ *
+ * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at
+ * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
+ *
+ * There are three levels of tradeoff of speed vs memory
+ * Because java has no preprocessor, they are written as three separate classes from which to choose
+ *
+ * The fastest uses 8Kbytes of static tables to precompute round calculations, 4 256 word tables for encryption
+ * and 4 for decryption.
+ *
+ * The middle performance version uses only one 256 word table for each, for a total of 2Kbytes,
+ * adding 12 rotate operations per round to compute the values contained in the other tables from
+ * the contents of the first
+ *
+ * The slowest version uses no static tables at all and computes the values
+ * in each round.
+ * <p>
+ * This file contains the slowest performance version with no static tables
+ * for round precomputation, but it has the smallest foot print.
+ *
+ */
+public class AESLightEngine
+ implements BlockCipher
+{
+ // The S box
+ private static final byte[] S = {
+ (byte)99, (byte)124, (byte)119, (byte)123, (byte)242, (byte)107, (byte)111, (byte)197,
+ (byte)48, (byte)1, (byte)103, (byte)43, (byte)254, (byte)215, (byte)171, (byte)118,
+ (byte)202, (byte)130, (byte)201, (byte)125, (byte)250, (byte)89, (byte)71, (byte)240,
+ (byte)173, (byte)212, (byte)162, (byte)175, (byte)156, (byte)164, (byte)114, (byte)192,
+ (byte)183, (byte)253, (byte)147, (byte)38, (byte)54, (byte)63, (byte)247, (byte)204,
+ (byte)52, (byte)165, (byte)229, (byte)241, (byte)113, (byte)216, (byte)49, (byte)21,
+ (byte)4, (byte)199, (byte)35, (byte)195, (byte)24, (byte)150, (byte)5, (byte)154,
+ (byte)7, (byte)18, (byte)128, (byte)226, (byte)235, (byte)39, (byte)178, (byte)117,
+ (byte)9, (byte)131, (byte)44, (byte)26, (byte)27, (byte)110, (byte)90, (byte)160,
+ (byte)82, (byte)59, (byte)214, (byte)179, (byte)41, (byte)227, (byte)47, (byte)132,
+ (byte)83, (byte)209, (byte)0, (byte)237, (byte)32, (byte)252, (byte)177, (byte)91,
+ (byte)106, (byte)203, (byte)190, (byte)57, (byte)74, (byte)76, (byte)88, (byte)207,
+ (byte)208, (byte)239, (byte)170, (byte)251, (byte)67, (byte)77, (byte)51, (byte)133,
+ (byte)69, (byte)249, (byte)2, (byte)127, (byte)80, (byte)60, (byte)159, (byte)168,
+ (byte)81, (byte)163, (byte)64, (byte)143, (byte)146, (byte)157, (byte)56, (byte)245,
+ (byte)188, (byte)182, (byte)218, (byte)33, (byte)16, (byte)255, (byte)243, (byte)210,
+ (byte)205, (byte)12, (byte)19, (byte)236, (byte)95, (byte)151, (byte)68, (byte)23,
+ (byte)196, (byte)167, (byte)126, (byte)61, (byte)100, (byte)93, (byte)25, (byte)115,
+ (byte)96, (byte)129, (byte)79, (byte)220, (byte)34, (byte)42, (byte)144, (byte)136,
+ (byte)70, (byte)238, (byte)184, (byte)20, (byte)222, (byte)94, (byte)11, (byte)219,
+ (byte)224, (byte)50, (byte)58, (byte)10, (byte)73, (byte)6, (byte)36, (byte)92,
+ (byte)194, (byte)211, (byte)172, (byte)98, (byte)145, (byte)149, (byte)228, (byte)121,
+ (byte)231, (byte)200, (byte)55, (byte)109, (byte)141, (byte)213, (byte)78, (byte)169,
+ (byte)108, (byte)86, (byte)244, (byte)234, (byte)101, (byte)122, (byte)174, (byte)8,
+ (byte)186, (byte)120, (byte)37, (byte)46, (byte)28, (byte)166, (byte)180, (byte)198,
+ (byte)232, (byte)221, (byte)116, (byte)31, (byte)75, (byte)189, (byte)139, (byte)138,
+ (byte)112, (byte)62, (byte)181, (byte)102, (byte)72, (byte)3, (byte)246, (byte)14,
+ (byte)97, (byte)53, (byte)87, (byte)185, (byte)134, (byte)193, (byte)29, (byte)158,
+ (byte)225, (byte)248, (byte)152, (byte)17, (byte)105, (byte)217, (byte)142, (byte)148,
+ (byte)155, (byte)30, (byte)135, (byte)233, (byte)206, (byte)85, (byte)40, (byte)223,
+ (byte)140, (byte)161, (byte)137, (byte)13, (byte)191, (byte)230, (byte)66, (byte)104,
+ (byte)65, (byte)153, (byte)45, (byte)15, (byte)176, (byte)84, (byte)187, (byte)22,
+ };
+
+ // The inverse S-box
+ private static final byte[] Si = {
+ (byte)82, (byte)9, (byte)106, (byte)213, (byte)48, (byte)54, (byte)165, (byte)56,
+ (byte)191, (byte)64, (byte)163, (byte)158, (byte)129, (byte)243, (byte)215, (byte)251,
+ (byte)124, (byte)227, (byte)57, (byte)130, (byte)155, (byte)47, (byte)255, (byte)135,
+ (byte)52, (byte)142, (byte)67, (byte)68, (byte)196, (byte)222, (byte)233, (byte)203,
+ (byte)84, (byte)123, (byte)148, (byte)50, (byte)166, (byte)194, (byte)35, (byte)61,
+ (byte)238, (byte)76, (byte)149, (byte)11, (byte)66, (byte)250, (byte)195, (byte)78,
+ (byte)8, (byte)46, (byte)161, (byte)102, (byte)40, (byte)217, (byte)36, (byte)178,
+ (byte)118, (byte)91, (byte)162, (byte)73, (byte)109, (byte)139, (byte)209, (byte)37,
+ (byte)114, (byte)248, (byte)246, (byte)100, (byte)134, (byte)104, (byte)152, (byte)22,
+ (byte)212, (byte)164, (byte)92, (byte)204, (byte)93, (byte)101, (byte)182, (byte)146,
+ (byte)108, (byte)112, (byte)72, (byte)80, (byte)253, (byte)237, (byte)185, (byte)218,
+ (byte)94, (byte)21, (byte)70, (byte)87, (byte)167, (byte)141, (byte)157, (byte)132,
+ (byte)144, (byte)216, (byte)171, (byte)0, (byte)140, (byte)188, (byte)211, (byte)10,
+ (byte)247, (byte)228, (byte)88, (byte)5, (byte)184, (byte)179, (byte)69, (byte)6,
+ (byte)208, (byte)44, (byte)30, (byte)143, (byte)202, (byte)63, (byte)15, (byte)2,
+ (byte)193, (byte)175, (byte)189, (byte)3, (byte)1, (byte)19, (byte)138, (byte)107,
+ (byte)58, (byte)145, (byte)17, (byte)65, (byte)79, (byte)103, (byte)220, (byte)234,
+ (byte)151, (byte)242, (byte)207, (byte)206, (byte)240, (byte)180, (byte)230, (byte)115,
+ (byte)150, (byte)172, (byte)116, (byte)34, (byte)231, (byte)173, (byte)53, (byte)133,
+ (byte)226, (byte)249, (byte)55, (byte)232, (byte)28, (byte)117, (byte)223, (byte)110,
+ (byte)71, (byte)241, (byte)26, (byte)113, (byte)29, (byte)41, (byte)197, (byte)137,
+ (byte)111, (byte)183, (byte)98, (byte)14, (byte)170, (byte)24, (byte)190, (byte)27,
+ (byte)252, (byte)86, (byte)62, (byte)75, (byte)198, (byte)210, (byte)121, (byte)32,
+ (byte)154, (byte)219, (byte)192, (byte)254, (byte)120, (byte)205, (byte)90, (byte)244,
+ (byte)31, (byte)221, (byte)168, (byte)51, (byte)136, (byte)7, (byte)199, (byte)49,
+ (byte)177, (byte)18, (byte)16, (byte)89, (byte)39, (byte)128, (byte)236, (byte)95,
+ (byte)96, (byte)81, (byte)127, (byte)169, (byte)25, (byte)181, (byte)74, (byte)13,
+ (byte)45, (byte)229, (byte)122, (byte)159, (byte)147, (byte)201, (byte)156, (byte)239,
+ (byte)160, (byte)224, (byte)59, (byte)77, (byte)174, (byte)42, (byte)245, (byte)176,
+ (byte)200, (byte)235, (byte)187, (byte)60, (byte)131, (byte)83, (byte)153, (byte)97,
+ (byte)23, (byte)43, (byte)4, (byte)126, (byte)186, (byte)119, (byte)214, (byte)38,
+ (byte)225, (byte)105, (byte)20, (byte)99, (byte)85, (byte)33, (byte)12, (byte)125,
+ };
+
+ // vector used in calculating key schedule (powers of x in GF(256))
+ private static final int[] rcon = {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
+ 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 };
+
+ private static int shift(int r, int shift)
+ {
+ return (r >>> shift) | (r << -shift);
+ }
+
+ /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
+
+ private static final int m1 = 0x80808080;
+ private static final int m2 = 0x7f7f7f7f;
+ private static final int m3 = 0x0000001b;
+
+ private static int FFmulX(int x)
+ {
+ return (((x & m2) << 1) ^ (((x & m1) >>> 7) * m3));
+ }
+
+ /*
+ The following defines provide alternative definitions of FFmulX that might
+ give improved performance if a fast 32-bit multiply is not available.
+
+ private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); }
+ private static final int m4 = 0x1b1b1b1b;
+ private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); }
+
+ */
+
+ private static int mcol(int x)
+ {
+ int f2 = FFmulX(x);
+ return f2 ^ shift(x ^ f2, 8) ^ shift(x, 16) ^ shift(x, 24);
+ }
+
+ private static int inv_mcol(int x)
+ {
+ int f2 = FFmulX(x);
+ int f4 = FFmulX(f2);
+ int f8 = FFmulX(f4);
+ int f9 = x ^ f8;
+
+ return f2 ^ f4 ^ f8 ^ shift(f2 ^ f9, 8) ^ shift(f4 ^ f9, 16) ^ shift(f9, 24);
+ }
+
+
+ private static int subWord(int x)
+ {
+ return (S[x&255]&255 | ((S[(x>>8)&255]&255)<<8) | ((S[(x>>16)&255]&255)<<16) | S[(x>>24)&255]<<24);
+ }
+
+ /**
+ * Calculate the necessary round keys
+ * The number of calculations depends on key size and block size
+ * AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits
+ * This code is written assuming those are the only possible values
+ */
+ private int[][] generateWorkingKey(
+ byte[] key,
+ boolean forEncryption)
+ {
+ int KC = key.length / 4; // key length in words
+ int t;
+
+ if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.length))
+ {
+ throw new IllegalArgumentException("Key length not 128/192/256 bits.");
+ }
+
+ ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes
+ int[][] W = new int[ROUNDS+1][4]; // 4 words in a block
+
+ //
+ // copy the key into the round key array
+ //
+
+ t = 0;
+ int i = 0;
+ while (i < key.length)
+ {
+ W[t >> 2][t & 3] = (key[i]&0xff) | ((key[i+1]&0xff) << 8) | ((key[i+2]&0xff) << 16) | (key[i+3] << 24);
+ i+=4;
+ t++;
+ }
+
+ //
+ // while not enough round key material calculated
+ // calculate new values
+ //
+ int k = (ROUNDS + 1) << 2;
+ for (i = KC; (i < k); i++)
+ {
+ int temp = W[(i-1)>>2][(i-1)&3];
+ if ((i % KC) == 0)
+ {
+ temp = subWord(shift(temp, 8)) ^ rcon[(i / KC)-1];
+ }
+ else if ((KC > 6) && ((i % KC) == 4))
+ {
+ temp = subWord(temp);
+ }
+
+ W[i>>2][i&3] = W[(i - KC)>>2][(i-KC)&3] ^ temp;
+ }
+
+ if (!forEncryption)
+ {
+ for (int j = 1; j < ROUNDS; j++)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ W[j][i] = inv_mcol(W[j][i]);
+ }
+ }
+ }
+
+ return W;
+ }
+
+ private int ROUNDS;
+ private int[][] WorkingKey = null;
+ private int C0, C1, C2, C3;
+ private boolean forEncryption;
+
+ private static final int BLOCK_SIZE = 16;
+
+ /**
+ * default constructor - 128 bit block size.
+ */
+ public AESLightEngine()
+ {
+ }
+
+ /**
+ * initialise an AES cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ WorkingKey = generateWorkingKey(((KeyParameter)params).getKey(), forEncryption);
+ this.forEncryption = forEncryption;
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to AES init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "AES";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (WorkingKey == null)
+ {
+ throw new IllegalStateException("AES engine not initialised");
+ }
+
+ if ((inOff + (32 / 2)) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + (32 / 2)) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (forEncryption)
+ {
+ unpackBlock(in, inOff);
+ encryptBlock(WorkingKey);
+ packBlock(out, outOff);
+ }
+ else
+ {
+ unpackBlock(in, inOff);
+ decryptBlock(WorkingKey);
+ packBlock(out, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+
+ private void unpackBlock(
+ byte[] bytes,
+ int off)
+ {
+ int index = off;
+
+ C0 = (bytes[index++] & 0xff);
+ C0 |= (bytes[index++] & 0xff) << 8;
+ C0 |= (bytes[index++] & 0xff) << 16;
+ C0 |= bytes[index++] << 24;
+
+ C1 = (bytes[index++] & 0xff);
+ C1 |= (bytes[index++] & 0xff) << 8;
+ C1 |= (bytes[index++] & 0xff) << 16;
+ C1 |= bytes[index++] << 24;
+
+ C2 = (bytes[index++] & 0xff);
+ C2 |= (bytes[index++] & 0xff) << 8;
+ C2 |= (bytes[index++] & 0xff) << 16;
+ C2 |= bytes[index++] << 24;
+
+ C3 = (bytes[index++] & 0xff);
+ C3 |= (bytes[index++] & 0xff) << 8;
+ C3 |= (bytes[index++] & 0xff) << 16;
+ C3 |= bytes[index++] << 24;
+ }
+
+ private void packBlock(
+ byte[] bytes,
+ int off)
+ {
+ int index = off;
+
+ bytes[index++] = (byte)C0;
+ bytes[index++] = (byte)(C0 >> 8);
+ bytes[index++] = (byte)(C0 >> 16);
+ bytes[index++] = (byte)(C0 >> 24);
+
+ bytes[index++] = (byte)C1;
+ bytes[index++] = (byte)(C1 >> 8);
+ bytes[index++] = (byte)(C1 >> 16);
+ bytes[index++] = (byte)(C1 >> 24);
+
+ bytes[index++] = (byte)C2;
+ bytes[index++] = (byte)(C2 >> 8);
+ bytes[index++] = (byte)(C2 >> 16);
+ bytes[index++] = (byte)(C2 >> 24);
+
+ bytes[index++] = (byte)C3;
+ bytes[index++] = (byte)(C3 >> 8);
+ bytes[index++] = (byte)(C3 >> 16);
+ bytes[index++] = (byte)(C3 >> 24);
+ }
+
+ private void encryptBlock(int[][] KW)
+ {
+ int r, r0, r1, r2, r3;
+
+ C0 ^= KW[0][0];
+ C1 ^= KW[0][1];
+ C2 ^= KW[0][2];
+ C3 ^= KW[0][3];
+
+ for (r = 1; r < ROUNDS - 1;)
+ {
+ r0 = mcol((S[C0&255]&255) ^ ((S[(C1>>8)&255]&255)<<8) ^ ((S[(C2>>16)&255]&255)<<16) ^ (S[(C3>>24)&255]<<24)) ^ KW[r][0];
+ r1 = mcol((S[C1&255]&255) ^ ((S[(C2>>8)&255]&255)<<8) ^ ((S[(C3>>16)&255]&255)<<16) ^ (S[(C0>>24)&255]<<24)) ^ KW[r][1];
+ r2 = mcol((S[C2&255]&255) ^ ((S[(C3>>8)&255]&255)<<8) ^ ((S[(C0>>16)&255]&255)<<16) ^ (S[(C1>>24)&255]<<24)) ^ KW[r][2];
+ r3 = mcol((S[C3&255]&255) ^ ((S[(C0>>8)&255]&255)<<8) ^ ((S[(C1>>16)&255]&255)<<16) ^ (S[(C2>>24)&255]<<24)) ^ KW[r++][3];
+ C0 = mcol((S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24)) ^ KW[r][0];
+ C1 = mcol((S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24)) ^ KW[r][1];
+ C2 = mcol((S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24)) ^ KW[r][2];
+ C3 = mcol((S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24)) ^ KW[r++][3];
+ }
+
+ r0 = mcol((S[C0&255]&255) ^ ((S[(C1>>8)&255]&255)<<8) ^ ((S[(C2>>16)&255]&255)<<16) ^ (S[(C3>>24)&255]<<24)) ^ KW[r][0];
+ r1 = mcol((S[C1&255]&255) ^ ((S[(C2>>8)&255]&255)<<8) ^ ((S[(C3>>16)&255]&255)<<16) ^ (S[(C0>>24)&255]<<24)) ^ KW[r][1];
+ r2 = mcol((S[C2&255]&255) ^ ((S[(C3>>8)&255]&255)<<8) ^ ((S[(C0>>16)&255]&255)<<16) ^ (S[(C1>>24)&255]<<24)) ^ KW[r][2];
+ r3 = mcol((S[C3&255]&255) ^ ((S[(C0>>8)&255]&255)<<8) ^ ((S[(C1>>16)&255]&255)<<16) ^ (S[(C2>>24)&255]<<24)) ^ KW[r++][3];
+
+ // the final round is a simple function of S
+
+ C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24) ^ KW[r][0];
+ C1 = (S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24) ^ KW[r][1];
+ C2 = (S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r][2];
+ C3 = (S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r][3];
+
+ }
+
+ private void decryptBlock(int[][] KW)
+ {
+ int r, r0, r1, r2, r3;
+
+ C0 ^= KW[ROUNDS][0];
+ C1 ^= KW[ROUNDS][1];
+ C2 ^= KW[ROUNDS][2];
+ C3 ^= KW[ROUNDS][3];
+
+ for (r = ROUNDS-1; r>1;)
+ {
+ r0 = inv_mcol((Si[C0&255]&255) ^ ((Si[(C3>>8)&255]&255)<<8) ^ ((Si[(C2>>16)&255]&255)<<16) ^ (Si[(C1>>24)&255]<<24)) ^ KW[r][0];
+ r1 = inv_mcol((Si[C1&255]&255) ^ ((Si[(C0>>8)&255]&255)<<8) ^ ((Si[(C3>>16)&255]&255)<<16) ^ (Si[(C2>>24)&255]<<24)) ^ KW[r][1];
+ r2 = inv_mcol((Si[C2&255]&255) ^ ((Si[(C1>>8)&255]&255)<<8) ^ ((Si[(C0>>16)&255]&255)<<16) ^ (Si[(C3>>24)&255]<<24)) ^ KW[r][2];
+ r3 = inv_mcol((Si[C3&255]&255) ^ ((Si[(C2>>8)&255]&255)<<8) ^ ((Si[(C1>>16)&255]&255)<<16) ^ (Si[(C0>>24)&255]<<24)) ^ KW[r--][3];
+ C0 = inv_mcol((Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24)) ^ KW[r][0];
+ C1 = inv_mcol((Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24)) ^ KW[r][1];
+ C2 = inv_mcol((Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24)) ^ KW[r][2];
+ C3 = inv_mcol((Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24)) ^ KW[r--][3];
+ }
+
+ r0 = inv_mcol((Si[C0&255]&255) ^ ((Si[(C3>>8)&255]&255)<<8) ^ ((Si[(C2>>16)&255]&255)<<16) ^ (Si[(C1>>24)&255]<<24)) ^ KW[r][0];
+ r1 = inv_mcol((Si[C1&255]&255) ^ ((Si[(C0>>8)&255]&255)<<8) ^ ((Si[(C3>>16)&255]&255)<<16) ^ (Si[(C2>>24)&255]<<24)) ^ KW[r][1];
+ r2 = inv_mcol((Si[C2&255]&255) ^ ((Si[(C1>>8)&255]&255)<<8) ^ ((Si[(C0>>16)&255]&255)<<16) ^ (Si[(C3>>24)&255]<<24)) ^ KW[r][2];
+ r3 = inv_mcol((Si[C3&255]&255) ^ ((Si[(C2>>8)&255]&255)<<8) ^ ((Si[(C1>>16)&255]&255)<<16) ^ (Si[(C0>>24)&255]<<24)) ^ KW[r][3];
+
+ // the final round's table is a simple function of Si
+
+ C0 = (Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0][0];
+ C1 = (Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24) ^ KW[0][1];
+ C2 = (Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24) ^ KW[0][2];
+ C3 = (Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24) ^ KW[0][3];
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AESWrapEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AESWrapEngine.java
new file mode 100644
index 00000000..5d316ac4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/AESWrapEngine.java
@@ -0,0 +1,16 @@
+package org.bouncycastle.crypto.engines;
+
+/**
+ * an implementation of the AES Key Wrapper from the NIST Key Wrap
+ * Specification.
+ * <p>
+ * For further details see: <a href="http://csrc.nist.gov/encryption/kms/key-wrap.pdf">http://csrc.nist.gov/encryption/kms/key-wrap.pdf</a>.
+ */
+public class AESWrapEngine
+ extends RFC3394WrapEngine
+{
+ public AESWrapEngine()
+ {
+ super(new AESEngine());
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/BlowfishEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/BlowfishEngine.java
new file mode 100644
index 00000000..cfe7f1ff
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/BlowfishEngine.java
@@ -0,0 +1,577 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * A class that provides Blowfish key encryption operations,
+ * such as encoding data and generating keys.
+ * All the algorithms herein are from Applied Cryptography
+ * and implement a simplified cryptography interface.
+ */
+public final class BlowfishEngine
+implements BlockCipher
+{
+ private final static int[]
+ KP = {
+ 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344,
+ 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89,
+ 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C,
+ 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917,
+ 0x9216D5D9, 0x8979FB1B
+ },
+
+ KS0 = {
+ 0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7,
+ 0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99,
+ 0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16,
+ 0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E,
+ 0x0D95748F, 0x728EB658, 0x718BCD58, 0x82154AEE,
+ 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013,
+ 0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF,
+ 0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E,
+ 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60,
+ 0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440,
+ 0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE,
+ 0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A,
+ 0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E,
+ 0xAFD6BA33, 0x6C24CF5C, 0x7A325381, 0x28958677,
+ 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193,
+ 0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032,
+ 0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88,
+ 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239,
+ 0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E,
+ 0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0,
+ 0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3,
+ 0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98,
+ 0xA1F1651D, 0x39AF0176, 0x66CA593E, 0x82430E88,
+ 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE,
+ 0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6,
+ 0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D,
+ 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B,
+ 0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7,
+ 0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA,
+ 0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463,
+ 0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F,
+ 0x6DFC511F, 0x9B30952C, 0xCC814544, 0xAF5EBD09,
+ 0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3,
+ 0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB,
+ 0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279,
+ 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8,
+ 0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB,
+ 0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82,
+ 0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB,
+ 0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573,
+ 0x695B27B0, 0xBBCA58C8, 0xE1FFA35D, 0xB8F011A0,
+ 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B,
+ 0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790,
+ 0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8,
+ 0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4,
+ 0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0,
+ 0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7,
+ 0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C,
+ 0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD,
+ 0x2F2F2218, 0xBE0E1777, 0xEA752DFE, 0x8B021FA1,
+ 0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299,
+ 0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9,
+ 0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477,
+ 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF,
+ 0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49,
+ 0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF,
+ 0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA,
+ 0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5,
+ 0x83260376, 0x6295CFA9, 0x11C81968, 0x4E734A41,
+ 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915,
+ 0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400,
+ 0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915,
+ 0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664,
+ 0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A
+ },
+
+ KS1 = {
+ 0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623,
+ 0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266,
+ 0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1,
+ 0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E,
+ 0x3F54989A, 0x5B429D65, 0x6B8FE4D6, 0x99F73FD6,
+ 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1,
+ 0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E,
+ 0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1,
+ 0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737,
+ 0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8,
+ 0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF,
+ 0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD,
+ 0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701,
+ 0x3AE5E581, 0x37C2DADC, 0xC8B57634, 0x9AF3DDA7,
+ 0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41,
+ 0xE238CD99, 0x3BEA0E2F, 0x3280BBA1, 0x183EB331,
+ 0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF,
+ 0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF,
+ 0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E,
+ 0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87,
+ 0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C,
+ 0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2,
+ 0xEF1C1847, 0x3215D908, 0xDD433B37, 0x24C2BA16,
+ 0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD,
+ 0x71DFF89E, 0x10314E55, 0x81AC77D6, 0x5F11199B,
+ 0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509,
+ 0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E,
+ 0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3,
+ 0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F,
+ 0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A,
+ 0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4,
+ 0xF2F74EA7, 0x361D2B3D, 0x1939260F, 0x19C27960,
+ 0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66,
+ 0xE3BC4595, 0xA67BC883, 0xB17F37D1, 0x018CFF28,
+ 0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802,
+ 0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84,
+ 0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510,
+ 0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF,
+ 0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14,
+ 0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E,
+ 0x648B1EAF, 0x19BDF0CA, 0xA02369B9, 0x655ABB50,
+ 0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7,
+ 0x9B540B19, 0x875FA099, 0x95F7997E, 0x623D7DA8,
+ 0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281,
+ 0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99,
+ 0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696,
+ 0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128,
+ 0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73,
+ 0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0,
+ 0x45EEE2B6, 0xA3AAABEA, 0xDB6C4F15, 0xFACB4FD0,
+ 0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105,
+ 0xD81E799E, 0x86854DC7, 0xE44B476A, 0x3D816250,
+ 0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3,
+ 0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285,
+ 0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00,
+ 0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061,
+ 0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB,
+ 0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E,
+ 0xA6078084, 0x19F8509E, 0xE8EFD855, 0x61D99735,
+ 0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC,
+ 0x9E447A2E, 0xC3453484, 0xFDD56705, 0x0E1E9EC9,
+ 0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340,
+ 0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20,
+ 0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7
+ },
+
+ KS2 = {
+ 0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934,
+ 0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068,
+ 0xD4082471, 0x3320F46A, 0x43B7D4B7, 0x500061AF,
+ 0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840,
+ 0x4D95FC1D, 0x96B591AF, 0x70F4DDD3, 0x66A02F45,
+ 0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504,
+ 0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A,
+ 0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB,
+ 0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE,
+ 0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6,
+ 0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42,
+ 0x20FE9E35, 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B,
+ 0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2,
+ 0x3A6EFA74, 0xDD5B4332, 0x6841E7F7, 0xCA7820FB,
+ 0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527,
+ 0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B,
+ 0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33,
+ 0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C,
+ 0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3,
+ 0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC,
+ 0x07F9C9EE, 0x41041F0F, 0x404779A4, 0x5D886E17,
+ 0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564,
+ 0x257B7834, 0x602A9C60, 0xDFF8E8A3, 0x1F636C1B,
+ 0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115,
+ 0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922,
+ 0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728,
+ 0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0,
+ 0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E,
+ 0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37,
+ 0xA812DC60, 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D,
+ 0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804,
+ 0xF1290DC7, 0xCC00FFA3, 0xB5390F92, 0x690FED0B,
+ 0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3,
+ 0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB,
+ 0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D,
+ 0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C,
+ 0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350,
+ 0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9,
+ 0x44421659, 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A,
+ 0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE,
+ 0x9DBC8057, 0xF0F7C086, 0x60787BF8, 0x6003604D,
+ 0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC,
+ 0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F,
+ 0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61,
+ 0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2,
+ 0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9,
+ 0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2,
+ 0x466E598E, 0x20B45770, 0x8CD55591, 0xC902DE4C,
+ 0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E,
+ 0xB77F19B6, 0xE0A9DC09, 0x662D09A1, 0xC4324633,
+ 0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10,
+ 0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169,
+ 0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52,
+ 0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027,
+ 0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5,
+ 0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62,
+ 0x11E69ED7, 0x2338EA63, 0x53C2DD94, 0xC2C21634,
+ 0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76,
+ 0x6F05E409, 0x4B7C0188, 0x39720A3D, 0x7C927C24,
+ 0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC,
+ 0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4,
+ 0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C,
+ 0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837,
+ 0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0
+ },
+
+ KS3 = {
+ 0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B,
+ 0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE,
+ 0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B,
+ 0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4,
+ 0x5748AB2F, 0xBC946E79, 0xC6A376D2, 0x6549C2C8,
+ 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6,
+ 0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304,
+ 0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22,
+ 0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4,
+ 0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6,
+ 0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9,
+ 0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59,
+ 0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593,
+ 0xE990FD5A, 0x9E34D797, 0x2CF0B7D9, 0x022B8B51,
+ 0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28,
+ 0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C,
+ 0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B,
+ 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28,
+ 0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C,
+ 0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD,
+ 0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A,
+ 0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319,
+ 0x7533D928, 0xB155FDF5, 0x03563482, 0x8ABA3CBB,
+ 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F,
+ 0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991,
+ 0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32,
+ 0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680,
+ 0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166,
+ 0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE,
+ 0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB,
+ 0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5,
+ 0x72EACEA8, 0xFA6484BB, 0x8D6612AE, 0xBF3C6F47,
+ 0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370,
+ 0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D,
+ 0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84,
+ 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048,
+ 0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8,
+ 0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD,
+ 0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9,
+ 0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7,
+ 0x1A908749, 0xD44FBD9A, 0xD0DADECB, 0xD50ADA38,
+ 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F,
+ 0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C,
+ 0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525,
+ 0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1,
+ 0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442,
+ 0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964,
+ 0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E,
+ 0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8,
+ 0xDF359F8D, 0x9B992F2E, 0xE60B6F47, 0x0FE3F11D,
+ 0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F,
+ 0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299,
+ 0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02,
+ 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC,
+ 0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614,
+ 0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A,
+ 0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6,
+ 0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B,
+ 0x53113EC0, 0x1640E3D3, 0x38ABBD60, 0x2547ADF0,
+ 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060,
+ 0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E,
+ 0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9,
+ 0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F,
+ 0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6
+ };
+
+ //====================================
+ // Useful constants
+ //====================================
+
+ private static final int ROUNDS = 16;
+ private static final int BLOCK_SIZE = 8; // bytes = 64 bits
+ private static final int SBOX_SK = 256;
+ private static final int P_SZ = ROUNDS+2;
+
+ private final int[] S0, S1, S2, S3; // the s-boxes
+ private final int[] P; // the p-array
+
+ private boolean encrypting = false;
+
+ private byte[] workingKey = null;
+
+ public BlowfishEngine()
+ {
+ S0 = new int[SBOX_SK];
+ S1 = new int[SBOX_SK];
+ S2 = new int[SBOX_SK];
+ S3 = new int[SBOX_SK];
+ P = new int[P_SZ];
+ }
+
+ /**
+ * initialise a Blowfish cipher.
+ *
+ * @param encrypting whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ this.encrypting = encrypting;
+ this.workingKey = ((KeyParameter)params).getKey();
+ setKey(this.workingKey);
+
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to Blowfish init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Blowfish";
+ }
+
+ public final int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new IllegalStateException("Blowfish not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (encrypting)
+ {
+ encryptBlock(in, inOff, out, outOff);
+ }
+ else
+ {
+ decryptBlock(in, inOff, out, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ //==================================
+ // Private Implementation
+ //==================================
+
+ private int F(int x)
+ {
+ return (((S0[(x >>> 24)] + S1[(x >>> 16) & 0xff])
+ ^ S2[(x >>> 8) & 0xff]) + S3[x & 0xff]);
+ }
+
+ /**
+ * apply the encryption cycle to each value pair in the table.
+ */
+ private void processTable(
+ int xl,
+ int xr,
+ int[] table)
+ {
+ int size = table.length;
+
+ for (int s = 0; s < size; s += 2)
+ {
+ xl ^= P[0];
+
+ for (int i = 1; i < ROUNDS; i += 2)
+ {
+ xr ^= F(xl) ^ P[i];
+ xl ^= F(xr) ^ P[i + 1];
+ }
+
+ xr ^= P[ROUNDS + 1];
+
+ table[s] = xr;
+ table[s + 1] = xl;
+
+ xr = xl; // end of cycle swap
+ xl = table[s];
+ }
+ }
+
+ private void setKey(byte[] key)
+ {
+ /*
+ * - comments are from _Applied Crypto_, Schneier, p338
+ * please be careful comparing the two, AC numbers the
+ * arrays from 1, the enclosed code from 0.
+ *
+ * (1)
+ * Initialise the S-boxes and the P-array, with a fixed string
+ * This string contains the hexadecimal digits of pi (3.141...)
+ */
+ System.arraycopy(KS0, 0, S0, 0, SBOX_SK);
+ System.arraycopy(KS1, 0, S1, 0, SBOX_SK);
+ System.arraycopy(KS2, 0, S2, 0, SBOX_SK);
+ System.arraycopy(KS3, 0, S3, 0, SBOX_SK);
+
+ System.arraycopy(KP, 0, P, 0, P_SZ);
+
+ /*
+ * (2)
+ * Now, XOR P[0] with the first 32 bits of the key, XOR P[1] with the
+ * second 32-bits of the key, and so on for all bits of the key
+ * (up to P[17]). Repeatedly cycle through the key bits until the
+ * entire P-array has been XOR-ed with the key bits
+ */
+ int keyLength = key.length;
+ int keyIndex = 0;
+
+ for (int i=0; i < P_SZ; i++)
+ {
+ // get the 32 bits of the key, in 4 * 8 bit chunks
+ int data = 0x0000000;
+ for (int j=0; j < 4; j++)
+ {
+ // create a 32 bit block
+ data = (data << 8) | (key[keyIndex++] & 0xff);
+
+ // wrap when we get to the end of the key
+ if (keyIndex >= keyLength)
+ {
+ keyIndex = 0;
+ }
+ }
+ // XOR the newly created 32 bit chunk onto the P-array
+ P[i] ^= data;
+ }
+
+ /*
+ * (3)
+ * Encrypt the all-zero string with the Blowfish algorithm, using
+ * the subkeys described in (1) and (2)
+ *
+ * (4)
+ * Replace P1 and P2 with the output of step (3)
+ *
+ * (5)
+ * Encrypt the output of step(3) using the Blowfish algorithm,
+ * with the modified subkeys.
+ *
+ * (6)
+ * Replace P3 and P4 with the output of step (5)
+ *
+ * (7)
+ * Continue the process, replacing all elements of the P-array
+ * and then all four S-boxes in order, with the output of the
+ * continuously changing Blowfish algorithm
+ */
+
+ processTable(0, 0, P);
+ processTable(P[P_SZ - 2], P[P_SZ - 1], S0);
+ processTable(S0[SBOX_SK - 2], S0[SBOX_SK - 1], S1);
+ processTable(S1[SBOX_SK - 2], S1[SBOX_SK - 1], S2);
+ processTable(S2[SBOX_SK - 2], S2[SBOX_SK - 1], S3);
+ }
+
+ /**
+ * Encrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ * The input will be an exact multiple of our blocksize.
+ */
+ private void encryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+ int xl = BytesTo32bits(src, srcIndex);
+ int xr = BytesTo32bits(src, srcIndex+4);
+
+ xl ^= P[0];
+
+ for (int i = 1; i < ROUNDS; i += 2)
+ {
+ xr ^= F(xl) ^ P[i];
+ xl ^= F(xr) ^ P[i + 1];
+ }
+
+ xr ^= P[ROUNDS + 1];
+
+ Bits32ToBytes(xr, dst, dstIndex);
+ Bits32ToBytes(xl, dst, dstIndex + 4);
+ }
+
+ /**
+ * Decrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ * The input will be an exact multiple of our blocksize.
+ */
+ private void decryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+ int xl = BytesTo32bits(src, srcIndex);
+ int xr = BytesTo32bits(src, srcIndex + 4);
+
+ xl ^= P[ROUNDS + 1];
+
+ for (int i = ROUNDS; i > 0 ; i -= 2)
+ {
+ xr ^= F(xl) ^ P[i];
+ xl ^= F(xr) ^ P[i - 1];
+ }
+
+ xr ^= P[0];
+
+ Bits32ToBytes(xr, dst, dstIndex);
+ Bits32ToBytes(xl, dst, dstIndex+4);
+ }
+
+ private int BytesTo32bits(byte[] b, int i)
+ {
+ return ((b[i] & 0xff) << 24) |
+ ((b[i+1] & 0xff) << 16) |
+ ((b[i+2] & 0xff) << 8) |
+ ((b[i+3] & 0xff));
+ }
+
+ private void Bits32ToBytes(int in, byte[] b, int offset)
+ {
+ b[offset + 3] = (byte)in;
+ b[offset + 2] = (byte)(in >> 8);
+ b[offset + 1] = (byte)(in >> 16);
+ b[offset] = (byte)(in >> 24);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/CAST5Engine.java b/core/src/main/java/org/bouncycastle/crypto/engines/CAST5Engine.java
new file mode 100644
index 00000000..5a8c7806
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/CAST5Engine.java
@@ -0,0 +1,831 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * A class that provides CAST key encryption operations,
+ * such as encoding data and generating keys.
+ *
+ * All the algorithms herein are from the Internet RFC's
+ *
+ * RFC2144 - CAST5 (64bit block, 40-128bit key)
+ * RFC2612 - CAST6 (128bit block, 128-256bit key)
+ *
+ * and implement a simplified cryptography interface.
+ */
+public class CAST5Engine
+ implements BlockCipher
+{
+ protected final static int M32 = 0xffffffff;
+
+ protected final static int[]
+ S1 = {
+0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949,
+0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e,
+0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d,
+0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, 0x22568e3a, 0xa2d341d0,
+0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7,
+0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935,
+0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f, 0xb48ee411, 0x4bff345d,
+0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, 0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50,
+0x882240f2, 0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe,
+0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3,
+0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167,
+0x38901091, 0xc6b505eb, 0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291,
+0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779,
+0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6c2,
+0x81383f05, 0x6963c5c8, 0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511,
+0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495, 0xaa573b04, 0x4a805d8d,
+0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e, 0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5,
+0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324,
+0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, 0xe31231b2, 0x2ad5ad6c,
+0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f, 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc,
+0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d,
+0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 0x5ad328d8, 0xb347cc96,
+0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, 0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a,
+0x3f04442f, 0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d,
+0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd,
+0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6,
+0x580304f0, 0xca042cf1, 0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9,
+0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872,
+0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814c,
+0x474d6ad7, 0x7c0c5e5c, 0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e,
+0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff, 0xb141ab08, 0x7cca89b9,
+0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, 0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf
+ },
+ S2 = {
+0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, 0xeec5207a, 0x55889c94, 0x72fc0651,
+0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba, 0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3,
+0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909, 0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb,
+0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, 0x25a1ff41, 0xe180f806,
+0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4, 0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b,
+0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f, 0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359,
+0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, 0x361e3084, 0xe4eb573b,
+0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, 0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c,
+0x10843094, 0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34,
+0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c, 0x1d804366, 0x721d9bfd, 0xa58684bb,
+0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd,
+0xc5d655dd, 0xeb667064, 0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860,
+0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf, 0x68561be6, 0x83ca6b94, 0x2d6ed23b,
+0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304,
+0x81ed6f61, 0x20e74364, 0xb45e1378, 0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b,
+0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, 0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf,
+0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9, 0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c,
+0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3, 0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13,
+0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, 0x7cbad9a2, 0x2180036f,
+0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab, 0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6,
+0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4, 0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6,
+0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, 0xef8579cc, 0xd152de58,
+0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, 0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906,
+0xb8da230c, 0x80823028, 0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d,
+0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc, 0x301e16e6, 0x273be979, 0xb0ffeaa6,
+0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4,
+0xdc8637a0, 0x16a7d3b1, 0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6,
+0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb, 0x5eea29cb, 0x145892f5, 0x91584f7f,
+0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea, 0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249,
+0xb284600c, 0xd835731d, 0xdcb1c647, 0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa,
+0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, 0xa345415e, 0x5c038323, 0x3e5d3bb9,
+0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef, 0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1
+ },
+ S3 = {
+0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, 0x369fe44b, 0x8c1fc644, 0xaececa90,
+0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae, 0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5,
+0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e, 0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e,
+0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd, 0x9255c5ed, 0x1257a240,
+0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e, 0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5,
+0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f, 0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b,
+0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, 0xc5884a28, 0xccc36f71,
+0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, 0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04,
+0xa747d2d0, 0x1651192e, 0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82,
+0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0, 0x1eac5790, 0x796fb449, 0x8252dc15,
+0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2,
+0x23efe941, 0xa903f12e, 0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176,
+0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83, 0x340ce5c8, 0x96bbb682, 0x93b4b148,
+0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d, 0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc,
+0x8b907cee, 0xb51fd240, 0xe7c07ce3, 0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341,
+0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, 0xbda8229c, 0x127dadaa, 0x438a074e,
+0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15, 0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51,
+0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4, 0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f,
+0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, 0x27627545, 0x825cf47a,
+0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392, 0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b,
+0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231, 0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b,
+0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, 0x12deca4d, 0x2c3f8cc5,
+0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, 0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45,
+0x3a609437, 0xec00c9a9, 0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536,
+0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1, 0x006e1888, 0xa2e53f55, 0xb9e6d4bc,
+0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0,
+0x947b0001, 0x570075d2, 0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69,
+0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2, 0x61efc8c2, 0xf1ac2571, 0xcc8239c2,
+0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49,
+0x5727c148, 0x2be98a1d, 0x8ab41738, 0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d,
+0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, 0x9c305a00, 0x52bce688, 0x1b03588a,
+0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5, 0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783
+ },
+ S4 = {
+0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, 0x64ad8c57, 0x85510443, 0xfa020ed1,
+0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120, 0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf,
+0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220, 0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15,
+0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, 0x081b08ca, 0x05170121,
+0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701, 0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25,
+0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b, 0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5,
+0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, 0x72500e03, 0xf80eb2bb,
+0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746, 0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5,
+0x4d351805, 0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d,
+0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8, 0x3fb6180c, 0x18f8931e, 0x281658e6,
+0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c, 0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23,
+0x69dead38, 0x1574ca16, 0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003,
+0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de, 0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6,
+0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327, 0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119,
+0x6e85cb75, 0xbe07c002, 0xc2325577, 0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24,
+0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, 0xeca1d7c7, 0x041afa32, 0x1d16625a,
+0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031, 0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79,
+0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5, 0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df,
+0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, 0x213d42f6, 0x2c1c7c26,
+0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69, 0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab,
+0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9, 0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7,
+0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, 0xcfcbd12f, 0xc1de8417,
+0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, 0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2,
+0x6f7de532, 0x58fd7eb6, 0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2,
+0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415, 0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a,
+0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919,
+0x77079103, 0xdea03af6, 0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef,
+0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb, 0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876,
+0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367, 0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab,
+0xb5676e69, 0x9bd3ddda, 0xdf7e052f, 0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04,
+0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, 0x932bcdf6, 0xb657c34d, 0x4edfd282,
+0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e, 0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2
+ },
+ S5 = {
+0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911, 0xb86a7fff, 0x1dd358f5, 0x44dd9d44, 0x1731167f,
+0x08fbf1fa, 0xe7f511cc, 0xd2051b00, 0x735aba00, 0x2ab722d8, 0x386381cb, 0xacf6243a, 0x69befd7a,
+0xe6a2e77f, 0xf0c720cd, 0xc4494816, 0xccf5c180, 0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff,
+0x5f480a01, 0x0412b2aa, 0x259814fc, 0x41d0efe2, 0x4e40b48d, 0x248eb6fb, 0x8dba1cfe, 0x41a99b02,
+0x1a550a04, 0xba8f65cb, 0x7251f4e7, 0x95a51725, 0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a,
+0xf2f3f763, 0x68af8040, 0xed0c9e56, 0x11b4958b, 0xe1eb5a88, 0x8709e6b0, 0xd7e07156, 0x4e29fea7,
+0x6366e52d, 0x02d1c000, 0xc4ac8e05, 0x9377f571, 0x0c05372a, 0x578535f2, 0x2261be02, 0xd642a0c9,
+0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec, 0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981,
+0x5c1ff900, 0xfe38d399, 0x0c4eff0b, 0x062407ea, 0xaa2f4fb1, 0x4fb96976, 0x90c79505, 0xb0a8a774,
+0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, 0xe66a4263, 0xdf65001f, 0x0ec50966, 0xdfdd55bc, 0x29de0655,
+0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468, 0x0d01e980, 0x524755f4, 0x03b63cc9, 0x0cc844b2,
+0xbcf3f0aa, 0x87ac36e9, 0xe53a7426, 0x01b3d82b, 0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, 0x01c94910,
+0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284, 0x636737b6, 0x50f5b616, 0xf24766e3, 0x8eca36c1,
+0x136e05db, 0xfef18391, 0xfb887a37, 0xd6e7f7d4, 0xc7fb7dc9, 0x3063fcdf, 0xb6f589de, 0xec2941da,
+0x26e46695, 0xb7566419, 0xf654efc5, 0xd08d58b7, 0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049,
+0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce, 0xc62bf3cd, 0x9e0885f9, 0x68cb3e47, 0x086c010f,
+0xa21de820, 0xd18b69de, 0xf3f65777, 0xfa02c3f6, 0x407edac3, 0xcbb3d550, 0x1793084d, 0xb0d70eba,
+0x0ab378d5, 0xd951fb0c, 0xded7da56, 0x4124bbe4, 0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be,
+0x580a249f, 0x94f74bc0, 0xe327888e, 0x9f7b5561, 0xc3dc0280, 0x05687715, 0x646c6bd7, 0x44904db3,
+0x66b4f0a3, 0xc0f1648a, 0x697ed5af, 0x49e92ff6, 0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840,
+0x76f0ae02, 0x083be84d, 0x28421c9a, 0x44489406, 0x736e4cb8, 0xc1092910, 0x8bc95fc6, 0x7d869cf4,
+0x134f616f, 0x2e77118d, 0xb31b2be1, 0xaa90b472, 0x3ca5d717, 0x7d161bba, 0x9cad9010, 0xaf462ba2,
+0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487, 0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7,
+0x445f7382, 0x175683f4, 0xcdc66a97, 0x70be0288, 0xb3cdcf72, 0x6e5dd2f3, 0x20936079, 0x459b80a5,
+0xbe60e2db, 0xa9c23101, 0xeba5315c, 0x224e42f2, 0x1c5c1572, 0xf6721b2c, 0x1ad2fff3, 0x8c25404e,
+0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78, 0xdc0fd66e, 0x75922283, 0x784d6b17, 0x58ebb16e,
+0x44094f85, 0x3f481d87, 0xfcfeae7b, 0x77b5ff76, 0x8c2302bf, 0xaaf47556, 0x5f46b02a, 0x2b092801,
+0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0, 0xdf3b0874, 0x95055110, 0x1b5ad7a8, 0xf61ed5ad,
+0x6cf6e479, 0x20758184, 0xd0cefa65, 0x88f7be58, 0x4a046826, 0x0ff6f8f3, 0xa09c7f70, 0x5346aba0,
+0x5ce96c28, 0xe176eda3, 0x6bac307f, 0x376829d2, 0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20,
+0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be, 0xf9e0659a, 0xeeb9491d, 0x34010718, 0xbb30cab8,
+0xe822fe15, 0x88570983, 0x750e6249, 0xda627e55, 0x5e76ffa8, 0xb1534546, 0x6d47de08, 0xefe9e7d4
+ },
+ S6 = {
+0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c, 0x95db08e7, 0x016843b4, 0xeced5cbc, 0x325553ac,
+0xbf9f0960, 0xdfa1e2ed, 0x83f0579d, 0x63ed86b9, 0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, 0x8989b138,
+0x33f14961, 0xc01937bd, 0xf506c6da, 0xe4625e7e, 0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367,
+0xa3149619, 0xfec94bd5, 0xa114174a, 0xeaa01866, 0xa084db2d, 0x09a8486f, 0xa888614a, 0x2900af98,
+0x01665991, 0xe1992863, 0xc8f30c60, 0x2e78ef3c, 0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072,
+0xfd41197e, 0x9305a6b0, 0xe86be3da, 0x74bed3cd, 0x372da53c, 0x4c7f4448, 0xdab5d440, 0x6dba0ec3,
+0x083919a7, 0x9fbaeed9, 0x49dbcfb0, 0x4e670c53, 0x5c3d9c01, 0x64bdb941, 0x2c0e636a, 0xba7dd9cd,
+0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d, 0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8,
+0x284caf89, 0xaa928223, 0x9334be53, 0x3b3a21bf, 0x16434be3, 0x9aea3906, 0xefe8c36e, 0xf890cdd9,
+0x80226dae, 0xc340a4a3, 0xdf7e9c09, 0xa694a807, 0x5b7c5ecc, 0x221db3a6, 0x9a69a02f, 0x68818a54,
+0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a, 0xb4628abc, 0xcf222ebf, 0x25ac6f48, 0xa9a99387,
+0x53bddb65, 0xe76ffbe7, 0xe967fd78, 0x0ba93563, 0x8e342bc1, 0xe8a11be9, 0x4980740d, 0xc8087dfc,
+0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0, 0xe81f994f, 0x9528cd89, 0xfd339fed, 0xb87834bf,
+0x5f04456d, 0x22258698, 0xc9c4c83b, 0x2dc156be, 0x4f628daa, 0x57f55ec5, 0xe2220abe, 0xd2916ebf,
+0x4ec75b95, 0x24f2c3c0, 0x42d15d99, 0xcd0d7fa0, 0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f,
+0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2, 0x372b74af, 0x692573e4, 0xe9a9d848, 0xf3160289,
+0x3a62ef1d, 0xa787e238, 0xf3a5f676, 0x74364853, 0x20951063, 0x4576698d, 0xb6fad407, 0x592af950,
+0x36f73523, 0x4cfb6e87, 0x7da4cec0, 0x6c152daa, 0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f,
+0x89dff0bb, 0x5fe2be78, 0x448f4f33, 0x754613c9, 0x2b05d08d, 0x48b9d585, 0xdc049441, 0xc8098f9b,
+0x7dede786, 0xc39a3373, 0x42410005, 0x6a091751, 0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be,
+0xbf32679d, 0xd45b5b75, 0xb353fd00, 0xcbb0e358, 0x830f220a, 0x1f8fb214, 0xd372cf08, 0xcc3c4a13,
+0x8cf63166, 0x061c87be, 0x88c98f88, 0x6062e397, 0x47cf8e7a, 0xb6c85283, 0x3cc2acfb, 0x3fc06976,
+0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459, 0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0,
+0x3007cd3e, 0x74719eef, 0xdc872681, 0x073340d4, 0x7e432fd9, 0x0c5ec241, 0x8809286c, 0xf592d891,
+0x08a930f6, 0x957ef305, 0xb7fbffbd, 0xc266e96f, 0x6fe4ac98, 0xb173ecc0, 0xbc60b42a, 0x953498da,
+0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb, 0xe2969123, 0x257f0c3d, 0x9348af49, 0x361400bc,
+0xe8816f4a, 0x3814f200, 0xa3f94043, 0x9c7a54c2, 0xbc704f57, 0xda41e7f9, 0xc25ad33a, 0x54f4a084,
+0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab, 0xba5ac7b5, 0xb6f6deaf, 0x3a479c3a, 0x5302da25,
+0x653d7e6a, 0x54268d49, 0x51a477ea, 0x5017d55b, 0xd7d25d88, 0x44136c76, 0x0404a8c8, 0xb8e5a121,
+0xb81a928a, 0x60ed5869, 0x97c55b96, 0xeaec991b, 0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5,
+0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855, 0xd36b4cf1, 0xf544edeb, 0xb0e93524, 0xbebb8fbd,
+0xa2d762cf, 0x49c92f54, 0x38b5f331, 0x7128a454, 0x48392905, 0xa65b1db8, 0x851c97bd, 0xd675cf2f
+ },
+ S7 = {
+0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693, 0x2a8d7f6f, 0xab9bc912, 0xde6008a1, 0x2028da1f,
+0x0227bce7, 0x4d642916, 0x18fac300, 0x50f18b82, 0x2cb2cb11, 0xb232e75c, 0x4b3695f2, 0xb28707de,
+0xa05fbcf6, 0xcd4181e9, 0xe150210c, 0xe24ef1bd, 0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43,
+0x4d495001, 0x38be4341, 0x913cee1d, 0x92a79c3f, 0x089766be, 0xbaeeadf4, 0x1286becf, 0xb6eacb19,
+0x2660c200, 0x7565bde4, 0x64241f7a, 0x8248dca9, 0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2,
+0x107789be, 0xb3b2e9ce, 0x0502aa8f, 0x0bc0351e, 0x166bf52a, 0xeb12ff82, 0xe3486911, 0xd34d7516,
+0x4e7b3aff, 0x5f43671b, 0x9cf6e037, 0x4981ac83, 0x334266ce, 0x8c9341b7, 0xd0d854c0, 0xcb3a6c88,
+0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e, 0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816,
+0x0a961288, 0xe1a5c06e, 0x13749e67, 0x72fc081a, 0xb1d139f7, 0xf9583745, 0xcf19df58, 0xbec3f756,
+0xc06eba30, 0x07211b24, 0x45c28829, 0xc95e317f, 0xbc8ec511, 0x38bc46e9, 0xc6e6fa14, 0xbae8584a,
+0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b, 0x821dba9f, 0xaff60ff4, 0xea2c4e6d, 0x16e39264,
+0x92544a8b, 0x009b4fc3, 0xaba68ced, 0x9ac96f78, 0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, 0xbe838688,
+0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d, 0xf7debb85, 0x61fe033c, 0x16746233, 0x3c034c28,
+0xda6d0c74, 0x79aac56c, 0x3ce4e1ad, 0x51f0c802, 0x98f8f35a, 0x1626a49f, 0xeed82b29, 0x1d382fe3,
+0x0c4fb99a, 0xbb325778, 0x3ec6d97b, 0x6e77a6a9, 0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7,
+0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302, 0xb96d8c32, 0xebd4e7be, 0xbe8b9d2d, 0x7979fb06,
+0xe7225308, 0x8b75cf77, 0x11ef8da4, 0xe083c858, 0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, 0x5dda0033,
+0xf28ebfb0, 0xf5b9c310, 0xa0eac280, 0x08b9767a, 0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a,
+0x2711fd60, 0x438050e3, 0x069908a8, 0x3d7fedc4, 0x826d2bef, 0x4eeb8476, 0x488dcf25, 0x36c9d566,
+0x28e74e41, 0xc2610aca, 0x3d49a9cf, 0xbae3b9df, 0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509,
+0xf22b017d, 0xa4173f70, 0xdd1e16c3, 0x15e0d7f9, 0x50b1b887, 0x2b9f4fd5, 0x625aba82, 0x6a017962,
+0x2ec01b9c, 0x15488aa9, 0xd716e740, 0x40055a2c, 0x93d29a22, 0xe32dbf9a, 0x058745b9, 0x3453dc1e,
+0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07, 0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c,
+0x66626c1c, 0x7154c24c, 0xea082b2a, 0x93eb2939, 0x17dcb0f0, 0x58d4f2ae, 0x9ea294fb, 0x52cf564c,
+0x9883fe66, 0x2ec40581, 0x763953c3, 0x01d6692e, 0xd3a0c108, 0xa1e7160e, 0xe4f2dfa6, 0x693ed285,
+0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378, 0xa132234f, 0x3d321c5d, 0xc3f5e194, 0x4b269301,
+0xc79f022f, 0x3c997e7e, 0x5e4f9504, 0x3ffafbbd, 0x76f7ad0e, 0x296693f4, 0x3d1fce6f, 0xc61e45be,
+0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567, 0x5592a33d, 0xb5229301, 0xcfd2a87f, 0x60aeb767,
+0x1814386b, 0x30bcc33d, 0x38a0c07d, 0xfd1606f2, 0xc363519b, 0x589dd390, 0x5479f8e6, 0x1cb8d647,
+0x97fd61a9, 0xea7759f4, 0x2d57539d, 0x569a58cf, 0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914,
+0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2, 0x3ffa50bc, 0x3d40f021, 0xc3c0bdae, 0x4958c24c,
+0x518f36b2, 0x84b1d370, 0x0fedce83, 0x878ddada, 0xf2a279c7, 0x94e01be8, 0x90716f4b, 0x954b8aa3
+ },
+ S8 = {
+0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095, 0x7789f8b7, 0xe6c1121b, 0x0e241600, 0x052ce8b5,
+0x11a9cfb0, 0xe5952f11, 0xece7990a, 0x9386d174, 0x2a42931c, 0x76e38111, 0xb12def3a, 0x37ddddfc,
+0xde9adeb1, 0x0a0cc32c, 0xbe197029, 0x84a00940, 0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd,
+0x0b15a15d, 0x480d3168, 0x8bbbde5a, 0x669ded42, 0xc7ece831, 0x3f8f95e7, 0x72df191b, 0x7580330d,
+0x94074251, 0x5c7dcdfa, 0xabbe6d63, 0xaa402164, 0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2,
+0x12a8ddec, 0xfdaa335d, 0x176f43e8, 0x71fb46d4, 0x38129022, 0xce949ad4, 0xb84769ad, 0x965bd862,
+0x82f3d055, 0x66fb9767, 0x15b80b4e, 0x1d5b47a0, 0x4cfde06f, 0xc28ec4b8, 0x57e8726e, 0x647a78fc,
+0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6, 0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c,
+0xbbd35049, 0x2998df04, 0x980cf42a, 0x9b6df491, 0x9e7edd53, 0x06918548, 0x58cb7e07, 0x3b74ef2e,
+0x522fffb1, 0xd24708cc, 0x1c7e27cd, 0xa4eb215b, 0x3cf1d2e2, 0x19b47a38, 0x424f7618, 0x35856039,
+0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8, 0x09c467cd, 0xc18910b1, 0xe11dbf7b, 0x06cd1af8,
+0x7170c608, 0x2d5e3354, 0xd4de495a, 0x64c6d006, 0xbcc0c62c, 0x3dd00db3, 0x708f8f34, 0x77d51b42,
+0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564, 0xf8d7e54e, 0x3e378160, 0x7895cda5, 0x859c15a5,
+0xe6459788, 0xc37bc75f, 0xdb07ba0c, 0x0676a3ab, 0x7f229b1e, 0x31842e7b, 0x24259fd7, 0xf8bef472,
+0x835ffcb8, 0x6df4c1f2, 0x96f5b195, 0xfd0af0fc, 0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225,
+0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8, 0xc4618187, 0xea7a6e98, 0x7cd16efc, 0x1436876c,
+0xf1544107, 0xbedeee14, 0x56e9af27, 0xa04aa441, 0x3cf7c899, 0x92ecbae6, 0xdd67016d, 0x151682eb,
+0xa842eedf, 0xfdba60b4, 0xf1907b75, 0x20e3030f, 0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054,
+0xb6f2cf3b, 0x9f326442, 0xcb15a4cc, 0xb01a4504, 0xf1e47d8d, 0x844a1be5, 0xbae7dfdc, 0x42cbda70,
+0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, 0x20cf4d8c, 0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc,
+0x77853b53, 0x37effcb5, 0xc5068778, 0xe580b3e6, 0x4e68b8f4, 0xc5c8b37e, 0x0d809ea2, 0x398feb7c,
+0x132a4f94, 0x43b7950e, 0x2fee7d1c, 0x223613bd, 0xdd06caa2, 0x37df932b, 0xc4248289, 0xacf3ebc3,
+0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4, 0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4,
+0xe87b40e4, 0xe98ea084, 0x5889e9e1, 0xefd390fc, 0xdd07d35b, 0xdb485694, 0x38d7e5b2, 0x57720101,
+0x730edebc, 0x5b643113, 0x94917e4f, 0x503c2fba, 0x646f1282, 0x7523d24a, 0xe0779695, 0xf9c17a8f,
+0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf, 0x81f47c9f, 0xad1163ed, 0xea7b5965, 0x1a00726e,
+0x11403092, 0x00da6d77, 0x4a0cdd61, 0xad1f4603, 0x605bdfb0, 0x9eedc364, 0x22ebe6a8, 0xcee7d28a,
+0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37, 0x2de705ca, 0x8951570f, 0xdf09822b, 0xbd691a6c,
+0xaa12e4f2, 0x87451c0f, 0xe0f6a27a, 0x3ada4819, 0x4cf1764f, 0x0d771c2b, 0x67cdb156, 0x350d8384,
+0x5938fa0f, 0x42399ef3, 0x36997b07, 0x0e84093d, 0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c,
+0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347, 0x589e8d82, 0x0d2059d1, 0xa466bb1e, 0xf8da0a82,
+0x04f19130, 0xba6e4ec0, 0x99265164, 0x1ee7230d, 0x50b2ad80, 0xeaee6801, 0x8db2a283, 0xea8bf59e
+ };
+
+ //====================================
+ // Useful constants
+ //====================================
+
+ protected static final int MAX_ROUNDS = 16;
+ protected static final int RED_ROUNDS = 12;
+
+ protected static final int BLOCK_SIZE = 8; // bytes = 64 bits
+
+ protected int _Kr[] = new int[17]; // the rotating round key
+ protected int _Km[] = new int[17]; // the masking round key
+
+ private boolean _encrypting = false;
+
+ private byte[] _workingKey = null;
+ private int _rounds = MAX_ROUNDS;
+
+ public CAST5Engine()
+ {
+ }
+
+ /**
+ * initialise a CAST cipher.
+ *
+ * @param encrypting whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ _encrypting = encrypting;
+ _workingKey = ((KeyParameter)params).getKey();
+
+ setKey(_workingKey);
+
+ return;
+ }
+
+ throw new IllegalArgumentException("Invalid parameter passed to "+getAlgorithmName()+" init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "CAST5";
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (_workingKey == null)
+ {
+ throw new IllegalStateException(getAlgorithmName()+" not initialised");
+ }
+
+ int blockSize = getBlockSize();
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("Input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new OutputLengthException("Output buffer too short");
+ }
+
+ if (_encrypting)
+ {
+ return encryptBlock(in, inOff, out, outOff);
+ }
+ else
+ {
+ return decryptBlock(in, inOff, out, outOff);
+ }
+ }
+
+ public void reset()
+ {
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ //==================================
+ // Private Implementation
+ //==================================
+
+ /*
+ * Creates the subkeys using the same nomenclature
+ * as described in RFC2144.
+ *
+ * See section 2.4
+ */
+ protected void setKey(byte[] key)
+ {
+ /*
+ * Determine the key size here, if required
+ *
+ * if keysize <= 80bits, use 12 rounds instead of 16
+ * if keysize < 128bits, pad with 0
+ *
+ * Typical key sizes => 40, 64, 80, 128
+ */
+
+ if (key.length < 11)
+ {
+ _rounds = RED_ROUNDS;
+ }
+
+ int z[] = new int[16];
+ int x[] = new int[16];
+
+ int z03, z47, z8B, zCF;
+ int x03, x47, x8B, xCF;
+
+ /* copy the key into x */
+ for (int i=0; i< key.length; i++)
+ {
+ x[i] = key[i] & 0xff;
+ }
+
+ /*
+ * This will look different because the selection of
+ * bytes from the input key I've already chosen the
+ * correct int.
+ */
+ x03 = IntsTo32bits(x, 0x0);
+ x47 = IntsTo32bits(x, 0x4);
+ x8B = IntsTo32bits(x, 0x8);
+ xCF = IntsTo32bits(x, 0xC);
+
+ z03 = x03 ^S5[x[0xD]] ^S6[x[0xF]] ^S7[x[0xC]] ^S8[x[0xE]] ^S7[x[0x8]];
+
+ Bits32ToInts(z03, z, 0x0);
+ z47 = x8B ^S5[z[0x0]] ^S6[z[0x2]] ^S7[z[0x1]] ^S8[z[0x3]] ^S8[x[0xA]];
+ Bits32ToInts(z47, z, 0x4);
+ z8B = xCF ^S5[z[0x7]] ^S6[z[0x6]] ^S7[z[0x5]] ^S8[z[0x4]] ^S5[x[0x9]];
+ Bits32ToInts(z8B, z, 0x8);
+ zCF = x47 ^S5[z[0xA]] ^S6[z[0x9]] ^S7[z[0xB]] ^S8[z[0x8]] ^S6[x[0xB]];
+ Bits32ToInts(zCF, z, 0xC);
+ _Km[ 1]= S5[z[0x8]] ^ S6[z[0x9]] ^ S7[z[0x7]] ^ S8[z[0x6]] ^ S5[z[0x2]];
+ _Km[ 2]= S5[z[0xA]] ^ S6[z[0xB]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S6[z[0x6]];
+ _Km[ 3]= S5[z[0xC]] ^ S6[z[0xD]] ^ S7[z[0x3]] ^ S8[z[0x2]] ^ S7[z[0x9]];
+ _Km[ 4]= S5[z[0xE]] ^ S6[z[0xF]] ^ S7[z[0x1]] ^ S8[z[0x0]] ^ S8[z[0xC]];
+
+ z03 = IntsTo32bits(z, 0x0);
+ z47 = IntsTo32bits(z, 0x4);
+ z8B = IntsTo32bits(z, 0x8);
+ zCF = IntsTo32bits(z, 0xC);
+ x03 = z8B ^S5[z[0x5]] ^S6[z[0x7]] ^S7[z[0x4]] ^S8[z[0x6]] ^S7[z[0x0]];
+ Bits32ToInts(x03, x, 0x0);
+ x47 = z03 ^S5[x[0x0]] ^S6[x[0x2]] ^S7[x[0x1]] ^S8[x[0x3]] ^S8[z[0x2]];
+ Bits32ToInts(x47, x, 0x4);
+ x8B = z47 ^S5[x[0x7]] ^S6[x[0x6]] ^S7[x[0x5]] ^S8[x[0x4]] ^S5[z[0x1]];
+ Bits32ToInts(x8B, x, 0x8);
+ xCF = zCF ^S5[x[0xA]] ^S6[x[0x9]] ^S7[x[0xB]] ^S8[x[0x8]] ^S6[z[0x3]];
+ Bits32ToInts(xCF, x, 0xC);
+ _Km[ 5]= S5[x[0x3]] ^ S6[x[0x2]] ^ S7[x[0xC]] ^ S8[x[0xD]] ^ S5[x[0x8]];
+ _Km[ 6]= S5[x[0x1]] ^ S6[x[0x0]] ^ S7[x[0xE]] ^ S8[x[0xF]] ^ S6[x[0xD]];
+ _Km[ 7]= S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x8]] ^ S8[x[0x9]] ^ S7[x[0x3]];
+ _Km[ 8]= S5[x[0x5]] ^ S6[x[0x4]] ^ S7[x[0xA]] ^ S8[x[0xB]] ^ S8[x[0x7]];
+
+ x03 = IntsTo32bits(x, 0x0);
+ x47 = IntsTo32bits(x, 0x4);
+ x8B = IntsTo32bits(x, 0x8);
+ xCF = IntsTo32bits(x, 0xC);
+ z03 = x03 ^S5[x[0xD]] ^S6[x[0xF]] ^S7[x[0xC]] ^S8[x[0xE]] ^S7[x[0x8]];
+ Bits32ToInts(z03, z, 0x0);
+ z47 = x8B ^S5[z[0x0]] ^S6[z[0x2]] ^S7[z[0x1]] ^S8[z[0x3]] ^S8[x[0xA]];
+ Bits32ToInts(z47, z, 0x4);
+ z8B = xCF ^S5[z[0x7]] ^S6[z[0x6]] ^S7[z[0x5]] ^S8[z[0x4]] ^S5[x[0x9]];
+ Bits32ToInts(z8B, z, 0x8);
+ zCF = x47 ^S5[z[0xA]] ^S6[z[0x9]] ^S7[z[0xB]] ^S8[z[0x8]] ^S6[x[0xB]];
+ Bits32ToInts(zCF, z, 0xC);
+ _Km[ 9]= S5[z[0x3]] ^ S6[z[0x2]] ^ S7[z[0xC]] ^ S8[z[0xD]] ^ S5[z[0x9]];
+ _Km[10]= S5[z[0x1]] ^ S6[z[0x0]] ^ S7[z[0xE]] ^ S8[z[0xF]] ^ S6[z[0xc]];
+ _Km[11]= S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x8]] ^ S8[z[0x9]] ^ S7[z[0x2]];
+ _Km[12]= S5[z[0x5]] ^ S6[z[0x4]] ^ S7[z[0xA]] ^ S8[z[0xB]] ^ S8[z[0x6]];
+
+ z03 = IntsTo32bits(z, 0x0);
+ z47 = IntsTo32bits(z, 0x4);
+ z8B = IntsTo32bits(z, 0x8);
+ zCF = IntsTo32bits(z, 0xC);
+ x03 = z8B ^S5[z[0x5]] ^S6[z[0x7]] ^S7[z[0x4]] ^S8[z[0x6]] ^S7[z[0x0]];
+ Bits32ToInts(x03, x, 0x0);
+ x47 = z03 ^S5[x[0x0]] ^S6[x[0x2]] ^S7[x[0x1]] ^S8[x[0x3]] ^S8[z[0x2]];
+ Bits32ToInts(x47, x, 0x4);
+ x8B = z47 ^S5[x[0x7]] ^S6[x[0x6]] ^S7[x[0x5]] ^S8[x[0x4]] ^S5[z[0x1]];
+ Bits32ToInts(x8B, x, 0x8);
+ xCF = zCF ^S5[x[0xA]] ^S6[x[0x9]] ^S7[x[0xB]] ^S8[x[0x8]] ^S6[z[0x3]];
+ Bits32ToInts(xCF, x, 0xC);
+ _Km[13]= S5[x[0x8]] ^ S6[x[0x9]] ^ S7[x[0x7]] ^ S8[x[0x6]] ^ S5[x[0x3]];
+ _Km[14]= S5[x[0xA]] ^ S6[x[0xB]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S6[x[0x7]];
+ _Km[15]= S5[x[0xC]] ^ S6[x[0xD]] ^ S7[x[0x3]] ^ S8[x[0x2]] ^ S7[x[0x8]];
+ _Km[16]= S5[x[0xE]] ^ S6[x[0xF]] ^ S7[x[0x1]] ^ S8[x[0x0]] ^ S8[x[0xD]];
+
+ x03 = IntsTo32bits(x, 0x0);
+ x47 = IntsTo32bits(x, 0x4);
+ x8B = IntsTo32bits(x, 0x8);
+ xCF = IntsTo32bits(x, 0xC);
+ z03 = x03 ^S5[x[0xD]] ^S6[x[0xF]] ^S7[x[0xC]] ^S8[x[0xE]] ^S7[x[0x8]];
+ Bits32ToInts(z03, z, 0x0);
+ z47 = x8B ^S5[z[0x0]] ^S6[z[0x2]] ^S7[z[0x1]] ^S8[z[0x3]] ^S8[x[0xA]];
+ Bits32ToInts(z47, z, 0x4);
+ z8B = xCF ^S5[z[0x7]] ^S6[z[0x6]] ^S7[z[0x5]] ^S8[z[0x4]] ^S5[x[0x9]];
+ Bits32ToInts(z8B, z, 0x8);
+ zCF = x47 ^S5[z[0xA]] ^S6[z[0x9]] ^S7[z[0xB]] ^S8[z[0x8]] ^S6[x[0xB]];
+ Bits32ToInts(zCF, z, 0xC);
+ _Kr[ 1]=(S5[z[0x8]]^S6[z[0x9]]^S7[z[0x7]]^S8[z[0x6]] ^ S5[z[0x2]])&0x1f;
+ _Kr[ 2]=(S5[z[0xA]]^S6[z[0xB]]^S7[z[0x5]]^S8[z[0x4]] ^ S6[z[0x6]])&0x1f;
+ _Kr[ 3]=(S5[z[0xC]]^S6[z[0xD]]^S7[z[0x3]]^S8[z[0x2]] ^ S7[z[0x9]])&0x1f;
+ _Kr[ 4]=(S5[z[0xE]]^S6[z[0xF]]^S7[z[0x1]]^S8[z[0x0]] ^ S8[z[0xC]])&0x1f;
+
+ z03 = IntsTo32bits(z, 0x0);
+ z47 = IntsTo32bits(z, 0x4);
+ z8B = IntsTo32bits(z, 0x8);
+ zCF = IntsTo32bits(z, 0xC);
+ x03 = z8B ^S5[z[0x5]] ^S6[z[0x7]] ^S7[z[0x4]] ^S8[z[0x6]] ^S7[z[0x0]];
+ Bits32ToInts(x03, x, 0x0);
+ x47 = z03 ^S5[x[0x0]] ^S6[x[0x2]] ^S7[x[0x1]] ^S8[x[0x3]] ^S8[z[0x2]];
+ Bits32ToInts(x47, x, 0x4);
+ x8B = z47 ^S5[x[0x7]] ^S6[x[0x6]] ^S7[x[0x5]] ^S8[x[0x4]] ^S5[z[0x1]];
+ Bits32ToInts(x8B, x, 0x8);
+ xCF = zCF ^S5[x[0xA]] ^S6[x[0x9]] ^S7[x[0xB]] ^S8[x[0x8]] ^S6[z[0x3]];
+ Bits32ToInts(xCF, x, 0xC);
+ _Kr[ 5]=(S5[x[0x3]]^S6[x[0x2]]^S7[x[0xC]]^S8[x[0xD]]^S5[x[0x8]])&0x1f;
+ _Kr[ 6]=(S5[x[0x1]]^S6[x[0x0]]^S7[x[0xE]]^S8[x[0xF]]^S6[x[0xD]])&0x1f;
+ _Kr[ 7]=(S5[x[0x7]]^S6[x[0x6]]^S7[x[0x8]]^S8[x[0x9]]^S7[x[0x3]])&0x1f;
+ _Kr[ 8]=(S5[x[0x5]]^S6[x[0x4]]^S7[x[0xA]]^S8[x[0xB]]^S8[x[0x7]])&0x1f;
+
+ x03 = IntsTo32bits(x, 0x0);
+ x47 = IntsTo32bits(x, 0x4);
+ x8B = IntsTo32bits(x, 0x8);
+ xCF = IntsTo32bits(x, 0xC);
+ z03 = x03 ^S5[x[0xD]] ^S6[x[0xF]] ^S7[x[0xC]] ^S8[x[0xE]] ^S7[x[0x8]];
+ Bits32ToInts(z03, z, 0x0);
+ z47 = x8B ^S5[z[0x0]] ^S6[z[0x2]] ^S7[z[0x1]] ^S8[z[0x3]] ^S8[x[0xA]];
+ Bits32ToInts(z47, z, 0x4);
+ z8B = xCF ^S5[z[0x7]] ^S6[z[0x6]] ^S7[z[0x5]] ^S8[z[0x4]] ^S5[x[0x9]];
+ Bits32ToInts(z8B, z, 0x8);
+ zCF = x47 ^S5[z[0xA]] ^S6[z[0x9]] ^S7[z[0xB]] ^S8[z[0x8]] ^S6[x[0xB]];
+ Bits32ToInts(zCF, z, 0xC);
+ _Kr[ 9]=(S5[z[0x3]]^S6[z[0x2]]^S7[z[0xC]]^S8[z[0xD]]^S5[z[0x9]])&0x1f;
+ _Kr[10]=(S5[z[0x1]]^S6[z[0x0]]^S7[z[0xE]]^S8[z[0xF]]^S6[z[0xc]])&0x1f;
+ _Kr[11]=(S5[z[0x7]]^S6[z[0x6]]^S7[z[0x8]]^S8[z[0x9]]^S7[z[0x2]])&0x1f;
+ _Kr[12]=(S5[z[0x5]]^S6[z[0x4]]^S7[z[0xA]]^S8[z[0xB]]^S8[z[0x6]])&0x1f;
+
+ z03 = IntsTo32bits(z, 0x0);
+ z47 = IntsTo32bits(z, 0x4);
+ z8B = IntsTo32bits(z, 0x8);
+ zCF = IntsTo32bits(z, 0xC);
+ x03 = z8B ^S5[z[0x5]] ^S6[z[0x7]] ^S7[z[0x4]] ^S8[z[0x6]] ^S7[z[0x0]];
+ Bits32ToInts(x03, x, 0x0);
+ x47 = z03 ^S5[x[0x0]] ^S6[x[0x2]] ^S7[x[0x1]] ^S8[x[0x3]] ^S8[z[0x2]];
+ Bits32ToInts(x47, x, 0x4);
+ x8B = z47 ^S5[x[0x7]] ^S6[x[0x6]] ^S7[x[0x5]] ^S8[x[0x4]] ^S5[z[0x1]];
+ Bits32ToInts(x8B, x, 0x8);
+ xCF = zCF ^S5[x[0xA]] ^S6[x[0x9]] ^S7[x[0xB]] ^S8[x[0x8]] ^S6[z[0x3]];
+ Bits32ToInts(xCF, x, 0xC);
+ _Kr[13]=(S5[x[0x8]]^S6[x[0x9]]^S7[x[0x7]]^S8[x[0x6]]^S5[x[0x3]])&0x1f;
+ _Kr[14]=(S5[x[0xA]]^S6[x[0xB]]^S7[x[0x5]]^S8[x[0x4]]^S6[x[0x7]])&0x1f;
+ _Kr[15]=(S5[x[0xC]]^S6[x[0xD]]^S7[x[0x3]]^S8[x[0x2]]^S7[x[0x8]])&0x1f;
+ _Kr[16]=(S5[x[0xE]]^S6[x[0xF]]^S7[x[0x1]]^S8[x[0x0]]^S8[x[0xD]])&0x1f;
+ }
+
+ /**
+ * Encrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ *
+ * @param src The plaintext buffer
+ * @param srcIndex An offset into src
+ * @param dst The ciphertext buffer
+ * @param dstIndex An offset into dst
+ */
+ protected int encryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+
+ int result[] = new int[2];
+
+ // process the input block
+ // batch the units up into a 32 bit chunk and go for it
+ // the array is in bytes, the increment is 8x8 bits = 64
+
+ int L0 = BytesTo32bits(src, srcIndex);
+ int R0 = BytesTo32bits(src, srcIndex + 4);
+
+ CAST_Encipher(L0, R0, result);
+
+ // now stuff them into the destination block
+ Bits32ToBytes(result[0], dst, dstIndex);
+ Bits32ToBytes(result[1], dst, dstIndex + 4);
+
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * Decrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ *
+ * @param src The plaintext buffer
+ * @param srcIndex An offset into src
+ * @param dst The ciphertext buffer
+ * @param dstIndex An offset into dst
+ */
+ protected int decryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+ int result[] = new int[2];
+
+ // process the input block
+ // batch the units up into a 32 bit chunk and go for it
+ // the array is in bytes, the increment is 8x8 bits = 64
+ int L16 = BytesTo32bits(src, srcIndex);
+ int R16 = BytesTo32bits(src, srcIndex+4);
+
+ CAST_Decipher(L16, R16, result);
+
+ // now stuff them into the destination block
+ Bits32ToBytes(result[0], dst, dstIndex);
+ Bits32ToBytes(result[1], dst, dstIndex+4);
+
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * The first of the three processing functions for the
+ * encryption and decryption.
+ *
+ * @param D the input to be processed
+ * @param Kmi the mask to be used from Km[n]
+ * @param Kri the rotation value to be used
+ *
+ */
+ protected final int F1(int D, int Kmi, int Kri)
+ {
+ int I = Kmi + D;
+ I = I << Kri | I >>> (32-Kri);
+ return ((S1[(I>>>24)&0xff]^S2[(I>>>16)&0xff])-S3[(I>>> 8)&0xff])+
+ S4[I & 0xff];
+ }
+
+ /**
+ * The second of the three processing functions for the
+ * encryption and decryption.
+ *
+ * @param D the input to be processed
+ * @param Kmi the mask to be used from Km[n]
+ * @param Kri the rotation value to be used
+ *
+ */
+ protected final int F2(int D, int Kmi, int Kri)
+ {
+ int I = Kmi ^ D;
+ I = I << Kri | I >>> (32-Kri);
+ return ((S1[(I>>>24)&0xff]-S2[(I>>>16)&0xff])+S3[(I>>> 8)&0xff])^
+ S4[I & 0xff];
+ }
+
+ /**
+ * The third of the three processing functions for the
+ * encryption and decryption.
+ *
+ * @param D the input to be processed
+ * @param Kmi the mask to be used from Km[n]
+ * @param Kri the rotation value to be used
+ *
+ */
+ protected final int F3(int D, int Kmi, int Kri)
+ {
+ int I = Kmi - D;
+ I = I << Kri | I >>> (32-Kri);
+ return ((S1[(I>>>24)&0xff]+S2[(I>>>16)&0xff])^S3[(I>>> 8)&0xff])-
+ S4[I & 0xff];
+ }
+
+ /**
+ * Does the 16 rounds to encrypt the block.
+ *
+ * @param L0 the LH-32bits of the plaintext block
+ * @param R0 the RH-32bits of the plaintext block
+ */
+ protected final void CAST_Encipher(int L0, int R0, int result[])
+ {
+ int Lp = L0; // the previous value, equiv to L[i-1]
+ int Rp = R0; // equivalent to R[i-1]
+
+ /*
+ * numbering consistent with paper to make
+ * checking and validating easier
+ */
+ int Li = L0, Ri = R0;
+
+ for (int i = 1; i<=_rounds ; i++)
+ {
+ Lp = Li;
+ Rp = Ri;
+
+ Li = Rp;
+ switch (i)
+ {
+ case 1:
+ case 4:
+ case 7:
+ case 10:
+ case 13:
+ case 16:
+ Ri = Lp ^ F1(Rp, _Km[i], _Kr[i]);
+ break;
+ case 2:
+ case 5:
+ case 8:
+ case 11:
+ case 14:
+ Ri = Lp ^ F2(Rp, _Km[i], _Kr[i]);
+ break;
+ case 3:
+ case 6:
+ case 9:
+ case 12:
+ case 15:
+ Ri = Lp ^ F3(Rp, _Km[i], _Kr[i]);
+ break;
+ }
+ }
+
+ result[0] = Ri;
+ result[1] = Li;
+
+ return;
+ }
+
+ protected final void CAST_Decipher(int L16, int R16, int result[])
+ {
+ int Lp = L16; // the previous value, equiv to L[i-1]
+ int Rp = R16; // equivalent to R[i-1]
+
+ /*
+ * numbering consistent with paper to make
+ * checking and validating easier
+ */
+ int Li = L16, Ri = R16;
+
+ for (int i = _rounds; i > 0; i--)
+ {
+ Lp = Li;
+ Rp = Ri;
+
+ Li = Rp;
+ switch (i)
+ {
+ case 1:
+ case 4:
+ case 7:
+ case 10:
+ case 13:
+ case 16:
+ Ri = Lp ^ F1(Rp, _Km[i], _Kr[i]);
+ break;
+ case 2:
+ case 5:
+ case 8:
+ case 11:
+ case 14:
+ Ri = Lp ^ F2(Rp, _Km[i], _Kr[i]);
+ break;
+ case 3:
+ case 6:
+ case 9:
+ case 12:
+ case 15:
+ Ri = Lp ^ F3(Rp, _Km[i], _Kr[i]);
+ break;
+ }
+ }
+
+ result[0] = Ri;
+ result[1] = Li;
+
+ return;
+ }
+
+ protected final void Bits32ToInts(int in, int[] b, int offset)
+ {
+ b[offset + 3] = (in & 0xff);
+ b[offset + 2] = ((in >>> 8) & 0xff);
+ b[offset + 1] = ((in >>> 16) & 0xff);
+ b[offset] = ((in >>> 24) & 0xff);
+ }
+
+ protected final int IntsTo32bits(int[] b, int i)
+ {
+ int rv = 0;
+
+ rv = ((b[i] & 0xff) << 24) |
+ ((b[i+1] & 0xff) << 16) |
+ ((b[i+2] & 0xff) << 8) |
+ ((b[i+3] & 0xff));
+
+ return rv;
+ }
+
+ protected final void Bits32ToBytes(int in, byte[] b, int offset)
+ {
+ b[offset + 3] = (byte)in;
+ b[offset + 2] = (byte)(in >>> 8);
+ b[offset + 1] = (byte)(in >>> 16);
+ b[offset] = (byte)(in >>> 24);
+ }
+
+ protected final int BytesTo32bits(byte[] b, int i)
+ {
+ return ((b[i] & 0xff) << 24) |
+ ((b[i+1] & 0xff) << 16) |
+ ((b[i+2] & 0xff) << 8) |
+ ((b[i+3] & 0xff));
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/CAST6Engine.java b/core/src/main/java/org/bouncycastle/crypto/engines/CAST6Engine.java
new file mode 100644
index 00000000..db57b503
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/CAST6Engine.java
@@ -0,0 +1,296 @@
+package org.bouncycastle.crypto.engines;
+
+
+/**
+ * A class that provides CAST6 key encryption operations,
+ * such as encoding data and generating keys.
+ *
+ * All the algorithms herein are from the Internet RFC
+ *
+ * RFC2612 - CAST6 (128bit block, 128-256bit key)
+ *
+ * and implement a simplified cryptography interface.
+ */
+public final class CAST6Engine extends CAST5Engine
+{
+ //====================================
+ // Useful constants
+ //====================================
+
+ protected static final int ROUNDS = 12;
+
+ protected static final int BLOCK_SIZE = 16; // bytes = 128 bits
+
+ /*
+ * Put the round and mask keys into an array.
+ * Kr0[i] => _Kr[i*4 + 0]
+ */
+ protected int _Kr[] = new int[ROUNDS*4]; // the rotating round key(s)
+ protected int _Km[] = new int[ROUNDS*4]; // the masking round key(s)
+
+ /*
+ * Key setup
+ */
+ protected int _Tr[] = new int[24 * 8];
+ protected int _Tm[] = new int[24 * 8];
+
+ private int[] _workingKey = new int[8];
+
+ public CAST6Engine()
+ {
+ }
+
+ public String getAlgorithmName()
+ {
+ return "CAST6";
+ }
+
+ public void reset()
+ {
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ //==================================
+ // Private Implementation
+ //==================================
+
+ /*
+ * Creates the subkeys using the same nomenclature
+ * as described in RFC2612.
+ *
+ * See section 2.4
+ */
+ protected void setKey(byte[] key)
+ {
+ int Cm = 0x5a827999;
+ int Mm = 0x6ed9eba1;
+ int Cr = 19;
+ int Mr = 17;
+
+ /*
+ * Determine the key size here, if required
+ *
+ * if keysize < 256 bytes, pad with 0
+ *
+ * Typical key sizes => 128, 160, 192, 224, 256
+ */
+ for (int i=0; i< 24; i++)
+ {
+ for (int j=0; j< 8; j++)
+ {
+ _Tm[i*8 + j] = Cm;
+ Cm = (Cm + Mm); // mod 2^32;
+
+ _Tr[i*8 + j] = Cr;
+ Cr = (Cr + Mr) & 0x1f; // mod 32
+ }
+ }
+
+ byte[] tmpKey = new byte[64];
+ int length = key.length;
+ System.arraycopy(key, 0, tmpKey, 0, length);
+
+ // now create ABCDEFGH
+ for (int i=0; i< 8; i++)
+ {
+ _workingKey[i] = BytesTo32bits(tmpKey, i*4);
+ }
+
+ // Generate the key schedule
+ for (int i=0; i< 12; i++)
+ {
+ // KAPPA <- W2i(KAPPA)
+ int i2 = i*2 *8;
+ _workingKey[6] ^= F1(_workingKey[7], _Tm[i2 ], _Tr[i2 ]);
+ _workingKey[5] ^= F2(_workingKey[6], _Tm[i2+1], _Tr[i2+1]);
+ _workingKey[4] ^= F3(_workingKey[5], _Tm[i2+2], _Tr[i2+2]);
+ _workingKey[3] ^= F1(_workingKey[4], _Tm[i2+3], _Tr[i2+3]);
+ _workingKey[2] ^= F2(_workingKey[3], _Tm[i2+4], _Tr[i2+4]);
+ _workingKey[1] ^= F3(_workingKey[2], _Tm[i2+5], _Tr[i2+5]);
+ _workingKey[0] ^= F1(_workingKey[1], _Tm[i2+6], _Tr[i2+6]);
+ _workingKey[7] ^= F2(_workingKey[0], _Tm[i2+7], _Tr[i2+7]);
+
+ // KAPPA <- W2i+1(KAPPA)
+ i2 = (i*2 + 1)*8;
+ _workingKey[6] ^= F1(_workingKey[7], _Tm[i2 ], _Tr[i2 ]);
+ _workingKey[5] ^= F2(_workingKey[6], _Tm[i2+1], _Tr[i2+1]);
+ _workingKey[4] ^= F3(_workingKey[5], _Tm[i2+2], _Tr[i2+2]);
+ _workingKey[3] ^= F1(_workingKey[4], _Tm[i2+3], _Tr[i2+3]);
+ _workingKey[2] ^= F2(_workingKey[3], _Tm[i2+4], _Tr[i2+4]);
+ _workingKey[1] ^= F3(_workingKey[2], _Tm[i2+5], _Tr[i2+5]);
+ _workingKey[0] ^= F1(_workingKey[1], _Tm[i2+6], _Tr[i2+6]);
+ _workingKey[7] ^= F2(_workingKey[0], _Tm[i2+7], _Tr[i2+7]);
+
+ // Kr_(i) <- KAPPA
+ _Kr[i*4 ] = _workingKey[0] & 0x1f;
+ _Kr[i*4 + 1] = _workingKey[2] & 0x1f;
+ _Kr[i*4 + 2] = _workingKey[4] & 0x1f;
+ _Kr[i*4 + 3] = _workingKey[6] & 0x1f;
+
+
+ // Km_(i) <- KAPPA
+ _Km[i*4 ] = _workingKey[7];
+ _Km[i*4 + 1] = _workingKey[5];
+ _Km[i*4 + 2] = _workingKey[3];
+ _Km[i*4 + 3] = _workingKey[1];
+ }
+
+ }
+
+ /**
+ * Encrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ *
+ * @param src The plaintext buffer
+ * @param srcIndex An offset into src
+ * @param dst The ciphertext buffer
+ * @param dstIndex An offset into dst
+ */
+ protected int encryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+
+ int result[] = new int[4];
+
+ // process the input block
+ // batch the units up into 4x32 bit chunks and go for it
+
+ int A = BytesTo32bits(src, srcIndex);
+ int B = BytesTo32bits(src, srcIndex + 4);
+ int C = BytesTo32bits(src, srcIndex + 8);
+ int D = BytesTo32bits(src, srcIndex + 12);
+
+ CAST_Encipher(A, B, C, D, result);
+
+ // now stuff them into the destination block
+ Bits32ToBytes(result[0], dst, dstIndex);
+ Bits32ToBytes(result[1], dst, dstIndex + 4);
+ Bits32ToBytes(result[2], dst, dstIndex + 8);
+ Bits32ToBytes(result[3], dst, dstIndex + 12);
+
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * Decrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ *
+ * @param src The plaintext buffer
+ * @param srcIndex An offset into src
+ * @param dst The ciphertext buffer
+ * @param dstIndex An offset into dst
+ */
+ protected int decryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+ int result[] = new int[4];
+
+ // process the input block
+ // batch the units up into 4x32 bit chunks and go for it
+ int A = BytesTo32bits(src, srcIndex);
+ int B = BytesTo32bits(src, srcIndex + 4);
+ int C = BytesTo32bits(src, srcIndex + 8);
+ int D = BytesTo32bits(src, srcIndex + 12);
+
+ CAST_Decipher(A, B, C, D, result);
+
+ // now stuff them into the destination block
+ Bits32ToBytes(result[0], dst, dstIndex);
+ Bits32ToBytes(result[1], dst, dstIndex + 4);
+ Bits32ToBytes(result[2], dst, dstIndex + 8);
+ Bits32ToBytes(result[3], dst, dstIndex + 12);
+
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * Does the 12 quad rounds rounds to encrypt the block.
+ *
+ * @param A the 00-31 bits of the plaintext block
+ * @param B the 32-63 bits of the plaintext block
+ * @param C the 64-95 bits of the plaintext block
+ * @param D the 96-127 bits of the plaintext block
+ * @param result the resulting ciphertext
+ */
+ protected final void CAST_Encipher(int A, int B, int C, int D,int result[])
+ {
+ int x;
+ for (int i=0; i< 6; i++)
+ {
+ x = i*4;
+ // BETA <- Qi(BETA)
+ C ^= F1(D, _Km[x], _Kr[x]);
+ B ^= F2(C, _Km[x + 1], _Kr[x + 1]);
+ A ^= F3(B, _Km[x + 2], _Kr[x + 2]);
+ D ^= F1(A, _Km[x + 3], _Kr[x + 3]);
+
+ }
+
+ for (int i=6; i<12; i++)
+ {
+ x = i*4;
+ // BETA <- QBARi(BETA)
+ D ^= F1(A, _Km[x + 3], _Kr[x + 3]);
+ A ^= F3(B, _Km[x + 2], _Kr[x + 2]);
+ B ^= F2(C, _Km[x + 1], _Kr[x + 1]);
+ C ^= F1(D, _Km[x], _Kr[x]);
+
+ }
+
+ result[0] = A;
+ result[1] = B;
+ result[2] = C;
+ result[3] = D;
+ }
+
+ /**
+ * Does the 12 quad rounds rounds to decrypt the block.
+ *
+ * @param A the 00-31 bits of the ciphertext block
+ * @param B the 32-63 bits of the ciphertext block
+ * @param C the 64-95 bits of the ciphertext block
+ * @param D the 96-127 bits of the ciphertext block
+ * @param result the resulting plaintext
+ */
+ protected final void CAST_Decipher(int A, int B, int C, int D,int result[])
+ {
+ int x;
+ for (int i=0; i< 6; i++)
+ {
+ x = (11-i)*4;
+ // BETA <- Qi(BETA)
+ C ^= F1(D, _Km[x], _Kr[x]);
+ B ^= F2(C, _Km[x + 1], _Kr[x + 1]);
+ A ^= F3(B, _Km[x + 2], _Kr[x + 2]);
+ D ^= F1(A, _Km[x + 3], _Kr[x + 3]);
+
+ }
+
+ for (int i=6; i<12; i++)
+ {
+ x = (11-i)*4;
+ // BETA <- QBARi(BETA)
+ D ^= F1(A, _Km[x + 3], _Kr[x + 3]);
+ A ^= F3(B, _Km[x + 2], _Kr[x + 2]);
+ B ^= F2(C, _Km[x + 1], _Kr[x + 1]);
+ C ^= F1(D, _Km[x], _Kr[x]);
+
+ }
+
+ result[0] = A;
+ result[1] = B;
+ result[2] = C;
+ result[3] = D;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/CamelliaEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/CamelliaEngine.java
new file mode 100644
index 00000000..a486e1b5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/CamelliaEngine.java
@@ -0,0 +1,684 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * Camellia - based on RFC 3713.
+ */
+public class CamelliaEngine
+ implements BlockCipher
+{
+ private boolean initialised = false;
+ private boolean _keyIs128;
+
+ private static final int BLOCK_SIZE = 16;
+ private static final int MASK8 = 0xff;
+
+ private int[] subkey = new int[24 * 4];
+ private int[] kw = new int[4 * 2]; // for whitening
+ private int[] ke = new int[6 * 2]; // for FL and FL^(-1)
+ private int[] state = new int[4]; // for encryption and decryption
+
+ private static final int SIGMA[] = {
+ 0xa09e667f, 0x3bcc908b,
+ 0xb67ae858, 0x4caa73b2,
+ 0xc6ef372f, 0xe94f82be,
+ 0x54ff53a5, 0xf1d36f1c,
+ 0x10e527fa, 0xde682d1d,
+ 0xb05688c2, 0xb3e6c1fd
+ };
+
+ /*
+ *
+ * S-box data
+ *
+ */
+ private static final int SBOX1_1110[] = {
+ 0x70707000, 0x82828200, 0x2c2c2c00, 0xececec00, 0xb3b3b300, 0x27272700,
+ 0xc0c0c000, 0xe5e5e500, 0xe4e4e400, 0x85858500, 0x57575700, 0x35353500,
+ 0xeaeaea00, 0x0c0c0c00, 0xaeaeae00, 0x41414100, 0x23232300, 0xefefef00,
+ 0x6b6b6b00, 0x93939300, 0x45454500, 0x19191900, 0xa5a5a500, 0x21212100,
+ 0xededed00, 0x0e0e0e00, 0x4f4f4f00, 0x4e4e4e00, 0x1d1d1d00, 0x65656500,
+ 0x92929200, 0xbdbdbd00, 0x86868600, 0xb8b8b800, 0xafafaf00, 0x8f8f8f00,
+ 0x7c7c7c00, 0xebebeb00, 0x1f1f1f00, 0xcecece00, 0x3e3e3e00, 0x30303000,
+ 0xdcdcdc00, 0x5f5f5f00, 0x5e5e5e00, 0xc5c5c500, 0x0b0b0b00, 0x1a1a1a00,
+ 0xa6a6a600, 0xe1e1e100, 0x39393900, 0xcacaca00, 0xd5d5d500, 0x47474700,
+ 0x5d5d5d00, 0x3d3d3d00, 0xd9d9d900, 0x01010100, 0x5a5a5a00, 0xd6d6d600,
+ 0x51515100, 0x56565600, 0x6c6c6c00, 0x4d4d4d00, 0x8b8b8b00, 0x0d0d0d00,
+ 0x9a9a9a00, 0x66666600, 0xfbfbfb00, 0xcccccc00, 0xb0b0b000, 0x2d2d2d00,
+ 0x74747400, 0x12121200, 0x2b2b2b00, 0x20202000, 0xf0f0f000, 0xb1b1b100,
+ 0x84848400, 0x99999900, 0xdfdfdf00, 0x4c4c4c00, 0xcbcbcb00, 0xc2c2c200,
+ 0x34343400, 0x7e7e7e00, 0x76767600, 0x05050500, 0x6d6d6d00, 0xb7b7b700,
+ 0xa9a9a900, 0x31313100, 0xd1d1d100, 0x17171700, 0x04040400, 0xd7d7d700,
+ 0x14141400, 0x58585800, 0x3a3a3a00, 0x61616100, 0xdedede00, 0x1b1b1b00,
+ 0x11111100, 0x1c1c1c00, 0x32323200, 0x0f0f0f00, 0x9c9c9c00, 0x16161600,
+ 0x53535300, 0x18181800, 0xf2f2f200, 0x22222200, 0xfefefe00, 0x44444400,
+ 0xcfcfcf00, 0xb2b2b200, 0xc3c3c300, 0xb5b5b500, 0x7a7a7a00, 0x91919100,
+ 0x24242400, 0x08080800, 0xe8e8e800, 0xa8a8a800, 0x60606000, 0xfcfcfc00,
+ 0x69696900, 0x50505000, 0xaaaaaa00, 0xd0d0d000, 0xa0a0a000, 0x7d7d7d00,
+ 0xa1a1a100, 0x89898900, 0x62626200, 0x97979700, 0x54545400, 0x5b5b5b00,
+ 0x1e1e1e00, 0x95959500, 0xe0e0e000, 0xffffff00, 0x64646400, 0xd2d2d200,
+ 0x10101000, 0xc4c4c400, 0x00000000, 0x48484800, 0xa3a3a300, 0xf7f7f700,
+ 0x75757500, 0xdbdbdb00, 0x8a8a8a00, 0x03030300, 0xe6e6e600, 0xdadada00,
+ 0x09090900, 0x3f3f3f00, 0xdddddd00, 0x94949400, 0x87878700, 0x5c5c5c00,
+ 0x83838300, 0x02020200, 0xcdcdcd00, 0x4a4a4a00, 0x90909000, 0x33333300,
+ 0x73737300, 0x67676700, 0xf6f6f600, 0xf3f3f300, 0x9d9d9d00, 0x7f7f7f00,
+ 0xbfbfbf00, 0xe2e2e200, 0x52525200, 0x9b9b9b00, 0xd8d8d800, 0x26262600,
+ 0xc8c8c800, 0x37373700, 0xc6c6c600, 0x3b3b3b00, 0x81818100, 0x96969600,
+ 0x6f6f6f00, 0x4b4b4b00, 0x13131300, 0xbebebe00, 0x63636300, 0x2e2e2e00,
+ 0xe9e9e900, 0x79797900, 0xa7a7a700, 0x8c8c8c00, 0x9f9f9f00, 0x6e6e6e00,
+ 0xbcbcbc00, 0x8e8e8e00, 0x29292900, 0xf5f5f500, 0xf9f9f900, 0xb6b6b600,
+ 0x2f2f2f00, 0xfdfdfd00, 0xb4b4b400, 0x59595900, 0x78787800, 0x98989800,
+ 0x06060600, 0x6a6a6a00, 0xe7e7e700, 0x46464600, 0x71717100, 0xbababa00,
+ 0xd4d4d400, 0x25252500, 0xababab00, 0x42424200, 0x88888800, 0xa2a2a200,
+ 0x8d8d8d00, 0xfafafa00, 0x72727200, 0x07070700, 0xb9b9b900, 0x55555500,
+ 0xf8f8f800, 0xeeeeee00, 0xacacac00, 0x0a0a0a00, 0x36363600, 0x49494900,
+ 0x2a2a2a00, 0x68686800, 0x3c3c3c00, 0x38383800, 0xf1f1f100, 0xa4a4a400,
+ 0x40404000, 0x28282800, 0xd3d3d300, 0x7b7b7b00, 0xbbbbbb00, 0xc9c9c900,
+ 0x43434300, 0xc1c1c100, 0x15151500, 0xe3e3e300, 0xadadad00, 0xf4f4f400,
+ 0x77777700, 0xc7c7c700, 0x80808000, 0x9e9e9e00
+ };
+
+ private static final int SBOX4_4404[] = {
+ 0x70700070, 0x2c2c002c, 0xb3b300b3, 0xc0c000c0, 0xe4e400e4, 0x57570057,
+ 0xeaea00ea, 0xaeae00ae, 0x23230023, 0x6b6b006b, 0x45450045, 0xa5a500a5,
+ 0xeded00ed, 0x4f4f004f, 0x1d1d001d, 0x92920092, 0x86860086, 0xafaf00af,
+ 0x7c7c007c, 0x1f1f001f, 0x3e3e003e, 0xdcdc00dc, 0x5e5e005e, 0x0b0b000b,
+ 0xa6a600a6, 0x39390039, 0xd5d500d5, 0x5d5d005d, 0xd9d900d9, 0x5a5a005a,
+ 0x51510051, 0x6c6c006c, 0x8b8b008b, 0x9a9a009a, 0xfbfb00fb, 0xb0b000b0,
+ 0x74740074, 0x2b2b002b, 0xf0f000f0, 0x84840084, 0xdfdf00df, 0xcbcb00cb,
+ 0x34340034, 0x76760076, 0x6d6d006d, 0xa9a900a9, 0xd1d100d1, 0x04040004,
+ 0x14140014, 0x3a3a003a, 0xdede00de, 0x11110011, 0x32320032, 0x9c9c009c,
+ 0x53530053, 0xf2f200f2, 0xfefe00fe, 0xcfcf00cf, 0xc3c300c3, 0x7a7a007a,
+ 0x24240024, 0xe8e800e8, 0x60600060, 0x69690069, 0xaaaa00aa, 0xa0a000a0,
+ 0xa1a100a1, 0x62620062, 0x54540054, 0x1e1e001e, 0xe0e000e0, 0x64640064,
+ 0x10100010, 0x00000000, 0xa3a300a3, 0x75750075, 0x8a8a008a, 0xe6e600e6,
+ 0x09090009, 0xdddd00dd, 0x87870087, 0x83830083, 0xcdcd00cd, 0x90900090,
+ 0x73730073, 0xf6f600f6, 0x9d9d009d, 0xbfbf00bf, 0x52520052, 0xd8d800d8,
+ 0xc8c800c8, 0xc6c600c6, 0x81810081, 0x6f6f006f, 0x13130013, 0x63630063,
+ 0xe9e900e9, 0xa7a700a7, 0x9f9f009f, 0xbcbc00bc, 0x29290029, 0xf9f900f9,
+ 0x2f2f002f, 0xb4b400b4, 0x78780078, 0x06060006, 0xe7e700e7, 0x71710071,
+ 0xd4d400d4, 0xabab00ab, 0x88880088, 0x8d8d008d, 0x72720072, 0xb9b900b9,
+ 0xf8f800f8, 0xacac00ac, 0x36360036, 0x2a2a002a, 0x3c3c003c, 0xf1f100f1,
+ 0x40400040, 0xd3d300d3, 0xbbbb00bb, 0x43430043, 0x15150015, 0xadad00ad,
+ 0x77770077, 0x80800080, 0x82820082, 0xecec00ec, 0x27270027, 0xe5e500e5,
+ 0x85850085, 0x35350035, 0x0c0c000c, 0x41410041, 0xefef00ef, 0x93930093,
+ 0x19190019, 0x21210021, 0x0e0e000e, 0x4e4e004e, 0x65650065, 0xbdbd00bd,
+ 0xb8b800b8, 0x8f8f008f, 0xebeb00eb, 0xcece00ce, 0x30300030, 0x5f5f005f,
+ 0xc5c500c5, 0x1a1a001a, 0xe1e100e1, 0xcaca00ca, 0x47470047, 0x3d3d003d,
+ 0x01010001, 0xd6d600d6, 0x56560056, 0x4d4d004d, 0x0d0d000d, 0x66660066,
+ 0xcccc00cc, 0x2d2d002d, 0x12120012, 0x20200020, 0xb1b100b1, 0x99990099,
+ 0x4c4c004c, 0xc2c200c2, 0x7e7e007e, 0x05050005, 0xb7b700b7, 0x31310031,
+ 0x17170017, 0xd7d700d7, 0x58580058, 0x61610061, 0x1b1b001b, 0x1c1c001c,
+ 0x0f0f000f, 0x16160016, 0x18180018, 0x22220022, 0x44440044, 0xb2b200b2,
+ 0xb5b500b5, 0x91910091, 0x08080008, 0xa8a800a8, 0xfcfc00fc, 0x50500050,
+ 0xd0d000d0, 0x7d7d007d, 0x89890089, 0x97970097, 0x5b5b005b, 0x95950095,
+ 0xffff00ff, 0xd2d200d2, 0xc4c400c4, 0x48480048, 0xf7f700f7, 0xdbdb00db,
+ 0x03030003, 0xdada00da, 0x3f3f003f, 0x94940094, 0x5c5c005c, 0x02020002,
+ 0x4a4a004a, 0x33330033, 0x67670067, 0xf3f300f3, 0x7f7f007f, 0xe2e200e2,
+ 0x9b9b009b, 0x26260026, 0x37370037, 0x3b3b003b, 0x96960096, 0x4b4b004b,
+ 0xbebe00be, 0x2e2e002e, 0x79790079, 0x8c8c008c, 0x6e6e006e, 0x8e8e008e,
+ 0xf5f500f5, 0xb6b600b6, 0xfdfd00fd, 0x59590059, 0x98980098, 0x6a6a006a,
+ 0x46460046, 0xbaba00ba, 0x25250025, 0x42420042, 0xa2a200a2, 0xfafa00fa,
+ 0x07070007, 0x55550055, 0xeeee00ee, 0x0a0a000a, 0x49490049, 0x68680068,
+ 0x38380038, 0xa4a400a4, 0x28280028, 0x7b7b007b, 0xc9c900c9, 0xc1c100c1,
+ 0xe3e300e3, 0xf4f400f4, 0xc7c700c7, 0x9e9e009e
+ };
+
+ private static final int SBOX2_0222[] = {
+ 0x00e0e0e0, 0x00050505, 0x00585858, 0x00d9d9d9, 0x00676767, 0x004e4e4e,
+ 0x00818181, 0x00cbcbcb, 0x00c9c9c9, 0x000b0b0b, 0x00aeaeae, 0x006a6a6a,
+ 0x00d5d5d5, 0x00181818, 0x005d5d5d, 0x00828282, 0x00464646, 0x00dfdfdf,
+ 0x00d6d6d6, 0x00272727, 0x008a8a8a, 0x00323232, 0x004b4b4b, 0x00424242,
+ 0x00dbdbdb, 0x001c1c1c, 0x009e9e9e, 0x009c9c9c, 0x003a3a3a, 0x00cacaca,
+ 0x00252525, 0x007b7b7b, 0x000d0d0d, 0x00717171, 0x005f5f5f, 0x001f1f1f,
+ 0x00f8f8f8, 0x00d7d7d7, 0x003e3e3e, 0x009d9d9d, 0x007c7c7c, 0x00606060,
+ 0x00b9b9b9, 0x00bebebe, 0x00bcbcbc, 0x008b8b8b, 0x00161616, 0x00343434,
+ 0x004d4d4d, 0x00c3c3c3, 0x00727272, 0x00959595, 0x00ababab, 0x008e8e8e,
+ 0x00bababa, 0x007a7a7a, 0x00b3b3b3, 0x00020202, 0x00b4b4b4, 0x00adadad,
+ 0x00a2a2a2, 0x00acacac, 0x00d8d8d8, 0x009a9a9a, 0x00171717, 0x001a1a1a,
+ 0x00353535, 0x00cccccc, 0x00f7f7f7, 0x00999999, 0x00616161, 0x005a5a5a,
+ 0x00e8e8e8, 0x00242424, 0x00565656, 0x00404040, 0x00e1e1e1, 0x00636363,
+ 0x00090909, 0x00333333, 0x00bfbfbf, 0x00989898, 0x00979797, 0x00858585,
+ 0x00686868, 0x00fcfcfc, 0x00ececec, 0x000a0a0a, 0x00dadada, 0x006f6f6f,
+ 0x00535353, 0x00626262, 0x00a3a3a3, 0x002e2e2e, 0x00080808, 0x00afafaf,
+ 0x00282828, 0x00b0b0b0, 0x00747474, 0x00c2c2c2, 0x00bdbdbd, 0x00363636,
+ 0x00222222, 0x00383838, 0x00646464, 0x001e1e1e, 0x00393939, 0x002c2c2c,
+ 0x00a6a6a6, 0x00303030, 0x00e5e5e5, 0x00444444, 0x00fdfdfd, 0x00888888,
+ 0x009f9f9f, 0x00656565, 0x00878787, 0x006b6b6b, 0x00f4f4f4, 0x00232323,
+ 0x00484848, 0x00101010, 0x00d1d1d1, 0x00515151, 0x00c0c0c0, 0x00f9f9f9,
+ 0x00d2d2d2, 0x00a0a0a0, 0x00555555, 0x00a1a1a1, 0x00414141, 0x00fafafa,
+ 0x00434343, 0x00131313, 0x00c4c4c4, 0x002f2f2f, 0x00a8a8a8, 0x00b6b6b6,
+ 0x003c3c3c, 0x002b2b2b, 0x00c1c1c1, 0x00ffffff, 0x00c8c8c8, 0x00a5a5a5,
+ 0x00202020, 0x00898989, 0x00000000, 0x00909090, 0x00474747, 0x00efefef,
+ 0x00eaeaea, 0x00b7b7b7, 0x00151515, 0x00060606, 0x00cdcdcd, 0x00b5b5b5,
+ 0x00121212, 0x007e7e7e, 0x00bbbbbb, 0x00292929, 0x000f0f0f, 0x00b8b8b8,
+ 0x00070707, 0x00040404, 0x009b9b9b, 0x00949494, 0x00212121, 0x00666666,
+ 0x00e6e6e6, 0x00cecece, 0x00ededed, 0x00e7e7e7, 0x003b3b3b, 0x00fefefe,
+ 0x007f7f7f, 0x00c5c5c5, 0x00a4a4a4, 0x00373737, 0x00b1b1b1, 0x004c4c4c,
+ 0x00919191, 0x006e6e6e, 0x008d8d8d, 0x00767676, 0x00030303, 0x002d2d2d,
+ 0x00dedede, 0x00969696, 0x00262626, 0x007d7d7d, 0x00c6c6c6, 0x005c5c5c,
+ 0x00d3d3d3, 0x00f2f2f2, 0x004f4f4f, 0x00191919, 0x003f3f3f, 0x00dcdcdc,
+ 0x00797979, 0x001d1d1d, 0x00525252, 0x00ebebeb, 0x00f3f3f3, 0x006d6d6d,
+ 0x005e5e5e, 0x00fbfbfb, 0x00696969, 0x00b2b2b2, 0x00f0f0f0, 0x00313131,
+ 0x000c0c0c, 0x00d4d4d4, 0x00cfcfcf, 0x008c8c8c, 0x00e2e2e2, 0x00757575,
+ 0x00a9a9a9, 0x004a4a4a, 0x00575757, 0x00848484, 0x00111111, 0x00454545,
+ 0x001b1b1b, 0x00f5f5f5, 0x00e4e4e4, 0x000e0e0e, 0x00737373, 0x00aaaaaa,
+ 0x00f1f1f1, 0x00dddddd, 0x00595959, 0x00141414, 0x006c6c6c, 0x00929292,
+ 0x00545454, 0x00d0d0d0, 0x00787878, 0x00707070, 0x00e3e3e3, 0x00494949,
+ 0x00808080, 0x00505050, 0x00a7a7a7, 0x00f6f6f6, 0x00777777, 0x00939393,
+ 0x00868686, 0x00838383, 0x002a2a2a, 0x00c7c7c7, 0x005b5b5b, 0x00e9e9e9,
+ 0x00eeeeee, 0x008f8f8f, 0x00010101, 0x003d3d3d
+ };
+
+ private static final int SBOX3_3033[] = {
+ 0x38003838, 0x41004141, 0x16001616, 0x76007676, 0xd900d9d9, 0x93009393,
+ 0x60006060, 0xf200f2f2, 0x72007272, 0xc200c2c2, 0xab00abab, 0x9a009a9a,
+ 0x75007575, 0x06000606, 0x57005757, 0xa000a0a0, 0x91009191, 0xf700f7f7,
+ 0xb500b5b5, 0xc900c9c9, 0xa200a2a2, 0x8c008c8c, 0xd200d2d2, 0x90009090,
+ 0xf600f6f6, 0x07000707, 0xa700a7a7, 0x27002727, 0x8e008e8e, 0xb200b2b2,
+ 0x49004949, 0xde00dede, 0x43004343, 0x5c005c5c, 0xd700d7d7, 0xc700c7c7,
+ 0x3e003e3e, 0xf500f5f5, 0x8f008f8f, 0x67006767, 0x1f001f1f, 0x18001818,
+ 0x6e006e6e, 0xaf00afaf, 0x2f002f2f, 0xe200e2e2, 0x85008585, 0x0d000d0d,
+ 0x53005353, 0xf000f0f0, 0x9c009c9c, 0x65006565, 0xea00eaea, 0xa300a3a3,
+ 0xae00aeae, 0x9e009e9e, 0xec00ecec, 0x80008080, 0x2d002d2d, 0x6b006b6b,
+ 0xa800a8a8, 0x2b002b2b, 0x36003636, 0xa600a6a6, 0xc500c5c5, 0x86008686,
+ 0x4d004d4d, 0x33003333, 0xfd00fdfd, 0x66006666, 0x58005858, 0x96009696,
+ 0x3a003a3a, 0x09000909, 0x95009595, 0x10001010, 0x78007878, 0xd800d8d8,
+ 0x42004242, 0xcc00cccc, 0xef00efef, 0x26002626, 0xe500e5e5, 0x61006161,
+ 0x1a001a1a, 0x3f003f3f, 0x3b003b3b, 0x82008282, 0xb600b6b6, 0xdb00dbdb,
+ 0xd400d4d4, 0x98009898, 0xe800e8e8, 0x8b008b8b, 0x02000202, 0xeb00ebeb,
+ 0x0a000a0a, 0x2c002c2c, 0x1d001d1d, 0xb000b0b0, 0x6f006f6f, 0x8d008d8d,
+ 0x88008888, 0x0e000e0e, 0x19001919, 0x87008787, 0x4e004e4e, 0x0b000b0b,
+ 0xa900a9a9, 0x0c000c0c, 0x79007979, 0x11001111, 0x7f007f7f, 0x22002222,
+ 0xe700e7e7, 0x59005959, 0xe100e1e1, 0xda00dada, 0x3d003d3d, 0xc800c8c8,
+ 0x12001212, 0x04000404, 0x74007474, 0x54005454, 0x30003030, 0x7e007e7e,
+ 0xb400b4b4, 0x28002828, 0x55005555, 0x68006868, 0x50005050, 0xbe00bebe,
+ 0xd000d0d0, 0xc400c4c4, 0x31003131, 0xcb00cbcb, 0x2a002a2a, 0xad00adad,
+ 0x0f000f0f, 0xca00caca, 0x70007070, 0xff00ffff, 0x32003232, 0x69006969,
+ 0x08000808, 0x62006262, 0x00000000, 0x24002424, 0xd100d1d1, 0xfb00fbfb,
+ 0xba00baba, 0xed00eded, 0x45004545, 0x81008181, 0x73007373, 0x6d006d6d,
+ 0x84008484, 0x9f009f9f, 0xee00eeee, 0x4a004a4a, 0xc300c3c3, 0x2e002e2e,
+ 0xc100c1c1, 0x01000101, 0xe600e6e6, 0x25002525, 0x48004848, 0x99009999,
+ 0xb900b9b9, 0xb300b3b3, 0x7b007b7b, 0xf900f9f9, 0xce00cece, 0xbf00bfbf,
+ 0xdf00dfdf, 0x71007171, 0x29002929, 0xcd00cdcd, 0x6c006c6c, 0x13001313,
+ 0x64006464, 0x9b009b9b, 0x63006363, 0x9d009d9d, 0xc000c0c0, 0x4b004b4b,
+ 0xb700b7b7, 0xa500a5a5, 0x89008989, 0x5f005f5f, 0xb100b1b1, 0x17001717,
+ 0xf400f4f4, 0xbc00bcbc, 0xd300d3d3, 0x46004646, 0xcf00cfcf, 0x37003737,
+ 0x5e005e5e, 0x47004747, 0x94009494, 0xfa00fafa, 0xfc00fcfc, 0x5b005b5b,
+ 0x97009797, 0xfe00fefe, 0x5a005a5a, 0xac00acac, 0x3c003c3c, 0x4c004c4c,
+ 0x03000303, 0x35003535, 0xf300f3f3, 0x23002323, 0xb800b8b8, 0x5d005d5d,
+ 0x6a006a6a, 0x92009292, 0xd500d5d5, 0x21002121, 0x44004444, 0x51005151,
+ 0xc600c6c6, 0x7d007d7d, 0x39003939, 0x83008383, 0xdc00dcdc, 0xaa00aaaa,
+ 0x7c007c7c, 0x77007777, 0x56005656, 0x05000505, 0x1b001b1b, 0xa400a4a4,
+ 0x15001515, 0x34003434, 0x1e001e1e, 0x1c001c1c, 0xf800f8f8, 0x52005252,
+ 0x20002020, 0x14001414, 0xe900e9e9, 0xbd00bdbd, 0xdd00dddd, 0xe400e4e4,
+ 0xa100a1a1, 0xe000e0e0, 0x8a008a8a, 0xf100f1f1, 0xd600d6d6, 0x7a007a7a,
+ 0xbb00bbbb, 0xe300e3e3, 0x40004040, 0x4f004f4f
+ };
+
+ private static int rightRotate(int x, int s)
+ {
+ return (((x) >>> (s)) + ((x) << (32 - s)));
+ }
+
+ private static int leftRotate(int x, int s)
+ {
+ return ((x) << (s)) + ((x) >>> (32 - s));
+ }
+
+ private static void roldq(int rot, int[] ki, int ioff,
+ int[] ko, int ooff)
+ {
+ ko[0 + ooff] = (ki[0 + ioff] << rot) | (ki[1 + ioff] >>> (32 - rot));
+ ko[1 + ooff] = (ki[1 + ioff] << rot) | (ki[2 + ioff] >>> (32 - rot));
+ ko[2 + ooff] = (ki[2 + ioff] << rot) | (ki[3 + ioff] >>> (32 - rot));
+ ko[3 + ooff] = (ki[3 + ioff] << rot) | (ki[0 + ioff] >>> (32 - rot));
+ ki[0 + ioff] = ko[0 + ooff];
+ ki[1 + ioff] = ko[1 + ooff];
+ ki[2 + ioff] = ko[2 + ooff];
+ ki[3 + ioff] = ko[3 + ooff];
+ }
+
+ private static void decroldq(int rot, int[] ki, int ioff,
+ int[] ko, int ooff)
+ {
+ ko[2 + ooff] = (ki[0 + ioff] << rot) | (ki[1 + ioff] >>> (32 - rot));
+ ko[3 + ooff] = (ki[1 + ioff] << rot) | (ki[2 + ioff] >>> (32 - rot));
+ ko[0 + ooff] = (ki[2 + ioff] << rot) | (ki[3 + ioff] >>> (32 - rot));
+ ko[1 + ooff] = (ki[3 + ioff] << rot) | (ki[0 + ioff] >>> (32 - rot));
+ ki[0 + ioff] = ko[2 + ooff];
+ ki[1 + ioff] = ko[3 + ooff];
+ ki[2 + ioff] = ko[0 + ooff];
+ ki[3 + ioff] = ko[1 + ooff];
+ }
+
+ private static void roldqo32(int rot, int[] ki, int ioff,
+ int[] ko, int ooff)
+ {
+ ko[0 + ooff] = (ki[1 + ioff] << (rot - 32)) | (ki[2 + ioff] >>> (64 - rot));
+ ko[1 + ooff] = (ki[2 + ioff] << (rot - 32)) | (ki[3 + ioff] >>> (64 - rot));
+ ko[2 + ooff] = (ki[3 + ioff] << (rot - 32)) | (ki[0 + ioff] >>> (64 - rot));
+ ko[3 + ooff] = (ki[0 + ioff] << (rot - 32)) | (ki[1 + ioff] >>> (64 - rot));
+ ki[0 + ioff] = ko[0 + ooff];
+ ki[1 + ioff] = ko[1 + ooff];
+ ki[2 + ioff] = ko[2 + ooff];
+ ki[3 + ioff] = ko[3 + ooff];
+ }
+
+ private static void decroldqo32(int rot, int[] ki, int ioff,
+ int[] ko, int ooff)
+ {
+ ko[2 + ooff] = (ki[1 + ioff] << (rot - 32)) | (ki[2 + ioff] >>> (64 - rot));
+ ko[3 + ooff] = (ki[2 + ioff] << (rot - 32)) | (ki[3 + ioff] >>> (64 - rot));
+ ko[0 + ooff] = (ki[3 + ioff] << (rot - 32)) | (ki[0 + ioff] >>> (64 - rot));
+ ko[1 + ooff] = (ki[0 + ioff] << (rot - 32)) | (ki[1 + ioff] >>> (64 - rot));
+ ki[0 + ioff] = ko[2 + ooff];
+ ki[1 + ioff] = ko[3 + ooff];
+ ki[2 + ioff] = ko[0 + ooff];
+ ki[3 + ioff] = ko[1 + ooff];
+ }
+
+ private int bytes2int(byte[] src, int offset)
+ {
+ int word = 0;
+
+ for (int i = 0; i < 4; i++)
+ {
+ word = (word << 8) + (src[i + offset] & MASK8);
+ }
+ return word;
+ }
+
+ private void int2bytes(int word, byte[] dst, int offset)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ dst[(3 - i) + offset] = (byte)word;
+ word >>>= 8;
+ }
+ }
+
+ private void camelliaF2(int[] s, int[] skey, int keyoff)
+ {
+ int t1, t2, u, v;
+
+ t1 = s[0] ^ skey[0 + keyoff];
+ u = SBOX4_4404[t1 & MASK8];
+ u ^= SBOX3_3033[(t1 >>> 8) & MASK8];
+ u ^= SBOX2_0222[(t1 >>> 16) & MASK8];
+ u ^= SBOX1_1110[(t1 >>> 24) & MASK8];
+ t2 = s[1] ^ skey[1 + keyoff];
+ v = SBOX1_1110[t2 & MASK8];
+ v ^= SBOX4_4404[(t2 >>> 8) & MASK8];
+ v ^= SBOX3_3033[(t2 >>> 16) & MASK8];
+ v ^= SBOX2_0222[(t2 >>> 24) & MASK8];
+
+ s[2] ^= u ^ v;
+ s[3] ^= u ^ v ^ rightRotate(u, 8);
+
+ t1 = s[2] ^ skey[2 + keyoff];
+ u = SBOX4_4404[t1 & MASK8];
+ u ^= SBOX3_3033[(t1 >>> 8) & MASK8];
+ u ^= SBOX2_0222[(t1 >>> 16) & MASK8];
+ u ^= SBOX1_1110[(t1 >>> 24) & MASK8];
+ t2 = s[3] ^ skey[3 + keyoff];
+ v = SBOX1_1110[t2 & MASK8];
+ v ^= SBOX4_4404[(t2 >>> 8) & MASK8];
+ v ^= SBOX3_3033[(t2 >>> 16) & MASK8];
+ v ^= SBOX2_0222[(t2 >>> 24) & MASK8];
+
+ s[0] ^= u ^ v;
+ s[1] ^= u ^ v ^ rightRotate(u, 8);
+ }
+
+ private void camelliaFLs(int[] s, int[] fkey, int keyoff)
+ {
+
+ s[1] ^= leftRotate(s[0] & fkey[0 + keyoff], 1);
+ s[0] ^= fkey[1 + keyoff] | s[1];
+
+ s[2] ^= fkey[3 + keyoff] | s[3];
+ s[3] ^= leftRotate(fkey[2 + keyoff] & s[2], 1);
+ }
+
+ private void setKey(boolean forEncryption, byte[] key)
+ {
+ int[] k = new int[8];
+ int[] ka = new int[4];
+ int[] kb = new int[4];
+ int[] t = new int[4];
+
+ switch (key.length)
+ {
+ case 16:
+ _keyIs128 = true;
+ k[0] = bytes2int(key, 0);
+ k[1] = bytes2int(key, 4);
+ k[2] = bytes2int(key, 8);
+ k[3] = bytes2int(key, 12);
+ k[4] = k[5] = k[6] = k[7] = 0;
+ break;
+ case 24:
+ k[0] = bytes2int(key, 0);
+ k[1] = bytes2int(key, 4);
+ k[2] = bytes2int(key, 8);
+ k[3] = bytes2int(key, 12);
+ k[4] = bytes2int(key, 16);
+ k[5] = bytes2int(key, 20);
+ k[6] = ~k[4];
+ k[7] = ~k[5];
+ _keyIs128 = false;
+ break;
+ case 32:
+ k[0] = bytes2int(key, 0);
+ k[1] = bytes2int(key, 4);
+ k[2] = bytes2int(key, 8);
+ k[3] = bytes2int(key, 12);
+ k[4] = bytes2int(key, 16);
+ k[5] = bytes2int(key, 20);
+ k[6] = bytes2int(key, 24);
+ k[7] = bytes2int(key, 28);
+ _keyIs128 = false;
+ break;
+ default:
+ throw new
+ IllegalArgumentException("key sizes are only 16/24/32 bytes.");
+ }
+
+ for (int i = 0; i < 4; i++)
+ {
+ ka[i] = k[i] ^ k[i + 4];
+ }
+ /* compute KA */
+ camelliaF2(ka, SIGMA, 0);
+ for (int i = 0; i < 4; i++)
+ {
+ ka[i] ^= k[i];
+ }
+ camelliaF2(ka, SIGMA, 4);
+
+ if (_keyIs128)
+ {
+ if (forEncryption)
+ {
+ /* KL dependant keys */
+ kw[0] = k[0];
+ kw[1] = k[1];
+ kw[2] = k[2];
+ kw[3] = k[3];
+ roldq(15, k, 0, subkey, 4);
+ roldq(30, k, 0, subkey, 12);
+ roldq(15, k, 0, t, 0);
+ subkey[18] = t[2];
+ subkey[19] = t[3];
+ roldq(17, k, 0, ke, 4);
+ roldq(17, k, 0, subkey, 24);
+ roldq(17, k, 0, subkey, 32);
+ /* KA dependant keys */
+ subkey[0] = ka[0];
+ subkey[1] = ka[1];
+ subkey[2] = ka[2];
+ subkey[3] = ka[3];
+ roldq(15, ka, 0, subkey, 8);
+ roldq(15, ka, 0, ke, 0);
+ roldq(15, ka, 0, t, 0);
+ subkey[16] = t[0];
+ subkey[17] = t[1];
+ roldq(15, ka, 0, subkey, 20);
+ roldqo32(34, ka, 0, subkey, 28);
+ roldq(17, ka, 0, kw, 4);
+
+ }
+ else
+ { // decryption
+ /* KL dependant keys */
+ kw[4] = k[0];
+ kw[5] = k[1];
+ kw[6] = k[2];
+ kw[7] = k[3];
+ decroldq(15, k, 0, subkey, 28);
+ decroldq(30, k, 0, subkey, 20);
+ decroldq(15, k, 0, t, 0);
+ subkey[16] = t[0];
+ subkey[17] = t[1];
+ decroldq(17, k, 0, ke, 0);
+ decroldq(17, k, 0, subkey, 8);
+ decroldq(17, k, 0, subkey, 0);
+ /* KA dependant keys */
+ subkey[34] = ka[0];
+ subkey[35] = ka[1];
+ subkey[32] = ka[2];
+ subkey[33] = ka[3];
+ decroldq(15, ka, 0, subkey, 24);
+ decroldq(15, ka, 0, ke, 4);
+ decroldq(15, ka, 0, t, 0);
+ subkey[18] = t[2];
+ subkey[19] = t[3];
+ decroldq(15, ka, 0, subkey, 12);
+ decroldqo32(34, ka, 0, subkey, 4);
+ roldq(17, ka, 0, kw, 0);
+ }
+ }
+ else
+ { // 192bit or 256bit
+ /* compute KB */
+ for (int i = 0; i < 4; i++)
+ {
+ kb[i] = ka[i] ^ k[i + 4];
+ }
+ camelliaF2(kb, SIGMA, 8);
+
+ if (forEncryption)
+ {
+ /* KL dependant keys */
+ kw[0] = k[0];
+ kw[1] = k[1];
+ kw[2] = k[2];
+ kw[3] = k[3];
+ roldqo32(45, k, 0, subkey, 16);
+ roldq(15, k, 0, ke, 4);
+ roldq(17, k, 0, subkey, 32);
+ roldqo32(34, k, 0, subkey, 44);
+ /* KR dependant keys */
+ roldq(15, k, 4, subkey, 4);
+ roldq(15, k, 4, ke, 0);
+ roldq(30, k, 4, subkey, 24);
+ roldqo32(34, k, 4, subkey, 36);
+ /* KA dependant keys */
+ roldq(15, ka, 0, subkey, 8);
+ roldq(30, ka, 0, subkey, 20);
+ /* 32bit rotation */
+ ke[8] = ka[1];
+ ke[9] = ka[2];
+ ke[10] = ka[3];
+ ke[11] = ka[0];
+ roldqo32(49, ka, 0, subkey, 40);
+
+ /* KB dependant keys */
+ subkey[0] = kb[0];
+ subkey[1] = kb[1];
+ subkey[2] = kb[2];
+ subkey[3] = kb[3];
+ roldq(30, kb, 0, subkey, 12);
+ roldq(30, kb, 0, subkey, 28);
+ roldqo32(51, kb, 0, kw, 4);
+
+ }
+ else
+ { // decryption
+ /* KL dependant keys */
+ kw[4] = k[0];
+ kw[5] = k[1];
+ kw[6] = k[2];
+ kw[7] = k[3];
+ decroldqo32(45, k, 0, subkey, 28);
+ decroldq(15, k, 0, ke, 4);
+ decroldq(17, k, 0, subkey, 12);
+ decroldqo32(34, k, 0, subkey, 0);
+ /* KR dependant keys */
+ decroldq(15, k, 4, subkey, 40);
+ decroldq(15, k, 4, ke, 8);
+ decroldq(30, k, 4, subkey, 20);
+ decroldqo32(34, k, 4, subkey, 8);
+ /* KA dependant keys */
+ decroldq(15, ka, 0, subkey, 36);
+ decroldq(30, ka, 0, subkey, 24);
+ /* 32bit rotation */
+ ke[2] = ka[1];
+ ke[3] = ka[2];
+ ke[0] = ka[3];
+ ke[1] = ka[0];
+ decroldqo32(49, ka, 0, subkey, 4);
+
+ /* KB dependant keys */
+ subkey[46] = kb[0];
+ subkey[47] = kb[1];
+ subkey[44] = kb[2];
+ subkey[45] = kb[3];
+ decroldq(30, kb, 0, subkey, 32);
+ decroldq(30, kb, 0, subkey, 16);
+ roldqo32(51, kb, 0, kw, 0);
+ }
+ }
+ }
+
+ private int processBlock128(byte[] in, int inOff,
+ byte[] out, int outOff)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ state[i] = bytes2int(in, inOff + (i * 4));
+ state[i] ^= kw[i];
+ }
+
+ camelliaF2(state, subkey, 0);
+ camelliaF2(state, subkey, 4);
+ camelliaF2(state, subkey, 8);
+ camelliaFLs(state, ke, 0);
+ camelliaF2(state, subkey, 12);
+ camelliaF2(state, subkey, 16);
+ camelliaF2(state, subkey, 20);
+ camelliaFLs(state, ke, 4);
+ camelliaF2(state, subkey, 24);
+ camelliaF2(state, subkey, 28);
+ camelliaF2(state, subkey, 32);
+
+ state[2] ^= kw[4];
+ state[3] ^= kw[5];
+ state[0] ^= kw[6];
+ state[1] ^= kw[7];
+
+ int2bytes(state[2], out, outOff);
+ int2bytes(state[3], out, outOff + 4);
+ int2bytes(state[0], out, outOff + 8);
+ int2bytes(state[1], out, outOff + 12);
+
+ return BLOCK_SIZE;
+ }
+
+ private int processBlock192or256(byte[] in, int inOff,
+ byte[] out, int outOff)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ state[i] = bytes2int(in, inOff + (i * 4));
+ state[i] ^= kw[i];
+ }
+
+ camelliaF2(state, subkey, 0);
+ camelliaF2(state, subkey, 4);
+ camelliaF2(state, subkey, 8);
+ camelliaFLs(state, ke, 0);
+ camelliaF2(state, subkey, 12);
+ camelliaF2(state, subkey, 16);
+ camelliaF2(state, subkey, 20);
+ camelliaFLs(state, ke, 4);
+ camelliaF2(state, subkey, 24);
+ camelliaF2(state, subkey, 28);
+ camelliaF2(state, subkey, 32);
+ camelliaFLs(state, ke, 8);
+ camelliaF2(state, subkey, 36);
+ camelliaF2(state, subkey, 40);
+ camelliaF2(state, subkey, 44);
+
+ state[2] ^= kw[4];
+ state[3] ^= kw[5];
+ state[0] ^= kw[6];
+ state[1] ^= kw[7];
+
+ int2bytes(state[2], out, outOff);
+ int2bytes(state[3], out, outOff + 4);
+ int2bytes(state[0], out, outOff + 8);
+ int2bytes(state[1], out, outOff + 12);
+ return BLOCK_SIZE;
+ }
+
+ public CamelliaEngine()
+ {
+ }
+
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("only simple KeyParameter expected.");
+ }
+
+ setKey(forEncryption, ((KeyParameter)params).getKey());
+ initialised = true;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Camellia";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if (!initialised)
+ {
+ throw new IllegalStateException("Camellia engine not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (_keyIs128)
+ {
+ return processBlock128(in, inOff, out, outOff);
+ }
+ else
+ {
+ return processBlock192or256(in, inOff, out, outOff);
+ }
+ }
+
+ public void reset()
+ {
+ // nothing
+
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/CamelliaLightEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/CamelliaLightEngine.java
new file mode 100644
index 00000000..2b1e71b2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/CamelliaLightEngine.java
@@ -0,0 +1,592 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * Camellia - based on RFC 3713, smaller implementation, about half the size of CamelliaEngine.
+ */
+
+public class CamelliaLightEngine
+ implements BlockCipher
+{
+ private static final int BLOCK_SIZE = 16;
+ private static final int MASK8 = 0xff;
+ private boolean initialized;
+ private boolean _keyis128;
+
+ private int[] subkey = new int[24 * 4];
+ private int[] kw = new int[4 * 2]; // for whitening
+ private int[] ke = new int[6 * 2]; // for FL and FL^(-1)
+ private int[] state = new int[4]; // for encryption and decryption
+
+ private static final int SIGMA[] = {
+ 0xa09e667f, 0x3bcc908b,
+ 0xb67ae858, 0x4caa73b2,
+ 0xc6ef372f, 0xe94f82be,
+ 0x54ff53a5, 0xf1d36f1c,
+ 0x10e527fa, 0xde682d1d,
+ 0xb05688c2, 0xb3e6c1fd
+ };
+
+ /*
+ *
+ * S-box data
+ *
+ */
+ private static final byte SBOX1[] = {
+ (byte)112, (byte)130, (byte)44, (byte)236,
+ (byte)179, (byte)39, (byte)192, (byte)229,
+ (byte)228, (byte)133, (byte)87, (byte)53,
+ (byte)234, (byte)12, (byte)174, (byte)65,
+ (byte)35, (byte)239, (byte)107, (byte)147,
+ (byte)69, (byte)25, (byte)165, (byte)33,
+ (byte)237, (byte)14, (byte)79, (byte)78,
+ (byte)29, (byte)101, (byte)146, (byte)189,
+ (byte)134, (byte)184, (byte)175, (byte)143,
+ (byte)124, (byte)235, (byte)31, (byte)206,
+ (byte)62, (byte)48, (byte)220, (byte)95,
+ (byte)94, (byte)197, (byte)11, (byte)26,
+ (byte)166, (byte)225, (byte)57, (byte)202,
+ (byte)213, (byte)71, (byte)93, (byte)61,
+ (byte)217, (byte)1, (byte)90, (byte)214,
+ (byte)81, (byte)86, (byte)108, (byte)77,
+ (byte)139, (byte)13, (byte)154, (byte)102,
+ (byte)251, (byte)204, (byte)176, (byte)45,
+ (byte)116, (byte)18, (byte)43, (byte)32,
+ (byte)240, (byte)177, (byte)132, (byte)153,
+ (byte)223, (byte)76, (byte)203, (byte)194,
+ (byte)52, (byte)126, (byte)118, (byte)5,
+ (byte)109, (byte)183, (byte)169, (byte)49,
+ (byte)209, (byte)23, (byte)4, (byte)215,
+ (byte)20, (byte)88, (byte)58, (byte)97,
+ (byte)222, (byte)27, (byte)17, (byte)28,
+ (byte)50, (byte)15, (byte)156, (byte)22,
+ (byte)83, (byte)24, (byte)242, (byte)34,
+ (byte)254, (byte)68, (byte)207, (byte)178,
+ (byte)195, (byte)181, (byte)122, (byte)145,
+ (byte)36, (byte)8, (byte)232, (byte)168,
+ (byte)96, (byte)252, (byte)105, (byte)80,
+ (byte)170, (byte)208, (byte)160, (byte)125,
+ (byte)161, (byte)137, (byte)98, (byte)151,
+ (byte)84, (byte)91, (byte)30, (byte)149,
+ (byte)224, (byte)255, (byte)100, (byte)210,
+ (byte)16, (byte)196, (byte)0, (byte)72,
+ (byte)163, (byte)247, (byte)117, (byte)219,
+ (byte)138, (byte)3, (byte)230, (byte)218,
+ (byte)9, (byte)63, (byte)221, (byte)148,
+ (byte)135, (byte)92, (byte)131, (byte)2,
+ (byte)205, (byte)74, (byte)144, (byte)51,
+ (byte)115, (byte)103, (byte)246, (byte)243,
+ (byte)157, (byte)127, (byte)191, (byte)226,
+ (byte)82, (byte)155, (byte)216, (byte)38,
+ (byte)200, (byte)55, (byte)198, (byte)59,
+ (byte)129, (byte)150, (byte)111, (byte)75,
+ (byte)19, (byte)190, (byte)99, (byte)46,
+ (byte)233, (byte)121, (byte)167, (byte)140,
+ (byte)159, (byte)110, (byte)188, (byte)142,
+ (byte)41, (byte)245, (byte)249, (byte)182,
+ (byte)47, (byte)253, (byte)180, (byte)89,
+ (byte)120, (byte)152, (byte)6, (byte)106,
+ (byte)231, (byte)70, (byte)113, (byte)186,
+ (byte)212, (byte)37, (byte)171, (byte)66,
+ (byte)136, (byte)162, (byte)141, (byte)250,
+ (byte)114, (byte)7, (byte)185, (byte)85,
+ (byte)248, (byte)238, (byte)172, (byte)10,
+ (byte)54, (byte)73, (byte)42, (byte)104,
+ (byte)60, (byte)56, (byte)241, (byte)164,
+ (byte)64, (byte)40, (byte)211, (byte)123,
+ (byte)187, (byte)201, (byte)67, (byte)193,
+ (byte)21, (byte)227, (byte)173, (byte)244,
+ (byte)119, (byte)199, (byte)128, (byte)158
+ };
+
+ private static int rightRotate(int x, int s)
+ {
+ return (((x) >>> (s)) + ((x) << (32 - s)));
+ }
+
+ private static int leftRotate(int x, int s)
+ {
+ return ((x) << (s)) + ((x) >>> (32 - s));
+ }
+
+ private static void roldq(int rot, int[] ki, int ioff,
+ int[] ko, int ooff)
+ {
+ ko[0 + ooff] = (ki[0 + ioff] << rot) | (ki[1 + ioff] >>> (32 - rot));
+ ko[1 + ooff] = (ki[1 + ioff] << rot) | (ki[2 + ioff] >>> (32 - rot));
+ ko[2 + ooff] = (ki[2 + ioff] << rot) | (ki[3 + ioff] >>> (32 - rot));
+ ko[3 + ooff] = (ki[3 + ioff] << rot) | (ki[0 + ioff] >>> (32 - rot));
+ ki[0 + ioff] = ko[0 + ooff];
+ ki[1 + ioff] = ko[1 + ooff];
+ ki[2 + ioff] = ko[2 + ooff];
+ ki[3 + ioff] = ko[3 + ooff];
+ }
+
+ private static void decroldq(int rot, int[] ki, int ioff,
+ int[] ko, int ooff)
+ {
+ ko[2 + ooff] = (ki[0 + ioff] << rot) | (ki[1 + ioff] >>> (32 - rot));
+ ko[3 + ooff] = (ki[1 + ioff] << rot) | (ki[2 + ioff] >>> (32 - rot));
+ ko[0 + ooff] = (ki[2 + ioff] << rot) | (ki[3 + ioff] >>> (32 - rot));
+ ko[1 + ooff] = (ki[3 + ioff] << rot) | (ki[0 + ioff] >>> (32 - rot));
+ ki[0 + ioff] = ko[2 + ooff];
+ ki[1 + ioff] = ko[3 + ooff];
+ ki[2 + ioff] = ko[0 + ooff];
+ ki[3 + ioff] = ko[1 + ooff];
+ }
+
+ private static void roldqo32(int rot, int[] ki, int ioff,
+ int[] ko, int ooff)
+ {
+ ko[0 + ooff] = (ki[1 + ioff] << (rot - 32)) | (ki[2 + ioff] >>> (64 - rot));
+ ko[1 + ooff] = (ki[2 + ioff] << (rot - 32)) | (ki[3 + ioff] >>> (64 - rot));
+ ko[2 + ooff] = (ki[3 + ioff] << (rot - 32)) | (ki[0 + ioff] >>> (64 - rot));
+ ko[3 + ooff] = (ki[0 + ioff] << (rot - 32)) | (ki[1 + ioff] >>> (64 - rot));
+ ki[0 + ioff] = ko[0 + ooff];
+ ki[1 + ioff] = ko[1 + ooff];
+ ki[2 + ioff] = ko[2 + ooff];
+ ki[3 + ioff] = ko[3 + ooff];
+ }
+
+ private static void decroldqo32(int rot, int[] ki, int ioff,
+ int[] ko, int ooff)
+ {
+ ko[2 + ooff] = (ki[1 + ioff] << (rot - 32)) | (ki[2 + ioff] >>> (64 - rot));
+ ko[3 + ooff] = (ki[2 + ioff] << (rot - 32)) | (ki[3 + ioff] >>> (64 - rot));
+ ko[0 + ooff] = (ki[3 + ioff] << (rot - 32)) | (ki[0 + ioff] >>> (64 - rot));
+ ko[1 + ooff] = (ki[0 + ioff] << (rot - 32)) | (ki[1 + ioff] >>> (64 - rot));
+ ki[0 + ioff] = ko[2 + ooff];
+ ki[1 + ioff] = ko[3 + ooff];
+ ki[2 + ioff] = ko[0 + ooff];
+ ki[3 + ioff] = ko[1 + ooff];
+ }
+
+ private int bytes2int(byte[] src, int offset)
+ {
+ int word = 0;
+
+ for (int i = 0; i < 4; i++)
+ {
+ word = (word << 8) + (src[i + offset] & MASK8);
+ }
+ return word;
+ }
+
+ private void int2bytes(int word, byte[] dst, int offset)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ dst[(3 - i) + offset] = (byte)word;
+ word >>>= 8;
+ }
+ }
+
+ private byte lRot8(byte v, int rot)
+ {
+ return (byte)((v << rot) | ((v & 0xff) >>> (8 - rot)));
+ }
+
+ private int sbox2(int x)
+ {
+ return (lRot8(SBOX1[x], 1) & MASK8);
+ }
+
+ private int sbox3(int x)
+ {
+ return (lRot8(SBOX1[x], 7) & MASK8);
+ }
+
+ private int sbox4(int x)
+ {
+ return (SBOX1[((int)lRot8((byte)x, 1) & MASK8)] & MASK8);
+ }
+
+ private void camelliaF2(int[] s, int[] skey, int keyoff)
+ {
+ int t1, t2, u, v;
+
+ t1 = s[0] ^ skey[0 + keyoff];
+ u = sbox4((t1 & MASK8));
+ u |= (sbox3(((t1 >>> 8) & MASK8)) << 8);
+ u |= (sbox2(((t1 >>> 16) & MASK8)) << 16);
+ u |= ((int)(SBOX1[((t1 >>> 24) & MASK8)] & MASK8) << 24);
+
+ t2 = s[1] ^ skey[1 + keyoff];
+ v = (int)SBOX1[(t2 & MASK8)] & MASK8;
+ v |= (sbox4(((t2 >>> 8) & MASK8)) << 8);
+ v |= (sbox3(((t2 >>> 16) & MASK8)) << 16);
+ v |= (sbox2(((t2 >>> 24) & MASK8)) << 24);
+
+ v = leftRotate(v, 8);
+ u ^= v;
+ v = leftRotate(v, 8) ^ u;
+ u = rightRotate(u, 8) ^ v;
+ s[2] ^= leftRotate(v, 16) ^ u;
+ s[3] ^= leftRotate(u, 8);
+
+ t1 = s[2] ^ skey[2 + keyoff];
+ u = sbox4((t1 & MASK8));
+ u |= sbox3(((t1 >>> 8) & MASK8)) << 8;
+ u |= sbox2(((t1 >>> 16) & MASK8)) << 16;
+ u |= ((int)SBOX1[((t1 >>> 24) & MASK8)] & MASK8) << 24;
+
+ t2 = s[3] ^ skey[3 + keyoff];
+ v = ((int)SBOX1[(t2 & MASK8)] & MASK8);
+ v |= sbox4(((t2 >>> 8) & MASK8)) << 8;
+ v |= sbox3(((t2 >>> 16) & MASK8)) << 16;
+ v |= sbox2(((t2 >>> 24) & MASK8)) << 24;
+
+ v = leftRotate(v, 8);
+ u ^= v;
+ v = leftRotate(v, 8) ^ u;
+ u = rightRotate(u, 8) ^ v;
+ s[0] ^= leftRotate(v, 16) ^ u;
+ s[1] ^= leftRotate(u, 8);
+ }
+
+ private void camelliaFLs(int[] s, int[] fkey, int keyoff)
+ {
+
+ s[1] ^= leftRotate(s[0] & fkey[0 + keyoff], 1);
+ s[0] ^= fkey[1 + keyoff] | s[1];
+
+ s[2] ^= fkey[3 + keyoff] | s[3];
+ s[3] ^= leftRotate(fkey[2 + keyoff] & s[2], 1);
+ }
+
+ private void setKey(boolean forEncryption, byte[] key)
+ {
+ int[] k = new int[8];
+ int[] ka = new int[4];
+ int[] kb = new int[4];
+ int[] t = new int[4];
+
+ switch (key.length)
+ {
+ case 16:
+ _keyis128 = true;
+ k[0] = bytes2int(key, 0);
+ k[1] = bytes2int(key, 4);
+ k[2] = bytes2int(key, 8);
+ k[3] = bytes2int(key, 12);
+ k[4] = k[5] = k[6] = k[7] = 0;
+ break;
+ case 24:
+ k[0] = bytes2int(key, 0);
+ k[1] = bytes2int(key, 4);
+ k[2] = bytes2int(key, 8);
+ k[3] = bytes2int(key, 12);
+ k[4] = bytes2int(key, 16);
+ k[5] = bytes2int(key, 20);
+ k[6] = ~k[4];
+ k[7] = ~k[5];
+ _keyis128 = false;
+ break;
+ case 32:
+ k[0] = bytes2int(key, 0);
+ k[1] = bytes2int(key, 4);
+ k[2] = bytes2int(key, 8);
+ k[3] = bytes2int(key, 12);
+ k[4] = bytes2int(key, 16);
+ k[5] = bytes2int(key, 20);
+ k[6] = bytes2int(key, 24);
+ k[7] = bytes2int(key, 28);
+ _keyis128 = false;
+ break;
+ default:
+ throw new
+ IllegalArgumentException("key sizes are only 16/24/32 bytes.");
+ }
+
+ for (int i = 0; i < 4; i++)
+ {
+ ka[i] = k[i] ^ k[i + 4];
+ }
+ /* compute KA */
+ camelliaF2(ka, SIGMA, 0);
+ for (int i = 0; i < 4; i++)
+ {
+ ka[i] ^= k[i];
+ }
+ camelliaF2(ka, SIGMA, 4);
+
+ if (_keyis128)
+ {
+ if (forEncryption)
+ {
+ /* KL dependant keys */
+ kw[0] = k[0];
+ kw[1] = k[1];
+ kw[2] = k[2];
+ kw[3] = k[3];
+ roldq(15, k, 0, subkey, 4);
+ roldq(30, k, 0, subkey, 12);
+ roldq(15, k, 0, t, 0);
+ subkey[18] = t[2];
+ subkey[19] = t[3];
+ roldq(17, k, 0, ke, 4);
+ roldq(17, k, 0, subkey, 24);
+ roldq(17, k, 0, subkey, 32);
+ /* KA dependant keys */
+ subkey[0] = ka[0];
+ subkey[1] = ka[1];
+ subkey[2] = ka[2];
+ subkey[3] = ka[3];
+ roldq(15, ka, 0, subkey, 8);
+ roldq(15, ka, 0, ke, 0);
+ roldq(15, ka, 0, t, 0);
+ subkey[16] = t[0];
+ subkey[17] = t[1];
+ roldq(15, ka, 0, subkey, 20);
+ roldqo32(34, ka, 0, subkey, 28);
+ roldq(17, ka, 0, kw, 4);
+
+ }
+ else
+ { // decryption
+ /* KL dependant keys */
+ kw[4] = k[0];
+ kw[5] = k[1];
+ kw[6] = k[2];
+ kw[7] = k[3];
+ decroldq(15, k, 0, subkey, 28);
+ decroldq(30, k, 0, subkey, 20);
+ decroldq(15, k, 0, t, 0);
+ subkey[16] = t[0];
+ subkey[17] = t[1];
+ decroldq(17, k, 0, ke, 0);
+ decroldq(17, k, 0, subkey, 8);
+ decroldq(17, k, 0, subkey, 0);
+ /* KA dependant keys */
+ subkey[34] = ka[0];
+ subkey[35] = ka[1];
+ subkey[32] = ka[2];
+ subkey[33] = ka[3];
+ decroldq(15, ka, 0, subkey, 24);
+ decroldq(15, ka, 0, ke, 4);
+ decroldq(15, ka, 0, t, 0);
+ subkey[18] = t[2];
+ subkey[19] = t[3];
+ decroldq(15, ka, 0, subkey, 12);
+ decroldqo32(34, ka, 0, subkey, 4);
+ roldq(17, ka, 0, kw, 0);
+ }
+ }
+ else
+ { // 192bit or 256bit
+ /* compute KB */
+ for (int i = 0; i < 4; i++)
+ {
+ kb[i] = ka[i] ^ k[i + 4];
+ }
+ camelliaF2(kb, SIGMA, 8);
+
+ if (forEncryption)
+ {
+ /* KL dependant keys */
+ kw[0] = k[0];
+ kw[1] = k[1];
+ kw[2] = k[2];
+ kw[3] = k[3];
+ roldqo32(45, k, 0, subkey, 16);
+ roldq(15, k, 0, ke, 4);
+ roldq(17, k, 0, subkey, 32);
+ roldqo32(34, k, 0, subkey, 44);
+ /* KR dependant keys */
+ roldq(15, k, 4, subkey, 4);
+ roldq(15, k, 4, ke, 0);
+ roldq(30, k, 4, subkey, 24);
+ roldqo32(34, k, 4, subkey, 36);
+ /* KA dependant keys */
+ roldq(15, ka, 0, subkey, 8);
+ roldq(30, ka, 0, subkey, 20);
+ /* 32bit rotation */
+ ke[8] = ka[1];
+ ke[9] = ka[2];
+ ke[10] = ka[3];
+ ke[11] = ka[0];
+ roldqo32(49, ka, 0, subkey, 40);
+
+ /* KB dependant keys */
+ subkey[0] = kb[0];
+ subkey[1] = kb[1];
+ subkey[2] = kb[2];
+ subkey[3] = kb[3];
+ roldq(30, kb, 0, subkey, 12);
+ roldq(30, kb, 0, subkey, 28);
+ roldqo32(51, kb, 0, kw, 4);
+
+ }
+ else
+ { // decryption
+ /* KL dependant keys */
+ kw[4] = k[0];
+ kw[5] = k[1];
+ kw[6] = k[2];
+ kw[7] = k[3];
+ decroldqo32(45, k, 0, subkey, 28);
+ decroldq(15, k, 0, ke, 4);
+ decroldq(17, k, 0, subkey, 12);
+ decroldqo32(34, k, 0, subkey, 0);
+ /* KR dependant keys */
+ decroldq(15, k, 4, subkey, 40);
+ decroldq(15, k, 4, ke, 8);
+ decroldq(30, k, 4, subkey, 20);
+ decroldqo32(34, k, 4, subkey, 8);
+ /* KA dependant keys */
+ decroldq(15, ka, 0, subkey, 36);
+ decroldq(30, ka, 0, subkey, 24);
+ /* 32bit rotation */
+ ke[2] = ka[1];
+ ke[3] = ka[2];
+ ke[0] = ka[3];
+ ke[1] = ka[0];
+ decroldqo32(49, ka, 0, subkey, 4);
+
+ /* KB dependant keys */
+ subkey[46] = kb[0];
+ subkey[47] = kb[1];
+ subkey[44] = kb[2];
+ subkey[45] = kb[3];
+ decroldq(30, kb, 0, subkey, 32);
+ decroldq(30, kb, 0, subkey, 16);
+ roldqo32(51, kb, 0, kw, 0);
+ }
+ }
+ }
+
+ private int processBlock128(byte[] in, int inOff,
+ byte[] out, int outOff)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ state[i] = bytes2int(in, inOff + (i * 4));
+ state[i] ^= kw[i];
+ }
+
+ camelliaF2(state, subkey, 0);
+ camelliaF2(state, subkey, 4);
+ camelliaF2(state, subkey, 8);
+ camelliaFLs(state, ke, 0);
+ camelliaF2(state, subkey, 12);
+ camelliaF2(state, subkey, 16);
+ camelliaF2(state, subkey, 20);
+ camelliaFLs(state, ke, 4);
+ camelliaF2(state, subkey, 24);
+ camelliaF2(state, subkey, 28);
+ camelliaF2(state, subkey, 32);
+
+ state[2] ^= kw[4];
+ state[3] ^= kw[5];
+ state[0] ^= kw[6];
+ state[1] ^= kw[7];
+
+ int2bytes(state[2], out, outOff);
+ int2bytes(state[3], out, outOff + 4);
+ int2bytes(state[0], out, outOff + 8);
+ int2bytes(state[1], out, outOff + 12);
+
+ return BLOCK_SIZE;
+ }
+
+ private int processBlock192or256(byte[] in, int inOff,
+ byte[] out, int outOff)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ state[i] = bytes2int(in, inOff + (i * 4));
+ state[i] ^= kw[i];
+ }
+
+ camelliaF2(state, subkey, 0);
+ camelliaF2(state, subkey, 4);
+ camelliaF2(state, subkey, 8);
+ camelliaFLs(state, ke, 0);
+ camelliaF2(state, subkey, 12);
+ camelliaF2(state, subkey, 16);
+ camelliaF2(state, subkey, 20);
+ camelliaFLs(state, ke, 4);
+ camelliaF2(state, subkey, 24);
+ camelliaF2(state, subkey, 28);
+ camelliaF2(state, subkey, 32);
+ camelliaFLs(state, ke, 8);
+ camelliaF2(state, subkey, 36);
+ camelliaF2(state, subkey, 40);
+ camelliaF2(state, subkey, 44);
+
+ state[2] ^= kw[4];
+ state[3] ^= kw[5];
+ state[0] ^= kw[6];
+ state[1] ^= kw[7];
+
+ int2bytes(state[2], out, outOff);
+ int2bytes(state[3], out, outOff + 4);
+ int2bytes(state[0], out, outOff + 8);
+ int2bytes(state[1], out, outOff + 12);
+ return BLOCK_SIZE;
+ }
+
+ public CamelliaLightEngine()
+ {
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Camellia";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public void init(boolean forEncryption, CipherParameters params)
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("only simple KeyParameter expected.");
+ }
+
+ setKey(forEncryption, ((KeyParameter)params).getKey());
+ initialized = true;
+ }
+
+ public int processBlock(byte[] in, int inOff,
+ byte[] out, int outOff)
+ throws IllegalStateException
+ {
+
+ if (!initialized)
+ {
+ throw new IllegalStateException("Camellia is not initialized");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (_keyis128)
+ {
+ return processBlock128(in, inOff, out, outOff);
+ }
+ else
+ {
+ return processBlock192or256(in, inOff, out, outOff);
+ }
+ }
+
+ public void reset()
+ {
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/CamelliaWrapEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/CamelliaWrapEngine.java
new file mode 100644
index 00000000..5ca239a6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/CamelliaWrapEngine.java
@@ -0,0 +1,15 @@
+package org.bouncycastle.crypto.engines;
+
+/**
+ * An implementation of the Camellia key wrapper based on RFC 3657/RFC 3394.
+ * <p>
+ * For further details see: <a href="http://www.ietf.org/rfc/rfc3657.txt">http://www.ietf.org/rfc/rfc3657.txt</a>.
+ */
+public class CamelliaWrapEngine
+ extends RFC3394WrapEngine
+{
+ public CamelliaWrapEngine()
+ {
+ super(new CamelliaEngine());
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java
new file mode 100644
index 00000000..9b1e404e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java
@@ -0,0 +1,495 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * a class that provides a basic DES engine.
+ */
+public class DESEngine
+ implements BlockCipher
+{
+ protected static final int BLOCK_SIZE = 8;
+
+ private int[] workingKey = null;
+
+ /**
+ * standard constructor.
+ */
+ public DESEngine()
+ {
+ }
+
+ /**
+ * initialise a DES cipher.
+ *
+ * @param encrypting whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ if (((KeyParameter)params).getKey().length > 8)
+ {
+ throw new IllegalArgumentException("DES key too long - should be 8 bytes");
+ }
+
+ workingKey = generateWorkingKey(encrypting,
+ ((KeyParameter)params).getKey());
+
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to DES init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "DES";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new IllegalStateException("DES engine not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ desFunc(workingKey, in, inOff, out, outOff);
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+
+ /**
+ * what follows is mainly taken from "Applied Cryptography", by
+ * Bruce Schneier, however it also bears great resemblance to Richard
+ * Outerbridge's D3DES...
+ */
+
+// private static final short[] Df_Key =
+// {
+// 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,
+// 0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10,
+// 0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67
+// };
+
+ private static final short[] bytebit =
+ {
+ 0200, 0100, 040, 020, 010, 04, 02, 01
+ };
+
+ private static final int[] bigbyte =
+ {
+ 0x800000, 0x400000, 0x200000, 0x100000,
+ 0x80000, 0x40000, 0x20000, 0x10000,
+ 0x8000, 0x4000, 0x2000, 0x1000,
+ 0x800, 0x400, 0x200, 0x100,
+ 0x80, 0x40, 0x20, 0x10,
+ 0x8, 0x4, 0x2, 0x1
+ };
+
+ /*
+ * Use the key schedule specified in the Standard (ANSI X3.92-1981).
+ */
+
+ private static final byte[] pc1 =
+ {
+ 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17,
+ 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35,
+ 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21,
+ 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3
+ };
+
+ private static final byte[] totrot =
+ {
+ 1, 2, 4, 6, 8, 10, 12, 14,
+ 15, 17, 19, 21, 23, 25, 27, 28
+ };
+
+ private static final byte[] pc2 =
+ {
+ 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9,
+ 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1,
+ 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
+ 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31
+ };
+
+ private static final int[] SP1 = {
+ 0x01010400, 0x00000000, 0x00010000, 0x01010404,
+ 0x01010004, 0x00010404, 0x00000004, 0x00010000,
+ 0x00000400, 0x01010400, 0x01010404, 0x00000400,
+ 0x01000404, 0x01010004, 0x01000000, 0x00000004,
+ 0x00000404, 0x01000400, 0x01000400, 0x00010400,
+ 0x00010400, 0x01010000, 0x01010000, 0x01000404,
+ 0x00010004, 0x01000004, 0x01000004, 0x00010004,
+ 0x00000000, 0x00000404, 0x00010404, 0x01000000,
+ 0x00010000, 0x01010404, 0x00000004, 0x01010000,
+ 0x01010400, 0x01000000, 0x01000000, 0x00000400,
+ 0x01010004, 0x00010000, 0x00010400, 0x01000004,
+ 0x00000400, 0x00000004, 0x01000404, 0x00010404,
+ 0x01010404, 0x00010004, 0x01010000, 0x01000404,
+ 0x01000004, 0x00000404, 0x00010404, 0x01010400,
+ 0x00000404, 0x01000400, 0x01000400, 0x00000000,
+ 0x00010004, 0x00010400, 0x00000000, 0x01010004
+ };
+
+ private static final int[] SP2 = {
+ 0x80108020, 0x80008000, 0x00008000, 0x00108020,
+ 0x00100000, 0x00000020, 0x80100020, 0x80008020,
+ 0x80000020, 0x80108020, 0x80108000, 0x80000000,
+ 0x80008000, 0x00100000, 0x00000020, 0x80100020,
+ 0x00108000, 0x00100020, 0x80008020, 0x00000000,
+ 0x80000000, 0x00008000, 0x00108020, 0x80100000,
+ 0x00100020, 0x80000020, 0x00000000, 0x00108000,
+ 0x00008020, 0x80108000, 0x80100000, 0x00008020,
+ 0x00000000, 0x00108020, 0x80100020, 0x00100000,
+ 0x80008020, 0x80100000, 0x80108000, 0x00008000,
+ 0x80100000, 0x80008000, 0x00000020, 0x80108020,
+ 0x00108020, 0x00000020, 0x00008000, 0x80000000,
+ 0x00008020, 0x80108000, 0x00100000, 0x80000020,
+ 0x00100020, 0x80008020, 0x80000020, 0x00100020,
+ 0x00108000, 0x00000000, 0x80008000, 0x00008020,
+ 0x80000000, 0x80100020, 0x80108020, 0x00108000
+ };
+
+ private static final int[] SP3 = {
+ 0x00000208, 0x08020200, 0x00000000, 0x08020008,
+ 0x08000200, 0x00000000, 0x00020208, 0x08000200,
+ 0x00020008, 0x08000008, 0x08000008, 0x00020000,
+ 0x08020208, 0x00020008, 0x08020000, 0x00000208,
+ 0x08000000, 0x00000008, 0x08020200, 0x00000200,
+ 0x00020200, 0x08020000, 0x08020008, 0x00020208,
+ 0x08000208, 0x00020200, 0x00020000, 0x08000208,
+ 0x00000008, 0x08020208, 0x00000200, 0x08000000,
+ 0x08020200, 0x08000000, 0x00020008, 0x00000208,
+ 0x00020000, 0x08020200, 0x08000200, 0x00000000,
+ 0x00000200, 0x00020008, 0x08020208, 0x08000200,
+ 0x08000008, 0x00000200, 0x00000000, 0x08020008,
+ 0x08000208, 0x00020000, 0x08000000, 0x08020208,
+ 0x00000008, 0x00020208, 0x00020200, 0x08000008,
+ 0x08020000, 0x08000208, 0x00000208, 0x08020000,
+ 0x00020208, 0x00000008, 0x08020008, 0x00020200
+ };
+
+ private static final int[] SP4 = {
+ 0x00802001, 0x00002081, 0x00002081, 0x00000080,
+ 0x00802080, 0x00800081, 0x00800001, 0x00002001,
+ 0x00000000, 0x00802000, 0x00802000, 0x00802081,
+ 0x00000081, 0x00000000, 0x00800080, 0x00800001,
+ 0x00000001, 0x00002000, 0x00800000, 0x00802001,
+ 0x00000080, 0x00800000, 0x00002001, 0x00002080,
+ 0x00800081, 0x00000001, 0x00002080, 0x00800080,
+ 0x00002000, 0x00802080, 0x00802081, 0x00000081,
+ 0x00800080, 0x00800001, 0x00802000, 0x00802081,
+ 0x00000081, 0x00000000, 0x00000000, 0x00802000,
+ 0x00002080, 0x00800080, 0x00800081, 0x00000001,
+ 0x00802001, 0x00002081, 0x00002081, 0x00000080,
+ 0x00802081, 0x00000081, 0x00000001, 0x00002000,
+ 0x00800001, 0x00002001, 0x00802080, 0x00800081,
+ 0x00002001, 0x00002080, 0x00800000, 0x00802001,
+ 0x00000080, 0x00800000, 0x00002000, 0x00802080
+ };
+
+ private static final int[] SP5 = {
+ 0x00000100, 0x02080100, 0x02080000, 0x42000100,
+ 0x00080000, 0x00000100, 0x40000000, 0x02080000,
+ 0x40080100, 0x00080000, 0x02000100, 0x40080100,
+ 0x42000100, 0x42080000, 0x00080100, 0x40000000,
+ 0x02000000, 0x40080000, 0x40080000, 0x00000000,
+ 0x40000100, 0x42080100, 0x42080100, 0x02000100,
+ 0x42080000, 0x40000100, 0x00000000, 0x42000000,
+ 0x02080100, 0x02000000, 0x42000000, 0x00080100,
+ 0x00080000, 0x42000100, 0x00000100, 0x02000000,
+ 0x40000000, 0x02080000, 0x42000100, 0x40080100,
+ 0x02000100, 0x40000000, 0x42080000, 0x02080100,
+ 0x40080100, 0x00000100, 0x02000000, 0x42080000,
+ 0x42080100, 0x00080100, 0x42000000, 0x42080100,
+ 0x02080000, 0x00000000, 0x40080000, 0x42000000,
+ 0x00080100, 0x02000100, 0x40000100, 0x00080000,
+ 0x00000000, 0x40080000, 0x02080100, 0x40000100
+ };
+
+ private static final int[] SP6 = {
+ 0x20000010, 0x20400000, 0x00004000, 0x20404010,
+ 0x20400000, 0x00000010, 0x20404010, 0x00400000,
+ 0x20004000, 0x00404010, 0x00400000, 0x20000010,
+ 0x00400010, 0x20004000, 0x20000000, 0x00004010,
+ 0x00000000, 0x00400010, 0x20004010, 0x00004000,
+ 0x00404000, 0x20004010, 0x00000010, 0x20400010,
+ 0x20400010, 0x00000000, 0x00404010, 0x20404000,
+ 0x00004010, 0x00404000, 0x20404000, 0x20000000,
+ 0x20004000, 0x00000010, 0x20400010, 0x00404000,
+ 0x20404010, 0x00400000, 0x00004010, 0x20000010,
+ 0x00400000, 0x20004000, 0x20000000, 0x00004010,
+ 0x20000010, 0x20404010, 0x00404000, 0x20400000,
+ 0x00404010, 0x20404000, 0x00000000, 0x20400010,
+ 0x00000010, 0x00004000, 0x20400000, 0x00404010,
+ 0x00004000, 0x00400010, 0x20004010, 0x00000000,
+ 0x20404000, 0x20000000, 0x00400010, 0x20004010
+ };
+
+ private static final int[] SP7 = {
+ 0x00200000, 0x04200002, 0x04000802, 0x00000000,
+ 0x00000800, 0x04000802, 0x00200802, 0x04200800,
+ 0x04200802, 0x00200000, 0x00000000, 0x04000002,
+ 0x00000002, 0x04000000, 0x04200002, 0x00000802,
+ 0x04000800, 0x00200802, 0x00200002, 0x04000800,
+ 0x04000002, 0x04200000, 0x04200800, 0x00200002,
+ 0x04200000, 0x00000800, 0x00000802, 0x04200802,
+ 0x00200800, 0x00000002, 0x04000000, 0x00200800,
+ 0x04000000, 0x00200800, 0x00200000, 0x04000802,
+ 0x04000802, 0x04200002, 0x04200002, 0x00000002,
+ 0x00200002, 0x04000000, 0x04000800, 0x00200000,
+ 0x04200800, 0x00000802, 0x00200802, 0x04200800,
+ 0x00000802, 0x04000002, 0x04200802, 0x04200000,
+ 0x00200800, 0x00000000, 0x00000002, 0x04200802,
+ 0x00000000, 0x00200802, 0x04200000, 0x00000800,
+ 0x04000002, 0x04000800, 0x00000800, 0x00200002
+ };
+
+ private static final int[] SP8 = {
+ 0x10001040, 0x00001000, 0x00040000, 0x10041040,
+ 0x10000000, 0x10001040, 0x00000040, 0x10000000,
+ 0x00040040, 0x10040000, 0x10041040, 0x00041000,
+ 0x10041000, 0x00041040, 0x00001000, 0x00000040,
+ 0x10040000, 0x10000040, 0x10001000, 0x00001040,
+ 0x00041000, 0x00040040, 0x10040040, 0x10041000,
+ 0x00001040, 0x00000000, 0x00000000, 0x10040040,
+ 0x10000040, 0x10001000, 0x00041040, 0x00040000,
+ 0x00041040, 0x00040000, 0x10041000, 0x00001000,
+ 0x00000040, 0x10040040, 0x00001000, 0x00041040,
+ 0x10001000, 0x00000040, 0x10000040, 0x10040000,
+ 0x10040040, 0x10000000, 0x00040000, 0x10001040,
+ 0x00000000, 0x10041040, 0x00040040, 0x10000040,
+ 0x10040000, 0x10001000, 0x10001040, 0x00000000,
+ 0x10041040, 0x00041000, 0x00041000, 0x00001040,
+ 0x00001040, 0x00040040, 0x10000000, 0x10041000
+ };
+
+ /**
+ * generate an integer based working key based on our secret key
+ * and what we processing we are planning to do.
+ *
+ * Acknowledgements for this routine go to James Gillogly & Phil Karn.
+ * (whoever, and wherever they are!).
+ */
+ protected int[] generateWorkingKey(
+ boolean encrypting,
+ byte[] key)
+ {
+ int[] newKey = new int[32];
+ boolean[] pc1m = new boolean[56],
+ pcr = new boolean[56];
+
+ for (int j = 0; j < 56; j++)
+ {
+ int l = pc1[j];
+
+ pc1m[j] = ((key[l >>> 3] & bytebit[l & 07]) != 0);
+ }
+
+ for (int i = 0; i < 16; i++)
+ {
+ int l, m, n;
+
+ if (encrypting)
+ {
+ m = i << 1;
+ }
+ else
+ {
+ m = (15 - i) << 1;
+ }
+
+ n = m + 1;
+ newKey[m] = newKey[n] = 0;
+
+ for (int j = 0; j < 28; j++)
+ {
+ l = j + totrot[i];
+ if (l < 28)
+ {
+ pcr[j] = pc1m[l];
+ }
+ else
+ {
+ pcr[j] = pc1m[l - 28];
+ }
+ }
+
+ for (int j = 28; j < 56; j++)
+ {
+ l = j + totrot[i];
+ if (l < 56)
+ {
+ pcr[j] = pc1m[l];
+ }
+ else
+ {
+ pcr[j] = pc1m[l - 28];
+ }
+ }
+
+ for (int j = 0; j < 24; j++)
+ {
+ if (pcr[pc2[j]])
+ {
+ newKey[m] |= bigbyte[j];
+ }
+
+ if (pcr[pc2[j + 24]])
+ {
+ newKey[n] |= bigbyte[j];
+ }
+ }
+ }
+
+ //
+ // store the processed key
+ //
+ for (int i = 0; i != 32; i += 2)
+ {
+ int i1, i2;
+
+ i1 = newKey[i];
+ i2 = newKey[i + 1];
+
+ newKey[i] = ((i1 & 0x00fc0000) << 6) | ((i1 & 0x00000fc0) << 10)
+ | ((i2 & 0x00fc0000) >>> 10) | ((i2 & 0x00000fc0) >>> 6);
+
+ newKey[i + 1] = ((i1 & 0x0003f000) << 12) | ((i1 & 0x0000003f) << 16)
+ | ((i2 & 0x0003f000) >>> 4) | (i2 & 0x0000003f);
+ }
+
+ return newKey;
+ }
+
+ /**
+ * the DES engine.
+ */
+ protected void desFunc(
+ int[] wKey,
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int work, right, left;
+
+ left = (in[inOff + 0] & 0xff) << 24;
+ left |= (in[inOff + 1] & 0xff) << 16;
+ left |= (in[inOff + 2] & 0xff) << 8;
+ left |= (in[inOff + 3] & 0xff);
+
+ right = (in[inOff + 4] & 0xff) << 24;
+ right |= (in[inOff + 5] & 0xff) << 16;
+ right |= (in[inOff + 6] & 0xff) << 8;
+ right |= (in[inOff + 7] & 0xff);
+
+ work = ((left >>> 4) ^ right) & 0x0f0f0f0f;
+ right ^= work;
+ left ^= (work << 4);
+ work = ((left >>> 16) ^ right) & 0x0000ffff;
+ right ^= work;
+ left ^= (work << 16);
+ work = ((right >>> 2) ^ left) & 0x33333333;
+ left ^= work;
+ right ^= (work << 2);
+ work = ((right >>> 8) ^ left) & 0x00ff00ff;
+ left ^= work;
+ right ^= (work << 8);
+ right = ((right << 1) | ((right >>> 31) & 1)) & 0xffffffff;
+ work = (left ^ right) & 0xaaaaaaaa;
+ left ^= work;
+ right ^= work;
+ left = ((left << 1) | ((left >>> 31) & 1)) & 0xffffffff;
+
+ for (int round = 0; round < 8; round++)
+ {
+ int fval;
+
+ work = (right << 28) | (right >>> 4);
+ work ^= wKey[round * 4 + 0];
+ fval = SP7[ work & 0x3f];
+ fval |= SP5[(work >>> 8) & 0x3f];
+ fval |= SP3[(work >>> 16) & 0x3f];
+ fval |= SP1[(work >>> 24) & 0x3f];
+ work = right ^ wKey[round * 4 + 1];
+ fval |= SP8[ work & 0x3f];
+ fval |= SP6[(work >>> 8) & 0x3f];
+ fval |= SP4[(work >>> 16) & 0x3f];
+ fval |= SP2[(work >>> 24) & 0x3f];
+ left ^= fval;
+ work = (left << 28) | (left >>> 4);
+ work ^= wKey[round * 4 + 2];
+ fval = SP7[ work & 0x3f];
+ fval |= SP5[(work >>> 8) & 0x3f];
+ fval |= SP3[(work >>> 16) & 0x3f];
+ fval |= SP1[(work >>> 24) & 0x3f];
+ work = left ^ wKey[round * 4 + 3];
+ fval |= SP8[ work & 0x3f];
+ fval |= SP6[(work >>> 8) & 0x3f];
+ fval |= SP4[(work >>> 16) & 0x3f];
+ fval |= SP2[(work >>> 24) & 0x3f];
+ right ^= fval;
+ }
+
+ right = (right << 31) | (right >>> 1);
+ work = (left ^ right) & 0xaaaaaaaa;
+ left ^= work;
+ right ^= work;
+ left = (left << 31) | (left >>> 1);
+ work = ((left >>> 8) ^ right) & 0x00ff00ff;
+ right ^= work;
+ left ^= (work << 8);
+ work = ((left >>> 2) ^ right) & 0x33333333;
+ right ^= work;
+ left ^= (work << 2);
+ work = ((right >>> 16) ^ left) & 0x0000ffff;
+ left ^= work;
+ right ^= (work << 16);
+ work = ((right >>> 4) ^ left) & 0x0f0f0f0f;
+ left ^= work;
+ right ^= (work << 4);
+
+ out[outOff + 0] = (byte)((right >>> 24) & 0xff);
+ out[outOff + 1] = (byte)((right >>> 16) & 0xff);
+ out[outOff + 2] = (byte)((right >>> 8) & 0xff);
+ out[outOff + 3] = (byte)(right & 0xff);
+ out[outOff + 4] = (byte)((left >>> 24) & 0xff);
+ out[outOff + 5] = (byte)((left >>> 16) & 0xff);
+ out[outOff + 6] = (byte)((left >>> 8) & 0xff);
+ out[outOff + 7] = (byte)(left & 0xff);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/DESedeEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/DESedeEngine.java
new file mode 100644
index 00000000..513eccdf
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/DESedeEngine.java
@@ -0,0 +1,127 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * a class that provides a basic DESede (or Triple DES) engine.
+ */
+public class DESedeEngine
+ extends DESEngine
+{
+ protected static final int BLOCK_SIZE = 8;
+
+ private int[] workingKey1 = null;
+ private int[] workingKey2 = null;
+ private int[] workingKey3 = null;
+
+ private boolean forEncryption;
+
+ /**
+ * standard constructor.
+ */
+ public DESedeEngine()
+ {
+ }
+
+ /**
+ * initialise a DESede cipher.
+ *
+ * @param encrypting whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("invalid parameter passed to DESede init - " + params.getClass().getName());
+ }
+
+ byte[] keyMaster = ((KeyParameter)params).getKey();
+
+ if (keyMaster.length != 24 && keyMaster.length != 16)
+ {
+ throw new IllegalArgumentException("key size must be 16 or 24 bytes.");
+ }
+
+ this.forEncryption = encrypting;
+
+ byte[] key1 = new byte[8];
+ System.arraycopy(keyMaster, 0, key1, 0, key1.length);
+ workingKey1 = generateWorkingKey(encrypting, key1);
+
+ byte[] key2 = new byte[8];
+ System.arraycopy(keyMaster, 8, key2, 0, key2.length);
+ workingKey2 = generateWorkingKey(!encrypting, key2);
+
+ if (keyMaster.length == 24)
+ {
+ byte[] key3 = new byte[8];
+ System.arraycopy(keyMaster, 16, key3, 0, key3.length);
+ workingKey3 = generateWorkingKey(encrypting, key3);
+ }
+ else // 16 byte key
+ {
+ workingKey3 = workingKey1;
+ }
+ }
+
+ public String getAlgorithmName()
+ {
+ return "DESede";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (workingKey1 == null)
+ {
+ throw new IllegalStateException("DESede engine not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ byte[] temp = new byte[BLOCK_SIZE];
+
+ if (forEncryption)
+ {
+ desFunc(workingKey1, in, inOff, temp, 0);
+ desFunc(workingKey2, temp, 0, temp, 0);
+ desFunc(workingKey3, temp, 0, out, outOff);
+ }
+ else
+ {
+ desFunc(workingKey3, in, inOff, temp, 0);
+ desFunc(workingKey2, temp, 0, temp, 0);
+ desFunc(workingKey1, temp, 0, out, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
new file mode 100644
index 00000000..a3c72ccb
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
@@ -0,0 +1,348 @@
+package org.bouncycastle.crypto.engines;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.Wrapper;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Wrap keys according to
+ * <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-smime-key-wrap-01.txt">
+ * draft-ietf-smime-key-wrap-01.txt</A>.
+ * <p>
+ * Note:
+ * <ul>
+ * <li>this is based on a draft, and as such is subject to change - don't use this class for anything requiring long term storage.
+ * <li>if you are using this to wrap triple-des keys you need to set the
+ * parity bits on the key and, if it's a two-key triple-des key, pad it
+ * yourself.
+ * </ul>
+ */
+public class DESedeWrapEngine
+ implements Wrapper
+{
+ /** Field engine */
+ private CBCBlockCipher engine;
+
+ /** Field param */
+ private KeyParameter param;
+
+ /** Field paramPlusIV */
+ private ParametersWithIV paramPlusIV;
+
+ /** Field iv */
+ private byte[] iv;
+
+ /** Field forWrapping */
+ private boolean forWrapping;
+
+ /** Field IV2 */
+ private static final byte[] IV2 = { (byte) 0x4a, (byte) 0xdd, (byte) 0xa2,
+ (byte) 0x2c, (byte) 0x79, (byte) 0xe8,
+ (byte) 0x21, (byte) 0x05 };
+
+ //
+ // checksum digest
+ //
+ Digest sha1 = new SHA1Digest();
+ byte[] digest = new byte[20];
+
+ /**
+ * Method init
+ *
+ * @param forWrapping
+ * @param param
+ */
+ public void init(boolean forWrapping, CipherParameters param)
+ {
+
+ this.forWrapping = forWrapping;
+ this.engine = new CBCBlockCipher(new DESedeEngine());
+
+ SecureRandom sr;
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom pr = (ParametersWithRandom) param;
+ param = pr.getParameters();
+ sr = pr.getRandom();
+ }
+ else
+ {
+ sr = new SecureRandom();
+ }
+
+ if (param instanceof KeyParameter)
+ {
+ this.param = (KeyParameter)param;
+
+ if (this.forWrapping)
+ {
+
+ // Hm, we have no IV but we want to wrap ?!?
+ // well, then we have to create our own IV.
+ this.iv = new byte[8];
+ sr.nextBytes(iv);
+
+ this.paramPlusIV = new ParametersWithIV(this.param, this.iv);
+ }
+ }
+ else if (param instanceof ParametersWithIV)
+ {
+ this.paramPlusIV = (ParametersWithIV)param;
+ this.iv = this.paramPlusIV.getIV();
+ this.param = (KeyParameter)this.paramPlusIV.getParameters();
+
+ if (this.forWrapping)
+ {
+ if ((this.iv == null) || (this.iv.length != 8))
+ {
+ throw new IllegalArgumentException("IV is not 8 octets");
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "You should not supply an IV for unwrapping");
+ }
+ }
+ }
+
+ /**
+ * Method getAlgorithmName
+ *
+ * @return the algorithm name "DESede".
+ */
+ public String getAlgorithmName()
+ {
+ return "DESede";
+ }
+
+ /**
+ * Method wrap
+ *
+ * @param in
+ * @param inOff
+ * @param inLen
+ * @return the wrapped bytes.
+ */
+ public byte[] wrap(byte[] in, int inOff, int inLen)
+ {
+ if (!forWrapping)
+ {
+ throw new IllegalStateException("Not initialized for wrapping");
+ }
+
+ byte keyToBeWrapped[] = new byte[inLen];
+
+ System.arraycopy(in, inOff, keyToBeWrapped, 0, inLen);
+
+ // Compute the CMS Key Checksum, (section 5.6.1), call this CKS.
+ byte[] CKS = calculateCMSKeyChecksum(keyToBeWrapped);
+
+ // Let WKCKS = WK || CKS where || is concatenation.
+ byte[] WKCKS = new byte[keyToBeWrapped.length + CKS.length];
+
+ System.arraycopy(keyToBeWrapped, 0, WKCKS, 0, keyToBeWrapped.length);
+ System.arraycopy(CKS, 0, WKCKS, keyToBeWrapped.length, CKS.length);
+
+ // Encrypt WKCKS in CBC mode using KEK as the key and IV as the
+ // initialization vector. Call the results TEMP1.
+
+ int blockSize = engine.getBlockSize();
+
+ if (WKCKS.length % blockSize != 0)
+ {
+ throw new IllegalStateException("Not multiple of block length");
+ }
+
+ engine.init(true, paramPlusIV);
+
+ byte TEMP1[] = new byte[WKCKS.length];
+
+ for (int currentBytePos = 0; currentBytePos != WKCKS.length; currentBytePos += blockSize)
+ {
+ engine.processBlock(WKCKS, currentBytePos, TEMP1, currentBytePos);
+ }
+
+ // Let TEMP2 = IV || TEMP1.
+ byte[] TEMP2 = new byte[this.iv.length + TEMP1.length];
+
+ System.arraycopy(this.iv, 0, TEMP2, 0, this.iv.length);
+ System.arraycopy(TEMP1, 0, TEMP2, this.iv.length, TEMP1.length);
+
+ // Reverse the order of the octets in TEMP2 and call the result TEMP3.
+ byte[] TEMP3 = reverse(TEMP2);
+
+ // Encrypt TEMP3 in CBC mode using the KEK and an initialization vector
+ // of 0x 4a dd a2 2c 79 e8 21 05. The resulting cipher text is the desired
+ // result. It is 40 octets long if a 168 bit key is being wrapped.
+ ParametersWithIV param2 = new ParametersWithIV(this.param, IV2);
+
+ this.engine.init(true, param2);
+
+ for (int currentBytePos = 0; currentBytePos != TEMP3.length; currentBytePos += blockSize)
+ {
+ engine.processBlock(TEMP3, currentBytePos, TEMP3, currentBytePos);
+ }
+
+ return TEMP3;
+ }
+
+ /**
+ * Method unwrap
+ *
+ * @param in
+ * @param inOff
+ * @param inLen
+ * @return the unwrapped bytes.
+ * @throws InvalidCipherTextException
+ */
+ public byte[] unwrap(byte[] in, int inOff, int inLen)
+ throws InvalidCipherTextException
+ {
+ if (forWrapping)
+ {
+ throw new IllegalStateException("Not set for unwrapping");
+ }
+
+ if (in == null)
+ {
+ throw new InvalidCipherTextException("Null pointer as ciphertext");
+ }
+
+ final int blockSize = engine.getBlockSize();
+ if (inLen % blockSize != 0)
+ {
+ throw new InvalidCipherTextException("Ciphertext not multiple of " + blockSize);
+ }
+
+ /*
+ // Check if the length of the cipher text is reasonable given the key
+ // type. It must be 40 bytes for a 168 bit key and either 32, 40, or
+ // 48 bytes for a 128, 192, or 256 bit key. If the length is not supported
+ // or inconsistent with the algorithm for which the key is intended,
+ // return error.
+ //
+ // we do not accept 168 bit keys. it has to be 192 bit.
+ int lengthA = (estimatedKeyLengthInBit / 8) + 16;
+ int lengthB = estimatedKeyLengthInBit % 8;
+
+ if ((lengthA != keyToBeUnwrapped.length) || (lengthB != 0)) {
+ throw new XMLSecurityException("empty");
+ }
+ */
+
+ // Decrypt the cipher text with TRIPLedeS in CBC mode using the KEK
+ // and an initialization vector (IV) of 0x4adda22c79e82105. Call the output TEMP3.
+ ParametersWithIV param2 = new ParametersWithIV(this.param, IV2);
+
+ this.engine.init(false, param2);
+
+ byte TEMP3[] = new byte[inLen];
+
+ for (int currentBytePos = 0; currentBytePos != inLen; currentBytePos += blockSize)
+ {
+ engine.processBlock(in, inOff + currentBytePos, TEMP3, currentBytePos);
+ }
+
+ // Reverse the order of the octets in TEMP3 and call the result TEMP2.
+ byte[] TEMP2 = reverse(TEMP3);
+
+ // Decompose TEMP2 into IV, the first 8 octets, and TEMP1, the remaining octets.
+ this.iv = new byte[8];
+
+ byte[] TEMP1 = new byte[TEMP2.length - 8];
+
+ System.arraycopy(TEMP2, 0, this.iv, 0, 8);
+ System.arraycopy(TEMP2, 8, TEMP1, 0, TEMP2.length - 8);
+
+ // Decrypt TEMP1 using TRIPLedeS in CBC mode using the KEK and the IV
+ // found in the previous step. Call the result WKCKS.
+ this.paramPlusIV = new ParametersWithIV(this.param, this.iv);
+
+ this.engine.init(false, this.paramPlusIV);
+
+ byte[] WKCKS = new byte[TEMP1.length];
+
+ for (int currentBytePos = 0; currentBytePos != WKCKS.length; currentBytePos += blockSize)
+ {
+ engine.processBlock(TEMP1, currentBytePos, WKCKS, currentBytePos);
+ }
+
+ // Decompose WKCKS. CKS is the last 8 octets and WK, the wrapped key, are
+ // those octets before the CKS.
+ byte[] result = new byte[WKCKS.length - 8];
+ byte[] CKStoBeVerified = new byte[8];
+
+ System.arraycopy(WKCKS, 0, result, 0, WKCKS.length - 8);
+ System.arraycopy(WKCKS, WKCKS.length - 8, CKStoBeVerified, 0, 8);
+
+ // Calculate a CMS Key Checksum, (section 5.6.1), over the WK and compare
+ // with the CKS extracted in the above step. If they are not equal, return error.
+ if (!checkCMSKeyChecksum(result, CKStoBeVerified))
+ {
+ throw new InvalidCipherTextException(
+ "Checksum inside ciphertext is corrupted");
+ }
+
+ // WK is the wrapped key, now extracted for use in data decryption.
+ return result;
+ }
+
+ /**
+ * Some key wrap algorithms make use of the Key Checksum defined
+ * in CMS [CMS-Algorithms]. This is used to provide an integrity
+ * check value for the key being wrapped. The algorithm is
+ *
+ * - Compute the 20 octet SHA-1 hash on the key being wrapped.
+ * - Use the first 8 octets of this hash as the checksum value.
+ *
+ * @param key
+ * @return the CMS checksum.
+ * @throws RuntimeException
+ * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
+ */
+ private byte[] calculateCMSKeyChecksum(
+ byte[] key)
+ {
+ byte[] result = new byte[8];
+
+ sha1.update(key, 0, key.length);
+ sha1.doFinal(digest, 0);
+
+ System.arraycopy(digest, 0, result, 0, 8);
+
+ return result;
+ }
+
+ /**
+ * @param key
+ * @param checksum
+ * @return true if okay, false otherwise.
+ * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
+ */
+ private boolean checkCMSKeyChecksum(
+ byte[] key,
+ byte[] checksum)
+ {
+ return Arrays.constantTimeAreEqual(calculateCMSKeyChecksum(key), checksum);
+ }
+
+ private static byte[] reverse(byte[] bs)
+ {
+ byte[] result = new byte[bs.length];
+ for (int i = 0; i < bs.length; i++)
+ {
+ result[i] = bs[bs.length - (i + 1)];
+ }
+ return result;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ElGamalEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ElGamalEngine.java
new file mode 100644
index 00000000..4bf8e75d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/ElGamalEngine.java
@@ -0,0 +1,217 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.params.ElGamalKeyParameters;
+import org.bouncycastle.crypto.params.ElGamalPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ElGamalPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.util.BigIntegers;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/**
+ * this does your basic ElGamal algorithm.
+ */
+public class ElGamalEngine
+ implements AsymmetricBlockCipher
+{
+ private ElGamalKeyParameters key;
+ private SecureRandom random;
+ private boolean forEncryption;
+ private int bitSize;
+
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+ private static final BigInteger TWO = BigInteger.valueOf(2);
+
+ /**
+ * initialise the ElGamal engine.
+ *
+ * @param forEncryption true if we are encrypting, false otherwise.
+ * @param param the necessary ElGamal key parameters.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters param)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom p = (ParametersWithRandom)param;
+
+ this.key = (ElGamalKeyParameters)p.getParameters();
+ this.random = p.getRandom();
+ }
+ else
+ {
+ this.key = (ElGamalKeyParameters)param;
+ this.random = new SecureRandom();
+ }
+
+ this.forEncryption = forEncryption;
+
+ BigInteger p = key.getParameters().getP();
+
+ bitSize = p.bitLength();
+
+ if (forEncryption)
+ {
+ if (!(key instanceof ElGamalPublicKeyParameters))
+ {
+ throw new IllegalArgumentException("ElGamalPublicKeyParameters are required for encryption.");
+ }
+ }
+ else
+ {
+ if (!(key instanceof ElGamalPrivateKeyParameters))
+ {
+ throw new IllegalArgumentException("ElGamalPrivateKeyParameters are required for decryption.");
+ }
+ }
+ }
+
+ /**
+ * Return the maximum size for an input block to this engine.
+ * For ElGamal this is always one byte less than the size of P on
+ * encryption, and twice the length as the size of P on decryption.
+ *
+ * @return maximum size for an input block.
+ */
+ public int getInputBlockSize()
+ {
+ if (forEncryption)
+ {
+ return (bitSize - 1) / 8;
+ }
+
+ return 2 * ((bitSize + 7) / 8);
+ }
+
+ /**
+ * Return the maximum size for an output block to this engine.
+ * For ElGamal this is always one byte less than the size of P on
+ * decryption, and twice the length as the size of P on encryption.
+ *
+ * @return maximum size for an output block.
+ */
+ public int getOutputBlockSize()
+ {
+ if (forEncryption)
+ {
+ return 2 * ((bitSize + 7) / 8);
+ }
+
+ return (bitSize - 1) / 8;
+ }
+
+ /**
+ * Process a single block using the basic ElGamal algorithm.
+ *
+ * @param in the input array.
+ * @param inOff the offset into the input buffer where the data starts.
+ * @param inLen the length of the data to be processed.
+ * @return the result of the ElGamal process.
+ * @exception DataLengthException the input block is too large.
+ */
+ public byte[] processBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ {
+ if (key == null)
+ {
+ throw new IllegalStateException("ElGamal engine not initialised");
+ }
+
+ int maxLength = forEncryption
+ ? (bitSize - 1 + 7) / 8
+ : getInputBlockSize();
+
+ if (inLen > maxLength)
+ {
+ throw new DataLengthException("input too large for ElGamal cipher.\n");
+ }
+
+ BigInteger p = key.getParameters().getP();
+
+ if (key instanceof ElGamalPrivateKeyParameters) // decryption
+ {
+ byte[] in1 = new byte[inLen / 2];
+ byte[] in2 = new byte[inLen / 2];
+
+ System.arraycopy(in, inOff, in1, 0, in1.length);
+ System.arraycopy(in, inOff + in1.length, in2, 0, in2.length);
+
+ BigInteger gamma = new BigInteger(1, in1);
+ BigInteger phi = new BigInteger(1, in2);
+
+ ElGamalPrivateKeyParameters priv = (ElGamalPrivateKeyParameters)key;
+ // a shortcut, which generally relies on p being prime amongst other things.
+ // if a problem with this shows up, check the p and g values!
+ BigInteger m = gamma.modPow(p.subtract(ONE).subtract(priv.getX()), p).multiply(phi).mod(p);
+
+ return BigIntegers.asUnsignedByteArray(m);
+ }
+ else // encryption
+ {
+ byte[] block;
+ if (inOff != 0 || inLen != in.length)
+ {
+ block = new byte[inLen];
+
+ System.arraycopy(in, inOff, block, 0, inLen);
+ }
+ else
+ {
+ block = in;
+ }
+
+ BigInteger input = new BigInteger(1, block);
+
+ if (input.bitLength() >= p.bitLength())
+ {
+ throw new DataLengthException("input too large for ElGamal cipher.\n");
+ }
+
+ ElGamalPublicKeyParameters pub = (ElGamalPublicKeyParameters)key;
+
+ int pBitLength = p.bitLength();
+ BigInteger k = new BigInteger(pBitLength, random);
+
+ while (k.equals(ZERO) || (k.compareTo(p.subtract(TWO)) > 0))
+ {
+ k = new BigInteger(pBitLength, random);
+ }
+
+ BigInteger g = key.getParameters().getG();
+ BigInteger gamma = g.modPow(k, p);
+ BigInteger phi = input.multiply(pub.getY().modPow(k, p)).mod(p);
+
+ byte[] out1 = gamma.toByteArray();
+ byte[] out2 = phi.toByteArray();
+ byte[] output = new byte[this.getOutputBlockSize()];
+
+ if (out1.length > output.length / 2)
+ {
+ System.arraycopy(out1, 1, output, output.length / 2 - (out1.length - 1), out1.length - 1);
+ }
+ else
+ {
+ System.arraycopy(out1, 0, output, output.length / 2 - out1.length, out1.length);
+ }
+
+ if (out2.length > output.length / 2)
+ {
+ System.arraycopy(out2, 1, output, output.length - (out2.length - 1), out2.length - 1);
+ }
+ else
+ {
+ System.arraycopy(out2, 0, output, output.length - out2.length, out2.length);
+ }
+
+ return output;
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/GOST28147Engine.java b/core/src/main/java/org/bouncycastle/crypto/engines/GOST28147Engine.java
new file mode 100644
index 00000000..5a88b7fe
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/GOST28147Engine.java
@@ -0,0 +1,372 @@
+package org.bouncycastle.crypto.engines;
+
+import java.util.Hashtable;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithSBox;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+/**
+ * implementation of GOST 28147-89
+ */
+public class GOST28147Engine
+ implements BlockCipher
+{
+ protected static final int BLOCK_SIZE = 8;
+ private int[] workingKey = null;
+ private boolean forEncryption;
+
+ private byte[] S = Sbox_Default;
+
+ // these are the S-boxes given in Applied Cryptography 2nd Ed., p. 333
+ // This is default S-box!
+ private static byte Sbox_Default[] = {
+ 0x4,0xA,0x9,0x2,0xD,0x8,0x0,0xE,0x6,0xB,0x1,0xC,0x7,0xF,0x5,0x3,
+ 0xE,0xB,0x4,0xC,0x6,0xD,0xF,0xA,0x2,0x3,0x8,0x1,0x0,0x7,0x5,0x9,
+ 0x5,0x8,0x1,0xD,0xA,0x3,0x4,0x2,0xE,0xF,0xC,0x7,0x6,0x0,0x9,0xB,
+ 0x7,0xD,0xA,0x1,0x0,0x8,0x9,0xF,0xE,0x4,0x6,0xC,0xB,0x2,0x5,0x3,
+ 0x6,0xC,0x7,0x1,0x5,0xF,0xD,0x8,0x4,0xA,0x9,0xE,0x0,0x3,0xB,0x2,
+ 0x4,0xB,0xA,0x0,0x7,0x2,0x1,0xD,0x3,0x6,0x8,0x5,0x9,0xC,0xF,0xE,
+ 0xD,0xB,0x4,0x1,0x3,0xF,0x5,0x9,0x0,0xA,0xE,0x7,0x6,0x8,0x2,0xC,
+ 0x1,0xF,0xD,0x0,0x5,0x7,0xA,0x4,0x9,0x2,0x3,0xE,0x6,0xB,0x8,0xC
+ };
+
+ /*
+ * class content S-box parameters for encrypting
+ * getting from, see: http://tools.ietf.org/id/draft-popov-cryptopro-cpalgs-01.txt
+ * http://tools.ietf.org/id/draft-popov-cryptopro-cpalgs-02.txt
+ */
+ private static byte[] ESbox_Test = {
+ 0x4,0x2,0xF,0x5,0x9,0x1,0x0,0x8,0xE,0x3,0xB,0xC,0xD,0x7,0xA,0x6,
+ 0xC,0x9,0xF,0xE,0x8,0x1,0x3,0xA,0x2,0x7,0x4,0xD,0x6,0x0,0xB,0x5,
+ 0xD,0x8,0xE,0xC,0x7,0x3,0x9,0xA,0x1,0x5,0x2,0x4,0x6,0xF,0x0,0xB,
+ 0xE,0x9,0xB,0x2,0x5,0xF,0x7,0x1,0x0,0xD,0xC,0x6,0xA,0x4,0x3,0x8,
+ 0x3,0xE,0x5,0x9,0x6,0x8,0x0,0xD,0xA,0xB,0x7,0xC,0x2,0x1,0xF,0x4,
+ 0x8,0xF,0x6,0xB,0x1,0x9,0xC,0x5,0xD,0x3,0x7,0xA,0x0,0xE,0x2,0x4,
+ 0x9,0xB,0xC,0x0,0x3,0x6,0x7,0x5,0x4,0x8,0xE,0xF,0x1,0xA,0x2,0xD,
+ 0xC,0x6,0x5,0x2,0xB,0x0,0x9,0xD,0x3,0xE,0x7,0xA,0xF,0x4,0x1,0x8
+ };
+
+ private static byte[] ESbox_A = {
+ 0x9,0x6,0x3,0x2,0x8,0xB,0x1,0x7,0xA,0x4,0xE,0xF,0xC,0x0,0xD,0x5,
+ 0x3,0x7,0xE,0x9,0x8,0xA,0xF,0x0,0x5,0x2,0x6,0xC,0xB,0x4,0xD,0x1,
+ 0xE,0x4,0x6,0x2,0xB,0x3,0xD,0x8,0xC,0xF,0x5,0xA,0x0,0x7,0x1,0x9,
+ 0xE,0x7,0xA,0xC,0xD,0x1,0x3,0x9,0x0,0x2,0xB,0x4,0xF,0x8,0x5,0x6,
+ 0xB,0x5,0x1,0x9,0x8,0xD,0xF,0x0,0xE,0x4,0x2,0x3,0xC,0x7,0xA,0x6,
+ 0x3,0xA,0xD,0xC,0x1,0x2,0x0,0xB,0x7,0x5,0x9,0x4,0x8,0xF,0xE,0x6,
+ 0x1,0xD,0x2,0x9,0x7,0xA,0x6,0x0,0x8,0xC,0x4,0x5,0xF,0x3,0xB,0xE,
+ 0xB,0xA,0xF,0x5,0x0,0xC,0xE,0x8,0x6,0x2,0x3,0x9,0x1,0x7,0xD,0x4
+ };
+
+ private static byte[] ESbox_B = {
+ 0x8,0x4,0xB,0x1,0x3,0x5,0x0,0x9,0x2,0xE,0xA,0xC,0xD,0x6,0x7,0xF,
+ 0x0,0x1,0x2,0xA,0x4,0xD,0x5,0xC,0x9,0x7,0x3,0xF,0xB,0x8,0x6,0xE,
+ 0xE,0xC,0x0,0xA,0x9,0x2,0xD,0xB,0x7,0x5,0x8,0xF,0x3,0x6,0x1,0x4,
+ 0x7,0x5,0x0,0xD,0xB,0x6,0x1,0x2,0x3,0xA,0xC,0xF,0x4,0xE,0x9,0x8,
+ 0x2,0x7,0xC,0xF,0x9,0x5,0xA,0xB,0x1,0x4,0x0,0xD,0x6,0x8,0xE,0x3,
+ 0x8,0x3,0x2,0x6,0x4,0xD,0xE,0xB,0xC,0x1,0x7,0xF,0xA,0x0,0x9,0x5,
+ 0x5,0x2,0xA,0xB,0x9,0x1,0xC,0x3,0x7,0x4,0xD,0x0,0x6,0xF,0x8,0xE,
+ 0x0,0x4,0xB,0xE,0x8,0x3,0x7,0x1,0xA,0x2,0x9,0x6,0xF,0xD,0x5,0xC
+ };
+
+ private static byte[] ESbox_C = {
+ 0x1,0xB,0xC,0x2,0x9,0xD,0x0,0xF,0x4,0x5,0x8,0xE,0xA,0x7,0x6,0x3,
+ 0x0,0x1,0x7,0xD,0xB,0x4,0x5,0x2,0x8,0xE,0xF,0xC,0x9,0xA,0x6,0x3,
+ 0x8,0x2,0x5,0x0,0x4,0x9,0xF,0xA,0x3,0x7,0xC,0xD,0x6,0xE,0x1,0xB,
+ 0x3,0x6,0x0,0x1,0x5,0xD,0xA,0x8,0xB,0x2,0x9,0x7,0xE,0xF,0xC,0x4,
+ 0x8,0xD,0xB,0x0,0x4,0x5,0x1,0x2,0x9,0x3,0xC,0xE,0x6,0xF,0xA,0x7,
+ 0xC,0x9,0xB,0x1,0x8,0xE,0x2,0x4,0x7,0x3,0x6,0x5,0xA,0x0,0xF,0xD,
+ 0xA,0x9,0x6,0x8,0xD,0xE,0x2,0x0,0xF,0x3,0x5,0xB,0x4,0x1,0xC,0x7,
+ 0x7,0x4,0x0,0x5,0xA,0x2,0xF,0xE,0xC,0x6,0x1,0xB,0xD,0x9,0x3,0x8
+ };
+
+ private static byte[] ESbox_D = {
+ 0xF,0xC,0x2,0xA,0x6,0x4,0x5,0x0,0x7,0x9,0xE,0xD,0x1,0xB,0x8,0x3,
+ 0xB,0x6,0x3,0x4,0xC,0xF,0xE,0x2,0x7,0xD,0x8,0x0,0x5,0xA,0x9,0x1,
+ 0x1,0xC,0xB,0x0,0xF,0xE,0x6,0x5,0xA,0xD,0x4,0x8,0x9,0x3,0x7,0x2,
+ 0x1,0x5,0xE,0xC,0xA,0x7,0x0,0xD,0x6,0x2,0xB,0x4,0x9,0x3,0xF,0x8,
+ 0x0,0xC,0x8,0x9,0xD,0x2,0xA,0xB,0x7,0x3,0x6,0x5,0x4,0xE,0xF,0x1,
+ 0x8,0x0,0xF,0x3,0x2,0x5,0xE,0xB,0x1,0xA,0x4,0x7,0xC,0x9,0xD,0x6,
+ 0x3,0x0,0x6,0xF,0x1,0xE,0x9,0x2,0xD,0x8,0xC,0x4,0xB,0xA,0x5,0x7,
+ 0x1,0xA,0x6,0x8,0xF,0xB,0x0,0x4,0xC,0x3,0x5,0x9,0x7,0xD,0x2,0xE
+ };
+
+ //S-box for digest
+ private static byte DSbox_Test[] = {
+ 0x4,0xA,0x9,0x2,0xD,0x8,0x0,0xE,0x6,0xB,0x1,0xC,0x7,0xF,0x5,0x3,
+ 0xE,0xB,0x4,0xC,0x6,0xD,0xF,0xA,0x2,0x3,0x8,0x1,0x0,0x7,0x5,0x9,
+ 0x5,0x8,0x1,0xD,0xA,0x3,0x4,0x2,0xE,0xF,0xC,0x7,0x6,0x0,0x9,0xB,
+ 0x7,0xD,0xA,0x1,0x0,0x8,0x9,0xF,0xE,0x4,0x6,0xC,0xB,0x2,0x5,0x3,
+ 0x6,0xC,0x7,0x1,0x5,0xF,0xD,0x8,0x4,0xA,0x9,0xE,0x0,0x3,0xB,0x2,
+ 0x4,0xB,0xA,0x0,0x7,0x2,0x1,0xD,0x3,0x6,0x8,0x5,0x9,0xC,0xF,0xE,
+ 0xD,0xB,0x4,0x1,0x3,0xF,0x5,0x9,0x0,0xA,0xE,0x7,0x6,0x8,0x2,0xC,
+ 0x1,0xF,0xD,0x0,0x5,0x7,0xA,0x4,0x9,0x2,0x3,0xE,0x6,0xB,0x8,0xC
+ };
+
+ private static byte DSbox_A[] = {
+ 0xA,0x4,0x5,0x6,0x8,0x1,0x3,0x7,0xD,0xC,0xE,0x0,0x9,0x2,0xB,0xF,
+ 0x5,0xF,0x4,0x0,0x2,0xD,0xB,0x9,0x1,0x7,0x6,0x3,0xC,0xE,0xA,0x8,
+ 0x7,0xF,0xC,0xE,0x9,0x4,0x1,0x0,0x3,0xB,0x5,0x2,0x6,0xA,0x8,0xD,
+ 0x4,0xA,0x7,0xC,0x0,0xF,0x2,0x8,0xE,0x1,0x6,0x5,0xD,0xB,0x9,0x3,
+ 0x7,0x6,0x4,0xB,0x9,0xC,0x2,0xA,0x1,0x8,0x0,0xE,0xF,0xD,0x3,0x5,
+ 0x7,0x6,0x2,0x4,0xD,0x9,0xF,0x0,0xA,0x1,0x5,0xB,0x8,0xE,0xC,0x3,
+ 0xD,0xE,0x4,0x1,0x7,0x0,0x5,0xA,0x3,0xC,0x8,0xF,0x6,0x2,0x9,0xB,
+ 0x1,0x3,0xA,0x9,0x5,0xB,0x4,0xF,0x8,0x6,0x7,0xE,0xD,0x0,0x2,0xC
+ };
+
+ //
+ // pre-defined sbox table
+ //
+ private static Hashtable sBoxes = new Hashtable();
+
+ static
+ {
+ addSBox("Default", Sbox_Default);
+ addSBox("E-TEST", ESbox_Test);
+ addSBox("E-A", ESbox_A);
+ addSBox("E-B", ESbox_B);
+ addSBox("E-C", ESbox_C);
+ addSBox("E-D", ESbox_D);
+ addSBox("D-TEST", DSbox_Test);
+ addSBox("D-A", DSbox_A);
+ }
+
+ private static void addSBox(String sBoxName, byte[] sBox)
+ {
+ sBoxes.put(Strings.toUpperCase(sBoxName), sBox);
+ }
+
+ /**
+ * standard constructor.
+ */
+ public GOST28147Engine()
+ {
+ }
+
+ /**
+ * initialise an GOST28147 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (params instanceof ParametersWithSBox)
+ {
+ ParametersWithSBox param = (ParametersWithSBox)params;
+
+ //
+ // Set the S-Box
+ //
+ byte[] sBox = param.getSBox();
+ if (sBox.length != Sbox_Default.length)
+ {
+ throw new IllegalArgumentException("invalid S-box passed to GOST28147 init");
+ }
+ this.S = Arrays.clone(sBox);
+
+ //
+ // set key if there is one
+ //
+ if (param.getParameters() != null)
+ {
+ workingKey = generateWorkingKey(forEncryption,
+ ((KeyParameter)param.getParameters()).getKey());
+ }
+ }
+ else if (params instanceof KeyParameter)
+ {
+ workingKey = generateWorkingKey(forEncryption,
+ ((KeyParameter)params).getKey());
+ }
+ else if (params != null)
+ {
+ throw new IllegalArgumentException("invalid parameter passed to GOST28147 init - " + params.getClass().getName());
+ }
+ }
+
+ public String getAlgorithmName()
+ {
+ return "GOST28147";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new IllegalStateException("GOST28147 engine not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ GOST28147Func(workingKey, in, inOff, out, outOff);
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+
+ private int[] generateWorkingKey(
+ boolean forEncryption,
+ byte[] userKey)
+ {
+ this.forEncryption = forEncryption;
+
+ if (userKey.length != 32)
+ {
+ throw new IllegalArgumentException("Key length invalid. Key needs to be 32 byte - 256 bit!!!");
+ }
+
+ int key[] = new int[8];
+ for(int i=0; i!=8; i++)
+ {
+ key[i] = bytesToint(userKey,i*4);
+ }
+
+ return key;
+ }
+
+ private int GOST28147_mainStep(int n1, int key)
+ {
+ int cm = (key + n1); // CM1
+
+ // S-box replacing
+
+ int om = S[ 0 + ((cm >> (0 * 4)) & 0xF)] << (0 * 4);
+ om += S[ 16 + ((cm >> (1 * 4)) & 0xF)] << (1 * 4);
+ om += S[ 32 + ((cm >> (2 * 4)) & 0xF)] << (2 * 4);
+ om += S[ 48 + ((cm >> (3 * 4)) & 0xF)] << (3 * 4);
+ om += S[ 64 + ((cm >> (4 * 4)) & 0xF)] << (4 * 4);
+ om += S[ 80 + ((cm >> (5 * 4)) & 0xF)] << (5 * 4);
+ om += S[ 96 + ((cm >> (6 * 4)) & 0xF)] << (6 * 4);
+ om += S[112 + ((cm >> (7 * 4)) & 0xF)] << (7 * 4);
+
+ return om << 11 | om >>> (32-11); // 11-leftshift
+ }
+
+ private void GOST28147Func(
+ int[] workingKey,
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int N1, N2, tmp; //tmp -> for saving N1
+ N1 = bytesToint(in, inOff);
+ N2 = bytesToint(in, inOff + 4);
+
+ if (this.forEncryption)
+ {
+ for(int k = 0; k < 3; k++) // 1-24 steps
+ {
+ for(int j = 0; j < 8; j++)
+ {
+ tmp = N1;
+ N1 = N2 ^ GOST28147_mainStep(N1, workingKey[j]); // CM2
+ N2 = tmp;
+ }
+ }
+ for(int j = 7; j > 0; j--) // 25-31 steps
+ {
+ tmp = N1;
+ N1 = N2 ^ GOST28147_mainStep(N1, workingKey[j]); // CM2
+ N2 = tmp;
+ }
+ }
+ else //decrypt
+ {
+ for(int j = 0; j < 8; j++) // 1-8 steps
+ {
+ tmp = N1;
+ N1 = N2 ^ GOST28147_mainStep(N1, workingKey[j]); // CM2
+ N2 = tmp;
+ }
+ for(int k = 0; k < 3; k++) //9-31 steps
+ {
+ for(int j = 7; j >= 0; j--)
+ {
+ if ((k == 2) && (j==0))
+ {
+ break; // break 32 step
+ }
+ tmp = N1;
+ N1 = N2 ^ GOST28147_mainStep(N1, workingKey[j]); // CM2
+ N2 = tmp;
+ }
+ }
+ }
+
+ N2 = N2 ^ GOST28147_mainStep(N1, workingKey[0]); // 32 step (N1=N1)
+
+ intTobytes(N1, out, outOff);
+ intTobytes(N2, out, outOff + 4);
+ }
+
+ //array of bytes to type int
+ private int bytesToint(
+ byte[] in,
+ int inOff)
+ {
+ return ((in[inOff + 3] << 24) & 0xff000000) + ((in[inOff + 2] << 16) & 0xff0000) +
+ ((in[inOff + 1] << 8) & 0xff00) + (in[inOff] & 0xff);
+ }
+
+ //int to array of bytes
+ private void intTobytes(
+ int num,
+ byte[] out,
+ int outOff)
+ {
+ out[outOff + 3] = (byte)(num >>> 24);
+ out[outOff + 2] = (byte)(num >>> 16);
+ out[outOff + 1] = (byte)(num >>> 8);
+ out[outOff] = (byte)num;
+ }
+
+ /**
+ * Return the S-Box associated with SBoxName
+ * @param sBoxName name of the S-Box
+ * @return byte array representing the S-Box
+ */
+ public static byte[] getSBox(
+ String sBoxName)
+ {
+ byte[] sBox = (byte[])sBoxes.get(Strings.toUpperCase(sBoxName));
+
+ if (sBox == null)
+ {
+ throw new IllegalArgumentException("Unknown S-Box - possible types: "
+ + "\"Default\", \"E-Test\", \"E-A\", \"E-B\", \"E-C\", \"E-D\", \"D-Test\", \"D-A\".");
+ }
+
+ return Arrays.clone(sBox);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java b/core/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java
new file mode 100644
index 00000000..6b3da1c3
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java
@@ -0,0 +1,303 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+
+/**
+ * Implementation of Martin Hell's, Thomas Johansson's and Willi Meier's stream
+ * cipher, Grain-128.
+ */
+public class Grain128Engine
+ implements StreamCipher
+{
+
+ /**
+ * Constants
+ */
+ private static final int STATE_SIZE = 4;
+
+ /**
+ * Variables to hold the state of the engine during encryption and
+ * decryption
+ */
+ private byte[] workingKey;
+ private byte[] workingIV;
+ private byte[] out;
+ private int[] lfsr;
+ private int[] nfsr;
+ private int output;
+ private int index = 4;
+
+ private boolean initialised = false;
+
+ public String getAlgorithmName()
+ {
+ return "Grain-128";
+ }
+
+ /**
+ * Initialize a Grain-128 cipher.
+ *
+ * @param forEncryption Whether or not we are for encryption.
+ * @param params The parameters required to set up the cipher.
+ * @throws IllegalArgumentException If the params argument is inappropriate.
+ */
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ /**
+ * Grain encryption and decryption is completely symmetrical, so the
+ * 'forEncryption' is irrelevant.
+ */
+ if (!(params instanceof ParametersWithIV))
+ {
+ throw new IllegalArgumentException(
+ "Grain-128 Init parameters must include an IV");
+ }
+
+ ParametersWithIV ivParams = (ParametersWithIV)params;
+
+ byte[] iv = ivParams.getIV();
+
+ if (iv == null || iv.length != 12)
+ {
+ throw new IllegalArgumentException(
+ "Grain-128 requires exactly 12 bytes of IV");
+ }
+
+ if (!(ivParams.getParameters() instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException(
+ "Grain-128 Init parameters must include a key");
+ }
+
+ KeyParameter key = (KeyParameter)ivParams.getParameters();
+
+ /**
+ * Initialize variables.
+ */
+ workingIV = new byte[key.getKey().length];
+ workingKey = new byte[key.getKey().length];
+ lfsr = new int[STATE_SIZE];
+ nfsr = new int[STATE_SIZE];
+ out = new byte[4];
+
+ System.arraycopy(iv, 0, workingIV, 0, iv.length);
+ System.arraycopy(key.getKey(), 0, workingKey, 0, key.getKey().length);
+
+ setKey(workingKey, workingIV);
+ initGrain();
+ }
+
+ /**
+ * 256 clocks initialization phase.
+ */
+ private void initGrain()
+ {
+ for (int i = 0; i < 8; i++)
+ {
+ output = getOutput();
+ nfsr = shift(nfsr, getOutputNFSR() ^ lfsr[0] ^ output);
+ lfsr = shift(lfsr, getOutputLFSR() ^ output);
+ }
+ initialised = true;
+ }
+
+ /**
+ * Get output from non-linear function g(x).
+ *
+ * @return Output from NFSR.
+ */
+ private int getOutputNFSR()
+ {
+ int b0 = nfsr[0];
+ int b3 = nfsr[0] >>> 3 | nfsr[1] << 29;
+ int b11 = nfsr[0] >>> 11 | nfsr[1] << 21;
+ int b13 = nfsr[0] >>> 13 | nfsr[1] << 19;
+ int b17 = nfsr[0] >>> 17 | nfsr[1] << 15;
+ int b18 = nfsr[0] >>> 18 | nfsr[1] << 14;
+ int b26 = nfsr[0] >>> 26 | nfsr[1] << 6;
+ int b27 = nfsr[0] >>> 27 | nfsr[1] << 5;
+ int b40 = nfsr[1] >>> 8 | nfsr[2] << 24;
+ int b48 = nfsr[1] >>> 16 | nfsr[2] << 16;
+ int b56 = nfsr[1] >>> 24 | nfsr[2] << 8;
+ int b59 = nfsr[1] >>> 27 | nfsr[2] << 5;
+ int b61 = nfsr[1] >>> 29 | nfsr[2] << 3;
+ int b65 = nfsr[2] >>> 1 | nfsr[3] << 31;
+ int b67 = nfsr[2] >>> 3 | nfsr[3] << 29;
+ int b68 = nfsr[2] >>> 4 | nfsr[3] << 28;
+ int b84 = nfsr[2] >>> 20 | nfsr[3] << 12;
+ int b91 = nfsr[2] >>> 27 | nfsr[3] << 5;
+ int b96 = nfsr[3];
+
+ return b0 ^ b26 ^ b56 ^ b91 ^ b96 ^ b3 & b67 ^ b11 & b13 ^ b17 & b18
+ ^ b27 & b59 ^ b40 & b48 ^ b61 & b65 ^ b68 & b84;
+ }
+
+ /**
+ * Get output from linear function f(x).
+ *
+ * @return Output from LFSR.
+ */
+ private int getOutputLFSR()
+ {
+ int s0 = lfsr[0];
+ int s7 = lfsr[0] >>> 7 | lfsr[1] << 25;
+ int s38 = lfsr[1] >>> 6 | lfsr[2] << 26;
+ int s70 = lfsr[2] >>> 6 | lfsr[3] << 26;
+ int s81 = lfsr[2] >>> 17 | lfsr[3] << 15;
+ int s96 = lfsr[3];
+
+ return s0 ^ s7 ^ s38 ^ s70 ^ s81 ^ s96;
+ }
+
+ /**
+ * Get output from output function h(x).
+ *
+ * @return Output from h(x).
+ */
+ private int getOutput()
+ {
+ int b2 = nfsr[0] >>> 2 | nfsr[1] << 30;
+ int b12 = nfsr[0] >>> 12 | nfsr[1] << 20;
+ int b15 = nfsr[0] >>> 15 | nfsr[1] << 17;
+ int b36 = nfsr[1] >>> 4 | nfsr[2] << 28;
+ int b45 = nfsr[1] >>> 13 | nfsr[2] << 19;
+ int b64 = nfsr[2];
+ int b73 = nfsr[2] >>> 9 | nfsr[3] << 23;
+ int b89 = nfsr[2] >>> 25 | nfsr[3] << 7;
+ int b95 = nfsr[2] >>> 31 | nfsr[3] << 1;
+ int s8 = lfsr[0] >>> 8 | lfsr[1] << 24;
+ int s13 = lfsr[0] >>> 13 | lfsr[1] << 19;
+ int s20 = lfsr[0] >>> 20 | lfsr[1] << 12;
+ int s42 = lfsr[1] >>> 10 | lfsr[2] << 22;
+ int s60 = lfsr[1] >>> 28 | lfsr[2] << 4;
+ int s79 = lfsr[2] >>> 15 | lfsr[3] << 17;
+ int s93 = lfsr[2] >>> 29 | lfsr[3] << 3;
+ int s95 = lfsr[2] >>> 31 | lfsr[3] << 1;
+
+ return b12 & s8 ^ s13 & s20 ^ b95 & s42 ^ s60 & s79 ^ b12 & b95 & s95 ^ s93
+ ^ b2 ^ b15 ^ b36 ^ b45 ^ b64 ^ b73 ^ b89;
+ }
+
+ /**
+ * Shift array 32 bits and add val to index.length - 1.
+ *
+ * @param array The array to shift.
+ * @param val The value to shift in.
+ * @return The shifted array with val added to index.length - 1.
+ */
+ private int[] shift(int[] array, int val)
+ {
+ array[0] = array[1];
+ array[1] = array[2];
+ array[2] = array[3];
+ array[3] = val;
+
+ return array;
+ }
+
+ /**
+ * Set keys, reset cipher.
+ *
+ * @param keyBytes The key.
+ * @param ivBytes The IV.
+ */
+ private void setKey(byte[] keyBytes, byte[] ivBytes)
+ {
+ ivBytes[12] = (byte)0xFF;
+ ivBytes[13] = (byte)0xFF;
+ ivBytes[14] = (byte)0xFF;
+ ivBytes[15] = (byte)0xFF;
+ workingKey = keyBytes;
+ workingIV = ivBytes;
+
+ /**
+ * Load NFSR and LFSR
+ */
+ int j = 0;
+ for (int i = 0; i < nfsr.length; i++)
+ {
+ nfsr[i] = ((workingKey[j + 3]) << 24) | ((workingKey[j + 2]) << 16)
+ & 0x00FF0000 | ((workingKey[j + 1]) << 8) & 0x0000FF00
+ | ((workingKey[j]) & 0x000000FF);
+
+ lfsr[i] = ((workingIV[j + 3]) << 24) | ((workingIV[j + 2]) << 16)
+ & 0x00FF0000 | ((workingIV[j + 1]) << 8) & 0x0000FF00
+ | ((workingIV[j]) & 0x000000FF);
+ j += 4;
+ }
+ }
+
+ public void processBytes(byte[] in, int inOff, int len, byte[] out,
+ int outOff)
+ throws DataLengthException
+ {
+ if (!initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName()
+ + " not initialised");
+ }
+
+ if ((inOff + len) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + len) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ for (int i = 0; i < len; i++)
+ {
+ out[outOff + i] = (byte)(in[inOff + i] ^ getKeyStream());
+ }
+ }
+
+ public void reset()
+ {
+ index = 4;
+ setKey(workingKey, workingIV);
+ initGrain();
+ }
+
+ /**
+ * Run Grain one round(i.e. 32 bits).
+ */
+ private void oneRound()
+ {
+ output = getOutput();
+ out[0] = (byte)output;
+ out[1] = (byte)(output >> 8);
+ out[2] = (byte)(output >> 16);
+ out[3] = (byte)(output >> 24);
+
+ nfsr = shift(nfsr, getOutputNFSR() ^ lfsr[0]);
+ lfsr = shift(lfsr, getOutputLFSR());
+ }
+
+ public byte returnByte(byte in)
+ {
+ if (!initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName()
+ + " not initialised");
+ }
+ return (byte)(in ^ getKeyStream());
+ }
+
+ private byte getKeyStream()
+ {
+ if (index > 3)
+ {
+ oneRound();
+ index = 0;
+ }
+ return out[index++];
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/Grainv1Engine.java b/core/src/main/java/org/bouncycastle/crypto/engines/Grainv1Engine.java
new file mode 100644
index 00000000..c3baaec5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/Grainv1Engine.java
@@ -0,0 +1,289 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+
+/**
+ * Implementation of Martin Hell's, Thomas Johansson's and Willi Meier's stream
+ * cipher, Grain v1.
+ */
+public class Grainv1Engine
+ implements StreamCipher
+{
+
+ /**
+ * Constants
+ */
+ private static final int STATE_SIZE = 5;
+
+ /**
+ * Variables to hold the state of the engine during encryption and
+ * decryption
+ */
+ private byte[] workingKey;
+ private byte[] workingIV;
+ private byte[] out;
+ private int[] lfsr;
+ private int[] nfsr;
+ private int output;
+ private int index = 2;
+
+ private boolean initialised = false;
+
+ public String getAlgorithmName()
+ {
+ return "Grain v1";
+ }
+
+ /**
+ * Initialize a Grain v1 cipher.
+ *
+ * @param forEncryption Whether or not we are for encryption.
+ * @param params The parameters required to set up the cipher.
+ * @throws IllegalArgumentException If the params argument is inappropriate.
+ */
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ /**
+ * Grain encryption and decryption is completely symmetrical, so the
+ * 'forEncryption' is irrelevant.
+ */
+ if (!(params instanceof ParametersWithIV))
+ {
+ throw new IllegalArgumentException(
+ "Grain v1 Init parameters must include an IV");
+ }
+
+ ParametersWithIV ivParams = (ParametersWithIV)params;
+
+ byte[] iv = ivParams.getIV();
+
+ if (iv == null || iv.length != 8)
+ {
+ throw new IllegalArgumentException(
+ "Grain v1 requires exactly 8 bytes of IV");
+ }
+
+ if (!(ivParams.getParameters() instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException(
+ "Grain v1 Init parameters must include a key");
+ }
+
+ KeyParameter key = (KeyParameter)ivParams.getParameters();
+
+ /**
+ * Initialize variables.
+ */
+ workingIV = new byte[key.getKey().length];
+ workingKey = new byte[key.getKey().length];
+ lfsr = new int[STATE_SIZE];
+ nfsr = new int[STATE_SIZE];
+ out = new byte[2];
+
+ System.arraycopy(iv, 0, workingIV, 0, iv.length);
+ System.arraycopy(key.getKey(), 0, workingKey, 0, key.getKey().length);
+
+ setKey(workingKey, workingIV);
+ initGrain();
+ }
+
+ /**
+ * 160 clocks initialization phase.
+ */
+ private void initGrain()
+ {
+ for (int i = 0; i < 10; i++)
+ {
+ output = getOutput();
+ nfsr = shift(nfsr, getOutputNFSR() ^ lfsr[0] ^ output);
+ lfsr = shift(lfsr, getOutputLFSR() ^ output);
+ }
+ initialised = true;
+ }
+
+ /**
+ * Get output from non-linear function g(x).
+ *
+ * @return Output from NFSR.
+ */
+ private int getOutputNFSR()
+ {
+ int b0 = nfsr[0];
+ int b9 = nfsr[0] >>> 9 | nfsr[1] << 7;
+ int b14 = nfsr[0] >>> 14 | nfsr[1] << 2;
+ int b15 = nfsr[0] >>> 15 | nfsr[1] << 1;
+ int b21 = nfsr[1] >>> 5 | nfsr[2] << 11;
+ int b28 = nfsr[1] >>> 12 | nfsr[2] << 4;
+ int b33 = nfsr[2] >>> 1 | nfsr[3] << 15;
+ int b37 = nfsr[2] >>> 5 | nfsr[3] << 11;
+ int b45 = nfsr[2] >>> 13 | nfsr[3] << 3;
+ int b52 = nfsr[3] >>> 4 | nfsr[4] << 12;
+ int b60 = nfsr[3] >>> 12 | nfsr[4] << 4;
+ int b62 = nfsr[3] >>> 14 | nfsr[4] << 2;
+ int b63 = nfsr[3] >>> 15 | nfsr[4] << 1;
+
+ return (b62 ^ b60 ^ b52 ^ b45 ^ b37 ^ b33 ^ b28 ^ b21 ^ b14
+ ^ b9 ^ b0 ^ b63 & b60 ^ b37 & b33 ^ b15 & b9 ^ b60 & b52 & b45
+ ^ b33 & b28 & b21 ^ b63 & b45 & b28 & b9 ^ b60 & b52 & b37
+ & b33 ^ b63 & b60 & b21 & b15 ^ b63 & b60 & b52 & b45 & b37
+ ^ b33 & b28 & b21 & b15 & b9 ^ b52 & b45 & b37 & b33 & b28
+ & b21) & 0x0000FFFF;
+ }
+
+ /**
+ * Get output from linear function f(x).
+ *
+ * @return Output from LFSR.
+ */
+ private int getOutputLFSR()
+ {
+ int s0 = lfsr[0];
+ int s13 = lfsr[0] >>> 13 | lfsr[1] << 3;
+ int s23 = lfsr[1] >>> 7 | lfsr[2] << 9;
+ int s38 = lfsr[2] >>> 6 | lfsr[3] << 10;
+ int s51 = lfsr[3] >>> 3 | lfsr[4] << 13;
+ int s62 = lfsr[3] >>> 14 | lfsr[4] << 2;
+
+ return (s0 ^ s13 ^ s23 ^ s38 ^ s51 ^ s62) & 0x0000FFFF;
+ }
+
+ /**
+ * Get output from output function h(x).
+ *
+ * @return Output from h(x).
+ */
+ private int getOutput()
+ {
+ int b1 = nfsr[0] >>> 1 | nfsr[1] << 15;
+ int b2 = nfsr[0] >>> 2 | nfsr[1] << 14;
+ int b4 = nfsr[0] >>> 4 | nfsr[1] << 12;
+ int b10 = nfsr[0] >>> 10 | nfsr[1] << 6;
+ int b31 = nfsr[1] >>> 15 | nfsr[2] << 1;
+ int b43 = nfsr[2] >>> 11 | nfsr[3] << 5;
+ int b56 = nfsr[3] >>> 8 | nfsr[4] << 8;
+ int b63 = nfsr[3] >>> 15 | nfsr[4] << 1;
+ int s3 = lfsr[0] >>> 3 | lfsr[1] << 13;
+ int s25 = lfsr[1] >>> 9 | lfsr[2] << 7;
+ int s46 = lfsr[2] >>> 14 | lfsr[3] << 2;
+ int s64 = lfsr[4];
+
+ return (s25 ^ b63 ^ s3 & s64 ^ s46 & s64 ^ s64 & b63 ^ s3
+ & s25 & s46 ^ s3 & s46 & s64 ^ s3 & s46 & b63 ^ s25 & s46 & b63 ^ s46
+ & s64 & b63 ^ b1 ^ b2 ^ b4 ^ b10 ^ b31 ^ b43 ^ b56) & 0x0000FFFF;
+ }
+
+ /**
+ * Shift array 16 bits and add val to index.length - 1.
+ *
+ * @param array The array to shift.
+ * @param val The value to shift in.
+ * @return The shifted array with val added to index.length - 1.
+ */
+ private int[] shift(int[] array, int val)
+ {
+ array[0] = array[1];
+ array[1] = array[2];
+ array[2] = array[3];
+ array[3] = array[4];
+ array[4] = val;
+
+ return array;
+ }
+
+ /**
+ * Set keys, reset cipher.
+ *
+ * @param keyBytes The key.
+ * @param ivBytes The IV.
+ */
+ private void setKey(byte[] keyBytes, byte[] ivBytes)
+ {
+ ivBytes[8] = (byte)0xFF;
+ ivBytes[9] = (byte)0xFF;
+ workingKey = keyBytes;
+ workingIV = ivBytes;
+
+ /**
+ * Load NFSR and LFSR
+ */
+ int j = 0;
+ for (int i = 0; i < nfsr.length; i++)
+ {
+ nfsr[i] = (workingKey[j + 1] << 8 | workingKey[j] & 0xFF) & 0x0000FFFF;
+ lfsr[i] = (workingIV[j + 1] << 8 | workingIV[j] & 0xFF) & 0x0000FFFF;
+ j += 2;
+ }
+ }
+
+ public void processBytes(byte[] in, int inOff, int len, byte[] out,
+ int outOff)
+ throws DataLengthException
+ {
+ if (!initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName()
+ + " not initialised");
+ }
+
+ if ((inOff + len) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + len) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ for (int i = 0; i < len; i++)
+ {
+ out[outOff + i] = (byte)(in[inOff + i] ^ getKeyStream());
+ }
+ }
+
+ public void reset()
+ {
+ index = 2;
+ setKey(workingKey, workingIV);
+ initGrain();
+ }
+
+ /**
+ * Run Grain one round(i.e. 16 bits).
+ */
+ private void oneRound()
+ {
+ output = getOutput();
+ out[0] = (byte)output;
+ out[1] = (byte)(output >> 8);
+
+ nfsr = shift(nfsr, getOutputNFSR() ^ lfsr[0]);
+ lfsr = shift(lfsr, getOutputLFSR());
+ }
+
+ public byte returnByte(byte in)
+ {
+ if (!initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName()
+ + " not initialised");
+ }
+ return (byte)(in ^ getKeyStream());
+ }
+
+ private byte getKeyStream()
+ {
+ if (index > 1)
+ {
+ oneRound();
+ index = 0;
+ }
+ return out[index++];
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/HC128Engine.java b/core/src/main/java/org/bouncycastle/crypto/engines/HC128Engine.java
new file mode 100644
index 00000000..69da0f03
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/HC128Engine.java
@@ -0,0 +1,257 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+
+/**
+ * HC-128 is a software-efficient stream cipher created by Hongjun Wu. It
+ * generates keystream from a 128-bit secret key and a 128-bit initialization
+ * vector.
+ * <p>
+ * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc128_p3.pdf
+ * </p><p>
+ * It is a third phase candidate in the eStream contest, and is patent-free.
+ * No attacks are known as of today (April 2007). See
+ *
+ * http://www.ecrypt.eu.org/stream/hcp3.html
+ * </p>
+ */
+public class HC128Engine
+ implements StreamCipher
+{
+ private int[] p = new int[512];
+ private int[] q = new int[512];
+ private int cnt = 0;
+
+ private static int f1(int x)
+ {
+ return rotateRight(x, 7) ^ rotateRight(x, 18)
+ ^ (x >>> 3);
+ }
+
+ private static int f2(int x)
+ {
+ return rotateRight(x, 17) ^ rotateRight(x, 19)
+ ^ (x >>> 10);
+ }
+
+ private int g1(int x, int y, int z)
+ {
+ return (rotateRight(x, 10) ^ rotateRight(z, 23))
+ + rotateRight(y, 8);
+ }
+
+ private int g2(int x, int y, int z)
+ {
+ return (rotateLeft(x, 10) ^ rotateLeft(z, 23)) + rotateLeft(y, 8);
+ }
+
+ private static int rotateLeft(
+ int x,
+ int bits)
+ {
+ return (x << bits) | (x >>> -bits);
+ }
+
+ private static int rotateRight(
+ int x,
+ int bits)
+ {
+ return (x >>> bits) | (x << -bits);
+ }
+
+ private int h1(int x)
+ {
+ return q[x & 0xFF] + q[((x >> 16) & 0xFF) + 256];
+ }
+
+ private int h2(int x)
+ {
+ return p[x & 0xFF] + p[((x >> 16) & 0xFF) + 256];
+ }
+
+ private static int mod1024(int x)
+ {
+ return x & 0x3FF;
+ }
+
+ private static int mod512(int x)
+ {
+ return x & 0x1FF;
+ }
+
+ private static int dim(int x, int y)
+ {
+ return mod512(x - y);
+ }
+
+ private int step()
+ {
+ int j = mod512(cnt);
+ int ret;
+ if (cnt < 512)
+ {
+ p[j] += g1(p[dim(j, 3)], p[dim(j, 10)], p[dim(j, 511)]);
+ ret = h1(p[dim(j, 12)]) ^ p[j];
+ }
+ else
+ {
+ q[j] += g2(q[dim(j, 3)], q[dim(j, 10)], q[dim(j, 511)]);
+ ret = h2(q[dim(j, 12)]) ^ q[j];
+ }
+ cnt = mod1024(cnt + 1);
+ return ret;
+ }
+
+ private byte[] key, iv;
+ private boolean initialised;
+
+ private void init()
+ {
+ if (key.length != 16)
+ {
+ throw new java.lang.IllegalArgumentException(
+ "The key must be 128 bits long");
+ }
+
+ cnt = 0;
+
+ int[] w = new int[1280];
+
+ for (int i = 0; i < 16; i++)
+ {
+ w[i >> 2] |= (key[i] & 0xff) << (8 * (i & 0x3));
+ }
+ System.arraycopy(w, 0, w, 4, 4);
+
+ for (int i = 0; i < iv.length && i < 16; i++)
+ {
+ w[(i >> 2) + 8] |= (iv[i] & 0xff) << (8 * (i & 0x3));
+ }
+ System.arraycopy(w, 8, w, 12, 4);
+
+ for (int i = 16; i < 1280; i++)
+ {
+ w[i] = f2(w[i - 2]) + w[i - 7] + f1(w[i - 15]) + w[i - 16] + i;
+ }
+
+ System.arraycopy(w, 256, p, 0, 512);
+ System.arraycopy(w, 768, q, 0, 512);
+
+ for (int i = 0; i < 512; i++)
+ {
+ p[i] = step();
+ }
+ for (int i = 0; i < 512; i++)
+ {
+ q[i] = step();
+ }
+
+ cnt = 0;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "HC-128";
+ }
+
+ /**
+ * Initialise a HC-128 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption. Irrelevant, as
+ * encryption and decryption are the same.
+ * @param params the parameters required to set up the cipher.
+ * @throws IllegalArgumentException if the params argument is
+ * inappropriate (ie. the key is not 128 bit long).
+ */
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ CipherParameters keyParam = params;
+
+ if (params instanceof ParametersWithIV)
+ {
+ iv = ((ParametersWithIV)params).getIV();
+ keyParam = ((ParametersWithIV)params).getParameters();
+ }
+ else
+ {
+ iv = new byte[0];
+ }
+
+ if (keyParam instanceof KeyParameter)
+ {
+ key = ((KeyParameter)keyParam).getKey();
+ init();
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "Invalid parameter passed to HC128 init - "
+ + params.getClass().getName());
+ }
+
+ initialised = true;
+ }
+
+ private byte[] buf = new byte[4];
+ private int idx = 0;
+
+ private byte getByte()
+ {
+ if (idx == 0)
+ {
+ int step = step();
+ buf[0] = (byte)(step & 0xFF);
+ step >>= 8;
+ buf[1] = (byte)(step & 0xFF);
+ step >>= 8;
+ buf[2] = (byte)(step & 0xFF);
+ step >>= 8;
+ buf[3] = (byte)(step & 0xFF);
+ }
+ byte ret = buf[idx];
+ idx = idx + 1 & 0x3;
+ return ret;
+ }
+
+ public void processBytes(byte[] in, int inOff, int len, byte[] out,
+ int outOff) throws DataLengthException
+ {
+ if (!initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName()
+ + " not initialised");
+ }
+
+ if ((inOff + len) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + len) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ for (int i = 0; i < len; i++)
+ {
+ out[outOff + i] = (byte)(in[inOff + i] ^ getByte());
+ }
+ }
+
+ public void reset()
+ {
+ idx = 0;
+ init();
+ }
+
+ public byte returnByte(byte in)
+ {
+ return (byte)(in ^ getByte());
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/HC256Engine.java b/core/src/main/java/org/bouncycastle/crypto/engines/HC256Engine.java
new file mode 100644
index 00000000..538d244d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/HC256Engine.java
@@ -0,0 +1,244 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+
+/**
+ * HC-256 is a software-efficient stream cipher created by Hongjun Wu. It
+ * generates keystream from a 256-bit secret key and a 256-bit initialization
+ * vector.
+ * <p>
+ * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc256_p3.pdf
+ * </p><p>
+ * Its brother, HC-128, is a third phase candidate in the eStream contest.
+ * The algorithm is patent-free. No attacks are known as of today (April 2007).
+ * See
+ *
+ * http://www.ecrypt.eu.org/stream/hcp3.html
+ * </p>
+ */
+public class HC256Engine
+ implements StreamCipher
+{
+ private int[] p = new int[1024];
+ private int[] q = new int[1024];
+ private int cnt = 0;
+
+ private int step()
+ {
+ int j = cnt & 0x3FF;
+ int ret;
+ if (cnt < 1024)
+ {
+ int x = p[(j - 3 & 0x3FF)];
+ int y = p[(j - 1023 & 0x3FF)];
+ p[j] += p[(j - 10 & 0x3FF)]
+ + (rotateRight(x, 10) ^ rotateRight(y, 23))
+ + q[((x ^ y) & 0x3FF)];
+
+ x = p[(j - 12 & 0x3FF)];
+ ret = (q[x & 0xFF] + q[((x >> 8) & 0xFF) + 256]
+ + q[((x >> 16) & 0xFF) + 512] + q[((x >> 24) & 0xFF) + 768])
+ ^ p[j];
+ }
+ else
+ {
+ int x = q[(j - 3 & 0x3FF)];
+ int y = q[(j - 1023 & 0x3FF)];
+ q[j] += q[(j - 10 & 0x3FF)]
+ + (rotateRight(x, 10) ^ rotateRight(y, 23))
+ + p[((x ^ y) & 0x3FF)];
+
+ x = q[(j - 12 & 0x3FF)];
+ ret = (p[x & 0xFF] + p[((x >> 8) & 0xFF) + 256]
+ + p[((x >> 16) & 0xFF) + 512] + p[((x >> 24) & 0xFF) + 768])
+ ^ q[j];
+ }
+ cnt = cnt + 1 & 0x7FF;
+ return ret;
+ }
+
+ private byte[] key, iv;
+ private boolean initialised;
+
+ private void init()
+ {
+ if (key.length != 32 && key.length != 16)
+ {
+ throw new IllegalArgumentException(
+ "The key must be 128/256 bits long");
+ }
+
+ if (iv.length < 16)
+ {
+ throw new IllegalArgumentException(
+ "The IV must be at least 128 bits long");
+ }
+
+ if (key.length != 32)
+ {
+ byte[] k = new byte[32];
+
+ System.arraycopy(key, 0, k, 0, key.length);
+ System.arraycopy(key, 0, k, 16, key.length);
+
+ key = k;
+ }
+
+ if (iv.length < 32)
+ {
+ byte[] newIV = new byte[32];
+
+ System.arraycopy(iv, 0, newIV, 0, iv.length);
+ System.arraycopy(iv, 0, newIV, iv.length, newIV.length - iv.length);
+
+ iv = newIV;
+ }
+
+ cnt = 0;
+
+ int[] w = new int[2560];
+
+ for (int i = 0; i < 32; i++)
+ {
+ w[i >> 2] |= (key[i] & 0xff) << (8 * (i & 0x3));
+ }
+
+ for (int i = 0; i < 32; i++)
+ {
+ w[(i >> 2) + 8] |= (iv[i] & 0xff) << (8 * (i & 0x3));
+ }
+
+ for (int i = 16; i < 2560; i++)
+ {
+ int x = w[i - 2];
+ int y = w[i - 15];
+ w[i] = (rotateRight(x, 17) ^ rotateRight(x, 19) ^ (x >>> 10))
+ + w[i - 7]
+ + (rotateRight(y, 7) ^ rotateRight(y, 18) ^ (y >>> 3))
+ + w[i - 16] + i;
+ }
+
+ System.arraycopy(w, 512, p, 0, 1024);
+ System.arraycopy(w, 1536, q, 0, 1024);
+
+ for (int i = 0; i < 4096; i++)
+ {
+ step();
+ }
+
+ cnt = 0;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "HC-256";
+ }
+
+ /**
+ * Initialise a HC-256 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption. Irrelevant, as
+ * encryption and decryption are the same.
+ * @param params the parameters required to set up the cipher.
+ * @throws IllegalArgumentException if the params argument is
+ * inappropriate (ie. the key is not 256 bit long).
+ */
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ CipherParameters keyParam = params;
+
+ if (params instanceof ParametersWithIV)
+ {
+ iv = ((ParametersWithIV)params).getIV();
+ keyParam = ((ParametersWithIV)params).getParameters();
+ }
+ else
+ {
+ iv = new byte[0];
+ }
+
+ if (keyParam instanceof KeyParameter)
+ {
+ key = ((KeyParameter)keyParam).getKey();
+ init();
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "Invalid parameter passed to HC256 init - "
+ + params.getClass().getName());
+ }
+
+ initialised = true;
+ }
+
+ private byte[] buf = new byte[4];
+ private int idx = 0;
+
+ private byte getByte()
+ {
+ if (idx == 0)
+ {
+ int step = step();
+ buf[0] = (byte)(step & 0xFF);
+ step >>= 8;
+ buf[1] = (byte)(step & 0xFF);
+ step >>= 8;
+ buf[2] = (byte)(step & 0xFF);
+ step >>= 8;
+ buf[3] = (byte)(step & 0xFF);
+ }
+ byte ret = buf[idx];
+ idx = idx + 1 & 0x3;
+ return ret;
+ }
+
+ public void processBytes(byte[] in, int inOff, int len, byte[] out,
+ int outOff) throws DataLengthException
+ {
+ if (!initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName()
+ + " not initialised");
+ }
+
+ if ((inOff + len) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + len) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ for (int i = 0; i < len; i++)
+ {
+ out[outOff + i] = (byte)(in[inOff + i] ^ getByte());
+ }
+ }
+
+ public void reset()
+ {
+ idx = 0;
+ init();
+ }
+
+ public byte returnByte(byte in)
+ {
+ return (byte)(in ^ getByte());
+ }
+
+ private static int rotateRight(
+ int x,
+ int bits)
+ {
+ return (x >>> bits) | (x << -bits);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/IDEAEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/IDEAEngine.java
new file mode 100644
index 00000000..fdf3f6d3
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/IDEAEngine.java
@@ -0,0 +1,367 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * A class that provides a basic International Data Encryption Algorithm (IDEA) engine.
+ * <p>
+ * This implementation is based on the "HOWTO: INTERNATIONAL DATA ENCRYPTION ALGORITHM"
+ * implementation summary by Fauzan Mirza (F.U.Mirza@sheffield.ac.uk). (baring 1 typo at the
+ * end of the mulinv function!).
+ * <p>
+ * It can be found at ftp://ftp.funet.fi/pub/crypt/cryptography/symmetric/idea/
+ * <p>
+ * Note 1: This algorithm is patented in the USA, Japan, and Europe including
+ * at least Austria, France, Germany, Italy, Netherlands, Spain, Sweden, Switzerland
+ * and the United Kingdom. Non-commercial use is free, however any commercial
+ * products are liable for royalties. Please see
+ * <a href="http://www.mediacrypt.com">www.mediacrypt.com</a> for
+ * further details. This announcement has been included at the request of
+ * the patent holders.
+ * <p>
+ * Note 2: Due to the requests concerning the above, this algorithm is now only
+ * included in the extended Bouncy Castle provider and JCE signed jars. It is
+ * not included in the default distributions.
+ */
+public class IDEAEngine
+ implements BlockCipher
+{
+ protected static final int BLOCK_SIZE = 8;
+
+ private int[] workingKey = null;
+
+ /**
+ * standard constructor.
+ */
+ public IDEAEngine()
+ {
+ }
+
+ /**
+ * initialise an IDEA cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ workingKey = generateWorkingKey(forEncryption,
+ ((KeyParameter)params).getKey());
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to IDEA init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "IDEA";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new IllegalStateException("IDEA engine not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ ideaFunc(workingKey, in, inOff, out, outOff);
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+
+ private static final int MASK = 0xffff;
+ private static final int BASE = 0x10001;
+
+ private int bytesToWord(
+ byte[] in,
+ int inOff)
+ {
+ return ((in[inOff] << 8) & 0xff00) + (in[inOff + 1] & 0xff);
+ }
+
+ private void wordToBytes(
+ int word,
+ byte[] out,
+ int outOff)
+ {
+ out[outOff] = (byte)(word >>> 8);
+ out[outOff + 1] = (byte)word;
+ }
+
+ /**
+ * return x = x * y where the multiplication is done modulo
+ * 65537 (0x10001) (as defined in the IDEA specification) and
+ * a zero input is taken to be 65536 (0x10000).
+ *
+ * @param x the x value
+ * @param y the y value
+ * @return x = x * y
+ */
+ private int mul(
+ int x,
+ int y)
+ {
+ if (x == 0)
+ {
+ x = (BASE - y);
+ }
+ else if (y == 0)
+ {
+ x = (BASE - x);
+ }
+ else
+ {
+ int p = x * y;
+
+ y = p & MASK;
+ x = p >>> 16;
+ x = y - x + ((y < x) ? 1 : 0);
+ }
+
+ return x & MASK;
+ }
+
+ private void ideaFunc(
+ int[] workingKey,
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int x0, x1, x2, x3, t0, t1;
+ int keyOff = 0;
+
+ x0 = bytesToWord(in, inOff);
+ x1 = bytesToWord(in, inOff + 2);
+ x2 = bytesToWord(in, inOff + 4);
+ x3 = bytesToWord(in, inOff + 6);
+
+ for (int round = 0; round < 8; round++)
+ {
+ x0 = mul(x0, workingKey[keyOff++]);
+ x1 += workingKey[keyOff++];
+ x1 &= MASK;
+ x2 += workingKey[keyOff++];
+ x2 &= MASK;
+ x3 = mul(x3, workingKey[keyOff++]);
+
+ t0 = x1;
+ t1 = x2;
+ x2 ^= x0;
+ x1 ^= x3;
+
+ x2 = mul(x2, workingKey[keyOff++]);
+ x1 += x2;
+ x1 &= MASK;
+
+ x1 = mul(x1, workingKey[keyOff++]);
+ x2 += x1;
+ x2 &= MASK;
+
+ x0 ^= x1;
+ x3 ^= x2;
+ x1 ^= t1;
+ x2 ^= t0;
+ }
+
+ wordToBytes(mul(x0, workingKey[keyOff++]), out, outOff);
+ wordToBytes(x2 + workingKey[keyOff++], out, outOff + 2); /* NB: Order */
+ wordToBytes(x1 + workingKey[keyOff++], out, outOff + 4);
+ wordToBytes(mul(x3, workingKey[keyOff]), out, outOff + 6);
+ }
+
+ /**
+ * The following function is used to expand the user key to the encryption
+ * subkey. The first 16 bytes are the user key, and the rest of the subkey
+ * is calculated by rotating the previous 16 bytes by 25 bits to the left,
+ * and so on until the subkey is completed.
+ */
+ private int[] expandKey(
+ byte[] uKey)
+ {
+ int[] key = new int[52];
+
+ if (uKey.length < 16)
+ {
+ byte[] tmp = new byte[16];
+
+ System.arraycopy(uKey, 0, tmp, tmp.length - uKey.length, uKey.length);
+
+ uKey = tmp;
+ }
+
+ for (int i = 0; i < 8; i++)
+ {
+ key[i] = bytesToWord(uKey, i * 2);
+ }
+
+ for (int i = 8; i < 52; i++)
+ {
+ if ((i & 7) < 6)
+ {
+ key[i] = ((key[i - 7] & 127) << 9 | key[i - 6] >> 7) & MASK;
+ }
+ else if ((i & 7) == 6)
+ {
+ key[i] = ((key[i - 7] & 127) << 9 | key[i - 14] >> 7) & MASK;
+ }
+ else
+ {
+ key[i] = ((key[i - 15] & 127) << 9 | key[i - 14] >> 7) & MASK;
+ }
+ }
+
+ return key;
+ }
+
+ /**
+ * This function computes multiplicative inverse using Euclid's Greatest
+ * Common Divisor algorithm. Zero and one are self inverse.
+ * <p>
+ * i.e. x * mulInv(x) == 1 (modulo BASE)
+ */
+ private int mulInv(
+ int x)
+ {
+ int t0, t1, q, y;
+
+ if (x < 2)
+ {
+ return x;
+ }
+
+ t0 = 1;
+ t1 = BASE / x;
+ y = BASE % x;
+
+ while (y != 1)
+ {
+ q = x / y;
+ x = x % y;
+ t0 = (t0 + (t1 * q)) & MASK;
+ if (x == 1)
+ {
+ return t0;
+ }
+ q = y / x;
+ y = y % x;
+ t1 = (t1 + (t0 * q)) & MASK;
+ }
+
+ return (1 - t1) & MASK;
+ }
+
+ /**
+ * Return the additive inverse of x.
+ * <p>
+ * i.e. x + addInv(x) == 0
+ */
+ int addInv(
+ int x)
+ {
+ return (0 - x) & MASK;
+ }
+
+ /**
+ * The function to invert the encryption subkey to the decryption subkey.
+ * It also involves the multiplicative inverse and the additive inverse functions.
+ */
+ private int[] invertKey(
+ int[] inKey)
+ {
+ int t1, t2, t3, t4;
+ int p = 52; /* We work backwards */
+ int[] key = new int[52];
+ int inOff = 0;
+
+ t1 = mulInv(inKey[inOff++]);
+ t2 = addInv(inKey[inOff++]);
+ t3 = addInv(inKey[inOff++]);
+ t4 = mulInv(inKey[inOff++]);
+ key[--p] = t4;
+ key[--p] = t3;
+ key[--p] = t2;
+ key[--p] = t1;
+
+ for (int round = 1; round < 8; round++)
+ {
+ t1 = inKey[inOff++];
+ t2 = inKey[inOff++];
+ key[--p] = t2;
+ key[--p] = t1;
+
+ t1 = mulInv(inKey[inOff++]);
+ t2 = addInv(inKey[inOff++]);
+ t3 = addInv(inKey[inOff++]);
+ t4 = mulInv(inKey[inOff++]);
+ key[--p] = t4;
+ key[--p] = t2; /* NB: Order */
+ key[--p] = t3;
+ key[--p] = t1;
+ }
+
+ t1 = inKey[inOff++];
+ t2 = inKey[inOff++];
+ key[--p] = t2;
+ key[--p] = t1;
+
+ t1 = mulInv(inKey[inOff++]);
+ t2 = addInv(inKey[inOff++]);
+ t3 = addInv(inKey[inOff++]);
+ t4 = mulInv(inKey[inOff]);
+ key[--p] = t4;
+ key[--p] = t3;
+ key[--p] = t2;
+ key[--p] = t1;
+
+ return key;
+ }
+
+ private int[] generateWorkingKey(
+ boolean forEncryption,
+ byte[] userKey)
+ {
+ if (forEncryption)
+ {
+ return expandKey(userKey);
+ }
+ else
+ {
+ return invertKey(expandKey(userKey));
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/IESEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/IESEngine.java
new file mode 100755
index 00000000..ea8556de
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/IESEngine.java
@@ -0,0 +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);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAACEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAACEngine.java
new file mode 100644
index 00000000..d6e1ae11
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAACEngine.java
@@ -0,0 +1,219 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.util.Pack;
+
+/**
+ * Implementation of Bob Jenkin's ISAAC (Indirection Shift Accumulate Add and Count).
+ * see: http://www.burtleburtle.net/bob/rand/isaacafa.html
+*/
+public class ISAACEngine
+ implements StreamCipher
+{
+ // Constants
+ private final int sizeL = 8,
+ stateArraySize = sizeL<<5; // 256
+
+ // Cipher's internal state
+ private int[] engineState = null, // mm
+ results = null; // randrsl
+ private int a = 0, b = 0, c = 0;
+
+ // Engine state
+ private int index = 0;
+ private byte[] keyStream = new byte[stateArraySize<<2], // results expanded into bytes
+ workingKey = null;
+ private boolean initialised = false;
+
+ /**
+ * initialise an ISAAC cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("invalid parameter passed to ISAAC init - " + params.getClass().getName());
+ }
+ /*
+ * ISAAC encryption and decryption is completely
+ * symmetrical, so the 'forEncryption' is
+ * irrelevant.
+ */
+ KeyParameter p = (KeyParameter)params;
+ setKey(p.getKey());
+
+ return;
+ }
+
+ public byte returnByte(byte in)
+ {
+ if (index == 0)
+ {
+ isaac();
+ keyStream = Pack.intToBigEndian(results);
+ }
+ byte out = (byte)(keyStream[index]^in);
+ index = (index + 1) & 1023;
+
+ return out;
+ }
+
+ public void processBytes(
+ byte[] in,
+ int inOff,
+ int len,
+ byte[] out,
+ int outOff)
+ {
+ if (!initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName()+" not initialised");
+ }
+
+ if ((inOff + len) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + len) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ for (int i = 0; i < len; i++)
+ {
+ if (index == 0)
+ {
+ isaac();
+ keyStream = Pack.intToBigEndian(results);
+ }
+ out[i+outOff] = (byte)(keyStream[index]^in[i+inOff]);
+ index = (index + 1) & 1023;
+ }
+ }
+
+ public String getAlgorithmName()
+ {
+ return "ISAAC";
+ }
+
+ public void reset()
+ {
+ setKey(workingKey);
+ }
+
+ // Private implementation
+ private void setKey(byte[] keyBytes)
+ {
+ workingKey = keyBytes;
+
+ if (engineState == null)
+ {
+ engineState = new int[stateArraySize];
+ }
+
+ if (results == null)
+ {
+ results = new int[stateArraySize];
+ }
+
+ int i, j, k;
+
+ // Reset state
+ for (i = 0; i < stateArraySize; i++)
+ {
+ engineState[i] = results[i] = 0;
+ }
+ a = b = c = 0;
+
+ // Reset index counter for output
+ index = 0;
+
+ // Convert the key bytes to ints and put them into results[] for initialization
+ byte[] t = new byte[keyBytes.length + (keyBytes.length & 3)];
+ System.arraycopy(keyBytes, 0, t, 0, keyBytes.length);
+ for (i = 0; i < t.length; i+=4)
+ {
+ results[i >>> 2] = Pack.littleEndianToInt(t, i);
+ }
+
+ // It has begun?
+ int[] abcdefgh = new int[sizeL];
+
+ for (i = 0; i < sizeL; i++)
+ {
+ abcdefgh[i] = 0x9e3779b9; // Phi (golden ratio)
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ mix(abcdefgh);
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ for (j = 0; j < stateArraySize; j+=sizeL)
+ {
+ for (k = 0; k < sizeL; k++)
+ {
+ abcdefgh[k] += (i<1) ? results[j+k] : engineState[j+k];
+ }
+
+ mix(abcdefgh);
+
+ for (k = 0; k < sizeL; k++)
+ {
+ engineState[j+k] = abcdefgh[k];
+ }
+ }
+ }
+
+ isaac();
+
+ initialised = true;
+ }
+
+ private void isaac()
+ {
+ int i, x, y;
+
+ b += ++c;
+ for (i = 0; i < stateArraySize; i++)
+ {
+ x = engineState[i];
+ switch (i & 3)
+ {
+ case 0: a ^= (a << 13); break;
+ case 1: a ^= (a >>> 6); break;
+ case 2: a ^= (a << 2); break;
+ case 3: a ^= (a >>> 16); break;
+ }
+ a += engineState[(i+128) & 0xFF];
+ engineState[i] = y = engineState[(x >>> 2) & 0xFF] + a + b;
+ results[i] = b = engineState[(y >>> 10) & 0xFF] + x;
+ }
+ }
+
+ private void mix(int[] x)
+ {
+ x[0]^=x[1]<< 11; x[3]+=x[0]; x[1]+=x[2];
+ x[1]^=x[2]>>> 2; x[4]+=x[1]; x[2]+=x[3];
+ x[2]^=x[3]<< 8; x[5]+=x[2]; x[3]+=x[4];
+ x[3]^=x[4]>>>16; x[6]+=x[3]; x[4]+=x[5];
+ x[4]^=x[5]<< 10; x[7]+=x[4]; x[5]+=x[6];
+ x[5]^=x[6]>>> 4; x[0]+=x[5]; x[6]+=x[7];
+ x[6]^=x[7]<< 8; x[1]+=x[6]; x[7]+=x[0];
+ x[7]^=x[0]>>> 9; x[2]+=x[7]; x[0]+=x[1];
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/NaccacheSternEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/NaccacheSternEngine.java
new file mode 100644
index 00000000..a5403fac
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/NaccacheSternEngine.java
@@ -0,0 +1,437 @@
+package org.bouncycastle.crypto.engines;
+
+import java.math.BigInteger;
+import java.util.Vector;
+import org.bouncycastle.util.Arrays;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.params.NaccacheSternKeyParameters;
+import org.bouncycastle.crypto.params.NaccacheSternPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+
+/**
+ * NaccacheStern Engine. For details on this cipher, please see
+ * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf
+ */
+public class NaccacheSternEngine
+ implements AsymmetricBlockCipher
+{
+ private boolean forEncryption;
+
+ private NaccacheSternKeyParameters key;
+
+ private Vector[] lookup = null;
+
+ private boolean debug = false;
+
+ private static BigInteger ZERO = BigInteger.valueOf(0);
+ private static BigInteger ONE = BigInteger.valueOf(1);
+
+ /**
+ * Initializes this algorithm. Must be called before all other Functions.
+ *
+ * @see org.bouncycastle.crypto.AsymmetricBlockCipher#init(boolean,
+ * org.bouncycastle.crypto.CipherParameters)
+ */
+ public void init(boolean forEncryption, CipherParameters param)
+ {
+ this.forEncryption = forEncryption;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ param = ((ParametersWithRandom) param).getParameters();
+ }
+
+ key = (NaccacheSternKeyParameters)param;
+
+ // construct lookup table for faster decryption if necessary
+ if (!this.forEncryption)
+ {
+ if (debug)
+ {
+ System.out.println("Constructing lookup Array");
+ }
+ NaccacheSternPrivateKeyParameters priv = (NaccacheSternPrivateKeyParameters)key;
+ Vector primes = priv.getSmallPrimes();
+ lookup = new Vector[primes.size()];
+ for (int i = 0; i < primes.size(); i++)
+ {
+ BigInteger actualPrime = (BigInteger)primes.elementAt(i);
+ int actualPrimeValue = actualPrime.intValue();
+
+ lookup[i] = new Vector();
+ lookup[i].addElement(ONE);
+
+ if (debug)
+ {
+ System.out.println("Constructing lookup ArrayList for " + actualPrimeValue);
+ }
+
+ BigInteger accJ = ZERO;
+
+ for (int j = 1; j < actualPrimeValue; j++)
+ {
+ accJ = accJ.add(priv.getPhi_n());
+ BigInteger comp = accJ.divide(actualPrime);
+ lookup[i].addElement(priv.getG().modPow(comp, priv.getModulus()));
+ }
+ }
+ }
+ }
+
+ public void setDebug(boolean debug)
+ {
+ this.debug = debug;
+ }
+
+ /**
+ * Returns the input block size of this algorithm.
+ *
+ * @see org.bouncycastle.crypto.AsymmetricBlockCipher#getInputBlockSize()
+ */
+ public int getInputBlockSize()
+ {
+ if (forEncryption)
+ {
+ // We can only encrypt values up to lowerSigmaBound
+ return (key.getLowerSigmaBound() + 7) / 8 - 1;
+ }
+ else
+ {
+ // We pad to modulus-size bytes for easier decryption.
+ return key.getModulus().toByteArray().length;
+ }
+ }
+
+ /**
+ * Returns the output block size of this algorithm.
+ *
+ * @see org.bouncycastle.crypto.AsymmetricBlockCipher#getOutputBlockSize()
+ */
+ public int getOutputBlockSize()
+ {
+ if (forEncryption)
+ {
+ // encrypted Data is always padded up to modulus size
+ return key.getModulus().toByteArray().length;
+ }
+ else
+ {
+ // decrypted Data has upper limit lowerSigmaBound
+ return (key.getLowerSigmaBound() + 7) / 8 - 1;
+ }
+ }
+
+ /**
+ * Process a single Block using the Naccache-Stern algorithm.
+ *
+ * @see org.bouncycastle.crypto.AsymmetricBlockCipher#processBlock(byte[],
+ * int, int)
+ */
+ public byte[] processBlock(byte[] in, int inOff, int len) throws InvalidCipherTextException
+ {
+ if (key == null)
+ {
+ throw new IllegalStateException("NaccacheStern engine not initialised");
+ }
+ if (len > (getInputBlockSize() + 1))
+ {
+ throw new DataLengthException("input too large for Naccache-Stern cipher.\n");
+ }
+
+ if (!forEncryption)
+ {
+ // At decryption make sure that we receive padded data blocks
+ if (len < getInputBlockSize())
+ {
+ throw new InvalidCipherTextException("BlockLength does not match modulus for Naccache-Stern cipher.\n");
+ }
+ }
+
+ byte[] block;
+
+ if (inOff != 0 || len != in.length)
+ {
+ block = new byte[len];
+ System.arraycopy(in, inOff, block, 0, len);
+ }
+ else
+ {
+ block = in;
+ }
+
+ // transform input into BigInteger
+ BigInteger input = new BigInteger(1, block);
+ if (debug)
+ {
+ System.out.println("input as BigInteger: " + input);
+ }
+ byte[] output;
+ if (forEncryption)
+ {
+ output = encrypt(input);
+ }
+ else
+ {
+ Vector plain = new Vector();
+ NaccacheSternPrivateKeyParameters priv = (NaccacheSternPrivateKeyParameters)key;
+ Vector primes = priv.getSmallPrimes();
+ // Get Chinese Remainders of CipherText
+ for (int i = 0; i < primes.size(); i++)
+ {
+ BigInteger exp = input.modPow(priv.getPhi_n().divide((BigInteger)primes.elementAt(i)), priv.getModulus());
+ Vector al = lookup[i];
+ if (lookup[i].size() != ((BigInteger)primes.elementAt(i)).intValue())
+ {
+ if (debug)
+ {
+ System.out.println("Prime is " + primes.elementAt(i) + ", lookup table has size " + al.size());
+ }
+ throw new InvalidCipherTextException("Error in lookup Array for "
+ + ((BigInteger)primes.elementAt(i)).intValue()
+ + ": Size mismatch. Expected ArrayList with length "
+ + ((BigInteger)primes.elementAt(i)).intValue() + " but found ArrayList of length "
+ + lookup[i].size());
+ }
+ int lookedup = al.indexOf(exp);
+
+ if (lookedup == -1)
+ {
+ if (debug)
+ {
+ System.out.println("Actual prime is " + primes.elementAt(i));
+ System.out.println("Decrypted value is " + exp);
+
+ System.out.println("LookupList for " + primes.elementAt(i) + " with size " + lookup[i].size()
+ + " is: ");
+ for (int j = 0; j < lookup[i].size(); j++)
+ {
+ System.out.println(lookup[i].elementAt(j));
+ }
+ }
+ throw new InvalidCipherTextException("Lookup failed");
+ }
+ plain.addElement(BigInteger.valueOf(lookedup));
+ }
+ BigInteger test = chineseRemainder(plain, primes);
+
+ // Should not be used as an oracle, so reencrypt output to see
+ // if it corresponds to input
+
+ // this breaks probabilisic encryption, so disable it. Anyway, we do
+ // use the first n primes for key generation, so it is pretty easy
+ // to guess them. But as stated in the paper, this is not a security
+ // breach. So we can just work with the correct sigma.
+
+ // if (debug) {
+ // System.out.println("Decryption is " + test);
+ // }
+ // if ((key.getG().modPow(test, key.getModulus())).equals(input)) {
+ // output = test.toByteArray();
+ // } else {
+ // if(debug){
+ // System.out.println("Engine seems to be used as an oracle,
+ // returning null");
+ // }
+ // output = null;
+ // }
+
+ output = test.toByteArray();
+
+ }
+
+ return output;
+ }
+
+ /**
+ * Encrypts a BigInteger aka Plaintext with the public key.
+ *
+ * @param plain
+ * The BigInteger to encrypt
+ * @return The byte[] representation of the encrypted BigInteger (i.e.
+ * crypted.toByteArray())
+ */
+ public byte[] encrypt(BigInteger plain)
+ {
+ // Always return modulus size values 0-padded at the beginning
+ // 0-padding at the beginning is correctly parsed by BigInteger :)
+ byte[] output = key.getModulus().toByteArray();
+ Arrays.fill(output, (byte)0);
+ byte[] tmp = key.getG().modPow(plain, key.getModulus()).toByteArray();
+ System
+ .arraycopy(tmp, 0, output, output.length - tmp.length,
+ tmp.length);
+ if (debug)
+ {
+ System.out
+ .println("Encrypted value is: " + new BigInteger(output));
+ }
+ return output;
+ }
+
+ /**
+ * Adds the contents of two encrypted blocks mod sigma
+ *
+ * @param block1
+ * the first encrypted block
+ * @param block2
+ * the second encrypted block
+ * @return encrypt((block1 + block2) mod sigma)
+ * @throws InvalidCipherTextException
+ */
+ public byte[] addCryptedBlocks(byte[] block1, byte[] block2)
+ throws InvalidCipherTextException
+ {
+ // check for correct blocksize
+ if (forEncryption)
+ {
+ if ((block1.length > getOutputBlockSize())
+ || (block2.length > getOutputBlockSize()))
+ {
+ throw new InvalidCipherTextException(
+ "BlockLength too large for simple addition.\n");
+ }
+ }
+ else
+ {
+ if ((block1.length > getInputBlockSize())
+ || (block2.length > getInputBlockSize()))
+ {
+ throw new InvalidCipherTextException(
+ "BlockLength too large for simple addition.\n");
+ }
+ }
+
+ // calculate resulting block
+ BigInteger m1Crypt = new BigInteger(1, block1);
+ BigInteger m2Crypt = new BigInteger(1, block2);
+ BigInteger m1m2Crypt = m1Crypt.multiply(m2Crypt);
+ m1m2Crypt = m1m2Crypt.mod(key.getModulus());
+ if (debug)
+ {
+ System.out.println("c(m1) as BigInteger:....... " + m1Crypt);
+ System.out.println("c(m2) as BigInteger:....... " + m2Crypt);
+ System.out.println("c(m1)*c(m2)%n = c(m1+m2)%n: " + m1m2Crypt);
+ }
+
+ byte[] output = key.getModulus().toByteArray();
+ Arrays.fill(output, (byte)0);
+ System.arraycopy(m1m2Crypt.toByteArray(), 0, output, output.length
+ - m1m2Crypt.toByteArray().length,
+ m1m2Crypt.toByteArray().length);
+
+ return output;
+ }
+
+ /**
+ * Convenience Method for data exchange with the cipher.
+ *
+ * Determines blocksize and splits data to blocksize.
+ *
+ * @param data the data to be processed
+ * @return the data after it went through the NaccacheSternEngine.
+ * @throws InvalidCipherTextException
+ */
+ public byte[] processData(byte[] data) throws InvalidCipherTextException
+ {
+ if (debug)
+ {
+ System.out.println();
+ }
+ if (data.length > getInputBlockSize())
+ {
+ int inBlocksize = getInputBlockSize();
+ int outBlocksize = getOutputBlockSize();
+ if (debug)
+ {
+ System.out.println("Input blocksize is: " + inBlocksize + " bytes");
+ System.out.println("Output blocksize is: " + outBlocksize + " bytes");
+ System.out.println("Data has length:.... " + data.length + " bytes");
+ }
+ int datapos = 0;
+ int retpos = 0;
+ byte[] retval = new byte[(data.length / inBlocksize + 1) * outBlocksize];
+ while (datapos < data.length)
+ {
+ byte[] tmp;
+ if (datapos + inBlocksize < data.length)
+ {
+ tmp = processBlock(data, datapos, inBlocksize);
+ datapos += inBlocksize;
+ }
+ else
+ {
+ tmp = processBlock(data, datapos, data.length - datapos);
+ datapos += data.length - datapos;
+ }
+ if (debug)
+ {
+ System.out.println("new datapos is " + datapos);
+ }
+ if (tmp != null)
+ {
+ System.arraycopy(tmp, 0, retval, retpos, tmp.length);
+
+ retpos += tmp.length;
+ }
+ else
+ {
+ if (debug)
+ {
+ System.out.println("cipher returned null");
+ }
+ throw new InvalidCipherTextException("cipher returned null");
+ }
+ }
+ byte[] ret = new byte[retpos];
+ System.arraycopy(retval, 0, ret, 0, retpos);
+ if (debug)
+ {
+ System.out.println("returning " + ret.length + " bytes");
+ }
+ return ret;
+ }
+ else
+ {
+ if (debug)
+ {
+ System.out.println("data size is less then input block size, processing directly");
+ }
+ return processBlock(data, 0, data.length);
+ }
+ }
+
+ /**
+ * Computes the integer x that is expressed through the given primes and the
+ * congruences with the chinese remainder theorem (CRT).
+ *
+ * @param congruences
+ * the congruences c_i
+ * @param primes
+ * the primes p_i
+ * @return an integer x for that x % p_i == c_i
+ */
+ private static BigInteger chineseRemainder(Vector congruences, Vector primes)
+ {
+ BigInteger retval = ZERO;
+ BigInteger all = ONE;
+ for (int i = 0; i < primes.size(); i++)
+ {
+ all = all.multiply((BigInteger)primes.elementAt(i));
+ }
+ for (int i = 0; i < primes.size(); i++)
+ {
+ BigInteger a = (BigInteger)primes.elementAt(i);
+ BigInteger b = all.divide(a);
+ BigInteger b_ = b.modInverse(a);
+ BigInteger tmp = b.multiply(b_);
+ tmp = tmp.multiply((BigInteger)congruences.elementAt(i));
+ retval = retval.add(tmp);
+ }
+
+ return retval.mod(all);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/NoekeonEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/NoekeonEngine.java
new file mode 100644
index 00000000..c4494c40
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/NoekeonEngine.java
@@ -0,0 +1,263 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * A Noekeon engine, using direct-key mode.
+ */
+
+public class NoekeonEngine
+ implements BlockCipher
+{
+ private static final int genericSize = 16; // Block and key size, as well as the amount of rounds.
+
+ private static final int[] nullVector =
+ {
+ 0x00, 0x00, 0x00, 0x00 // Used in decryption
+ },
+
+ roundConstants =
+ {
+ 0x80, 0x1b, 0x36, 0x6c,
+ 0xd8, 0xab, 0x4d, 0x9a,
+ 0x2f, 0x5e, 0xbc, 0x63,
+ 0xc6, 0x97, 0x35, 0x6a,
+ 0xd4
+ };
+
+ private int[] state = new int[4], // a
+ subKeys = new int[4], // k
+ decryptKeys = new int[4];
+
+ private boolean _initialised,
+ _forEncryption;
+
+ /**
+ * Create an instance of the Noekeon encryption algorithm
+ * and set some defaults
+ */
+ public NoekeonEngine()
+ {
+ _initialised = false;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Noekeon";
+ }
+
+ public int getBlockSize()
+ {
+ return genericSize;
+ }
+
+ /**
+ * initialise
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("invalid parameter passed to Noekeon init - " + params.getClass().getName());
+ }
+
+ _forEncryption = forEncryption;
+ _initialised = true;
+
+ KeyParameter p = (KeyParameter)params;
+
+ setKey(p.getKey());
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (!_initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName()+" not initialised");
+ }
+
+ if ((inOff + genericSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + genericSize) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ return (_forEncryption) ? encryptBlock(in, inOff, out, outOff)
+ : decryptBlock(in, inOff, out, outOff);
+ }
+
+ public void reset()
+ {
+ }
+
+ /**
+ * Re-key the cipher.
+ * <p>
+ * @param key the key to be used
+ */
+ private void setKey(
+ byte[] key)
+ {
+ subKeys[0] = bytesToIntBig(key, 0);
+ subKeys[1] = bytesToIntBig(key, 4);
+ subKeys[2] = bytesToIntBig(key, 8);
+ subKeys[3] = bytesToIntBig(key, 12);
+ }
+
+ private int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ state[0] = bytesToIntBig(in, inOff);
+ state[1] = bytesToIntBig(in, inOff+4);
+ state[2] = bytesToIntBig(in, inOff+8);
+ state[3] = bytesToIntBig(in, inOff+12);
+
+ int i;
+ for (i = 0; i < genericSize; i++)
+ {
+ state[0] ^= roundConstants[i];
+ theta(state, subKeys);
+ pi1(state);
+ gamma(state);
+ pi2(state);
+ }
+
+ state[0] ^= roundConstants[i];
+ theta(state, subKeys);
+
+ intToBytesBig(state[0], out, outOff);
+ intToBytesBig(state[1], out, outOff+4);
+ intToBytesBig(state[2], out, outOff+8);
+ intToBytesBig(state[3], out, outOff+12);
+
+ return genericSize;
+ }
+
+ private int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ state[0] = bytesToIntBig(in, inOff);
+ state[1] = bytesToIntBig(in, inOff+4);
+ state[2] = bytesToIntBig(in, inOff+8);
+ state[3] = bytesToIntBig(in, inOff+12);
+
+ System.arraycopy(subKeys, 0, decryptKeys, 0, subKeys.length);
+ theta(decryptKeys, nullVector);
+
+ int i;
+ for (i = genericSize; i > 0; i--)
+ {
+ theta(state, decryptKeys);
+ state[0] ^= roundConstants[i];
+ pi1(state);
+ gamma(state);
+ pi2(state);
+ }
+
+ theta(state, decryptKeys);
+ state[0] ^= roundConstants[i];
+
+ intToBytesBig(state[0], out, outOff);
+ intToBytesBig(state[1], out, outOff+4);
+ intToBytesBig(state[2], out, outOff+8);
+ intToBytesBig(state[3], out, outOff+12);
+
+ return genericSize;
+ }
+
+ private void gamma(int[] a)
+ {
+ a[1] ^= ~a[3] & ~a[2];
+ a[0] ^= a[2] & a[1];
+
+ int tmp = a[3];
+ a[3] = a[0];
+ a[0] = tmp;
+ a[2] ^= a[0]^a[1]^a[3];
+
+ a[1] ^= ~a[3] & ~a[2];
+ a[0] ^= a[2] & a[1];
+ }
+
+ private void theta(int[] a, int[] k)
+ {
+ int tmp;
+
+ tmp = a[0]^a[2];
+ tmp ^= rotl(tmp,8)^rotl(tmp,24);
+ a[1] ^= tmp;
+ a[3] ^= tmp;
+
+ for (int i = 0; i < 4; i++)
+ {
+ a[i] ^= k[i];
+ }
+
+ tmp = a[1]^a[3];
+ tmp ^= rotl(tmp,8)^rotl(tmp,24);
+ a[0] ^= tmp;
+ a[2] ^= tmp;
+ }
+
+ private void pi1(int[] a)
+ {
+ a[1] = rotl(a[1], 1);
+ a[2] = rotl(a[2], 5);
+ a[3] = rotl(a[3], 2);
+ }
+
+ private void pi2(int[] a)
+ {
+ a[1] = rotl(a[1], 31);
+ a[2] = rotl(a[2], 27);
+ a[3] = rotl(a[3], 30);
+ }
+
+ // Helpers
+
+ private int bytesToIntBig(byte[] in, int off)
+ {
+ return ((in[off++]) << 24) |
+ ((in[off++] & 0xff) << 16) |
+ ((in[off++] & 0xff) << 8) |
+ (in[off ] & 0xff);
+ }
+
+ private void intToBytesBig(int x, byte[] out, int off)
+ {
+ out[off++] = (byte)(x >>> 24);
+ out[off++] = (byte)(x >>> 16);
+ out[off++] = (byte)(x >>> 8);
+ out[off ] = (byte)x;
+ }
+
+ private int rotl(int x, int y)
+ {
+ return (x << y) | (x >>> (32-y));
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/NullEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/NullEngine.java
new file mode 100644
index 00000000..95a395a4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/NullEngine.java
@@ -0,0 +1,85 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+
+/**
+ * The no-op engine that just copies bytes through, irrespective of whether encrypting and decrypting.
+ * Provided for the sake of completeness.
+ */
+public class NullEngine implements BlockCipher
+{
+ private boolean initialised;
+ protected static final int BLOCK_SIZE = 1;
+
+ /**
+ * Standard constructor.
+ */
+ public NullEngine()
+ {
+ super();
+ }
+
+ /* (non-Javadoc)
+ * @see org.bouncycastle.crypto.BlockCipher#init(boolean, org.bouncycastle.crypto.CipherParameters)
+ */
+ public void init(boolean forEncryption, CipherParameters params) throws IllegalArgumentException
+ {
+ // we don't mind any parameters that may come in
+ this.initialised = true;
+ }
+
+ /* (non-Javadoc)
+ * @see org.bouncycastle.crypto.BlockCipher#getAlgorithmName()
+ */
+ public String getAlgorithmName()
+ {
+ return "Null";
+ }
+
+ /* (non-Javadoc)
+ * @see org.bouncycastle.crypto.BlockCipher#getBlockSize()
+ */
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ /* (non-Javadoc)
+ * @see org.bouncycastle.crypto.BlockCipher#processBlock(byte[], int, byte[], int)
+ */
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if (!initialised)
+ {
+ throw new IllegalStateException("Null engine not initialised");
+ }
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ for (int i = 0; i < BLOCK_SIZE; ++i)
+ {
+ out[outOff + i] = in[inOff + i];
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ /* (non-Javadoc)
+ * @see org.bouncycastle.crypto.BlockCipher#reset()
+ */
+ public void reset()
+ {
+ // nothing needs to be done
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RC2Engine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RC2Engine.java
new file mode 100644
index 00000000..02cb8811
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/RC2Engine.java
@@ -0,0 +1,317 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.RC2Parameters;
+
+/**
+ * an implementation of RC2 as described in RFC 2268
+ * "A Description of the RC2(r) Encryption Algorithm" R. Rivest.
+ */
+public class RC2Engine
+ implements BlockCipher
+{
+ //
+ // the values we use for key expansion (based on the digits of PI)
+ //
+ private static byte[] piTable =
+ {
+ (byte)0xd9, (byte)0x78, (byte)0xf9, (byte)0xc4, (byte)0x19, (byte)0xdd, (byte)0xb5, (byte)0xed,
+ (byte)0x28, (byte)0xe9, (byte)0xfd, (byte)0x79, (byte)0x4a, (byte)0xa0, (byte)0xd8, (byte)0x9d,
+ (byte)0xc6, (byte)0x7e, (byte)0x37, (byte)0x83, (byte)0x2b, (byte)0x76, (byte)0x53, (byte)0x8e,
+ (byte)0x62, (byte)0x4c, (byte)0x64, (byte)0x88, (byte)0x44, (byte)0x8b, (byte)0xfb, (byte)0xa2,
+ (byte)0x17, (byte)0x9a, (byte)0x59, (byte)0xf5, (byte)0x87, (byte)0xb3, (byte)0x4f, (byte)0x13,
+ (byte)0x61, (byte)0x45, (byte)0x6d, (byte)0x8d, (byte)0x9, (byte)0x81, (byte)0x7d, (byte)0x32,
+ (byte)0xbd, (byte)0x8f, (byte)0x40, (byte)0xeb, (byte)0x86, (byte)0xb7, (byte)0x7b, (byte)0xb,
+ (byte)0xf0, (byte)0x95, (byte)0x21, (byte)0x22, (byte)0x5c, (byte)0x6b, (byte)0x4e, (byte)0x82,
+ (byte)0x54, (byte)0xd6, (byte)0x65, (byte)0x93, (byte)0xce, (byte)0x60, (byte)0xb2, (byte)0x1c,
+ (byte)0x73, (byte)0x56, (byte)0xc0, (byte)0x14, (byte)0xa7, (byte)0x8c, (byte)0xf1, (byte)0xdc,
+ (byte)0x12, (byte)0x75, (byte)0xca, (byte)0x1f, (byte)0x3b, (byte)0xbe, (byte)0xe4, (byte)0xd1,
+ (byte)0x42, (byte)0x3d, (byte)0xd4, (byte)0x30, (byte)0xa3, (byte)0x3c, (byte)0xb6, (byte)0x26,
+ (byte)0x6f, (byte)0xbf, (byte)0xe, (byte)0xda, (byte)0x46, (byte)0x69, (byte)0x7, (byte)0x57,
+ (byte)0x27, (byte)0xf2, (byte)0x1d, (byte)0x9b, (byte)0xbc, (byte)0x94, (byte)0x43, (byte)0x3,
+ (byte)0xf8, (byte)0x11, (byte)0xc7, (byte)0xf6, (byte)0x90, (byte)0xef, (byte)0x3e, (byte)0xe7,
+ (byte)0x6, (byte)0xc3, (byte)0xd5, (byte)0x2f, (byte)0xc8, (byte)0x66, (byte)0x1e, (byte)0xd7,
+ (byte)0x8, (byte)0xe8, (byte)0xea, (byte)0xde, (byte)0x80, (byte)0x52, (byte)0xee, (byte)0xf7,
+ (byte)0x84, (byte)0xaa, (byte)0x72, (byte)0xac, (byte)0x35, (byte)0x4d, (byte)0x6a, (byte)0x2a,
+ (byte)0x96, (byte)0x1a, (byte)0xd2, (byte)0x71, (byte)0x5a, (byte)0x15, (byte)0x49, (byte)0x74,
+ (byte)0x4b, (byte)0x9f, (byte)0xd0, (byte)0x5e, (byte)0x4, (byte)0x18, (byte)0xa4, (byte)0xec,
+ (byte)0xc2, (byte)0xe0, (byte)0x41, (byte)0x6e, (byte)0xf, (byte)0x51, (byte)0xcb, (byte)0xcc,
+ (byte)0x24, (byte)0x91, (byte)0xaf, (byte)0x50, (byte)0xa1, (byte)0xf4, (byte)0x70, (byte)0x39,
+ (byte)0x99, (byte)0x7c, (byte)0x3a, (byte)0x85, (byte)0x23, (byte)0xb8, (byte)0xb4, (byte)0x7a,
+ (byte)0xfc, (byte)0x2, (byte)0x36, (byte)0x5b, (byte)0x25, (byte)0x55, (byte)0x97, (byte)0x31,
+ (byte)0x2d, (byte)0x5d, (byte)0xfa, (byte)0x98, (byte)0xe3, (byte)0x8a, (byte)0x92, (byte)0xae,
+ (byte)0x5, (byte)0xdf, (byte)0x29, (byte)0x10, (byte)0x67, (byte)0x6c, (byte)0xba, (byte)0xc9,
+ (byte)0xd3, (byte)0x0, (byte)0xe6, (byte)0xcf, (byte)0xe1, (byte)0x9e, (byte)0xa8, (byte)0x2c,
+ (byte)0x63, (byte)0x16, (byte)0x1, (byte)0x3f, (byte)0x58, (byte)0xe2, (byte)0x89, (byte)0xa9,
+ (byte)0xd, (byte)0x38, (byte)0x34, (byte)0x1b, (byte)0xab, (byte)0x33, (byte)0xff, (byte)0xb0,
+ (byte)0xbb, (byte)0x48, (byte)0xc, (byte)0x5f, (byte)0xb9, (byte)0xb1, (byte)0xcd, (byte)0x2e,
+ (byte)0xc5, (byte)0xf3, (byte)0xdb, (byte)0x47, (byte)0xe5, (byte)0xa5, (byte)0x9c, (byte)0x77,
+ (byte)0xa, (byte)0xa6, (byte)0x20, (byte)0x68, (byte)0xfe, (byte)0x7f, (byte)0xc1, (byte)0xad
+ };
+
+ private static final int BLOCK_SIZE = 8;
+
+ private int[] workingKey;
+ private boolean encrypting;
+
+ private int[] generateWorkingKey(
+ byte[] key,
+ int bits)
+ {
+ int x;
+ int[] xKey = new int[128];
+
+ for (int i = 0; i != key.length; i++)
+ {
+ xKey[i] = key[i] & 0xff;
+ }
+
+ // Phase 1: Expand input key to 128 bytes
+ int len = key.length;
+
+ if (len < 128)
+ {
+ int index = 0;
+
+ x = xKey[len - 1];
+
+ do
+ {
+ x = piTable[(x + xKey[index++]) & 255] & 0xff;
+ xKey[len++] = x;
+ }
+ while (len < 128);
+ }
+
+ // Phase 2 - reduce effective key size to "bits"
+ len = (bits + 7) >> 3;
+ x = piTable[xKey[128 - len] & (255 >> (7 & -bits))] & 0xff;
+ xKey[128 - len] = x;
+
+ for (int i = 128 - len - 1; i >= 0; i--)
+ {
+ x = piTable[x ^ xKey[i + len]] & 0xff;
+ xKey[i] = x;
+ }
+
+ // Phase 3 - copy to newKey in little-endian order
+ int[] newKey = new int[64];
+
+ for (int i = 0; i != newKey.length; i++)
+ {
+ newKey[i] = (xKey[2 * i] + (xKey[2 * i + 1] << 8));
+ }
+
+ return newKey;
+ }
+
+ /**
+ * initialise a RC2 cipher.
+ *
+ * @param encrypting whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ {
+ this.encrypting = encrypting;
+
+ if (params instanceof RC2Parameters)
+ {
+ RC2Parameters param = (RC2Parameters)params;
+
+ workingKey = generateWorkingKey(param.getKey(),
+ param.getEffectiveKeyBits());
+ }
+ else if (params instanceof KeyParameter)
+ {
+ byte[] key = ((KeyParameter)params).getKey();
+
+ workingKey = generateWorkingKey(key, key.length * 8);
+ }
+ else
+ {
+ throw new IllegalArgumentException("invalid parameter passed to RC2 init - " + params.getClass().getName());
+ }
+
+ }
+
+ public void reset()
+ {
+ }
+
+ public String getAlgorithmName()
+ {
+ return "RC2";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public final int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new IllegalStateException("RC2 engine not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (encrypting)
+ {
+ encryptBlock(in, inOff, out, outOff);
+ }
+ else
+ {
+ decryptBlock(in, inOff, out, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * return the result rotating the 16 bit number in x left by y
+ */
+ private int rotateWordLeft(
+ int x,
+ int y)
+ {
+ x &= 0xffff;
+ return (x << y) | (x >> (16 - y));
+ }
+
+ private void encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int x76, x54, x32, x10;
+
+ x76 = ((in[inOff + 7] & 0xff) << 8) + (in[inOff + 6] & 0xff);
+ x54 = ((in[inOff + 5] & 0xff) << 8) + (in[inOff + 4] & 0xff);
+ x32 = ((in[inOff + 3] & 0xff) << 8) + (in[inOff + 2] & 0xff);
+ x10 = ((in[inOff + 1] & 0xff) << 8) + (in[inOff + 0] & 0xff);
+
+ for (int i = 0; i <= 16; i += 4)
+ {
+ x10 = rotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i ], 1);
+ x32 = rotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2);
+ x54 = rotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3);
+ x76 = rotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5);
+ }
+
+ x10 += workingKey[x76 & 63];
+ x32 += workingKey[x10 & 63];
+ x54 += workingKey[x32 & 63];
+ x76 += workingKey[x54 & 63];
+
+ for (int i = 20; i <= 40; i += 4)
+ {
+ x10 = rotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i ], 1);
+ x32 = rotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2);
+ x54 = rotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3);
+ x76 = rotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5);
+ }
+
+ x10 += workingKey[x76 & 63];
+ x32 += workingKey[x10 & 63];
+ x54 += workingKey[x32 & 63];
+ x76 += workingKey[x54 & 63];
+
+ for (int i = 44; i < 64; i += 4)
+ {
+ x10 = rotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i ], 1);
+ x32 = rotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2);
+ x54 = rotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3);
+ x76 = rotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5);
+ }
+
+ out[outOff + 0] = (byte)x10;
+ out[outOff + 1] = (byte)(x10 >> 8);
+ out[outOff + 2] = (byte)x32;
+ out[outOff + 3] = (byte)(x32 >> 8);
+ out[outOff + 4] = (byte)x54;
+ out[outOff + 5] = (byte)(x54 >> 8);
+ out[outOff + 6] = (byte)x76;
+ out[outOff + 7] = (byte)(x76 >> 8);
+ }
+
+ private void decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int x76, x54, x32, x10;
+
+ x76 = ((in[inOff + 7] & 0xff) << 8) + (in[inOff + 6] & 0xff);
+ x54 = ((in[inOff + 5] & 0xff) << 8) + (in[inOff + 4] & 0xff);
+ x32 = ((in[inOff + 3] & 0xff) << 8) + (in[inOff + 2] & 0xff);
+ x10 = ((in[inOff + 1] & 0xff) << 8) + (in[inOff + 0] & 0xff);
+
+ for (int i = 60; i >= 44; i -= 4)
+ {
+ x76 = rotateWordLeft(x76, 11) - ((x10 & ~x54) + (x32 & x54) + workingKey[i+3]);
+ x54 = rotateWordLeft(x54, 13) - ((x76 & ~x32) + (x10 & x32) + workingKey[i+2]);
+ x32 = rotateWordLeft(x32, 14) - ((x54 & ~x10) + (x76 & x10) + workingKey[i+1]);
+ x10 = rotateWordLeft(x10, 15) - ((x32 & ~x76) + (x54 & x76) + workingKey[i ]);
+ }
+
+ x76 -= workingKey[x54 & 63];
+ x54 -= workingKey[x32 & 63];
+ x32 -= workingKey[x10 & 63];
+ x10 -= workingKey[x76 & 63];
+
+ for (int i = 40; i >= 20; i -= 4)
+ {
+ x76 = rotateWordLeft(x76, 11) - ((x10 & ~x54) + (x32 & x54) + workingKey[i+3]);
+ x54 = rotateWordLeft(x54, 13) - ((x76 & ~x32) + (x10 & x32) + workingKey[i+2]);
+ x32 = rotateWordLeft(x32, 14) - ((x54 & ~x10) + (x76 & x10) + workingKey[i+1]);
+ x10 = rotateWordLeft(x10, 15) - ((x32 & ~x76) + (x54 & x76) + workingKey[i ]);
+ }
+
+ x76 -= workingKey[x54 & 63];
+ x54 -= workingKey[x32 & 63];
+ x32 -= workingKey[x10 & 63];
+ x10 -= workingKey[x76 & 63];
+
+ for (int i = 16; i >= 0; i -= 4)
+ {
+ x76 = rotateWordLeft(x76, 11) - ((x10 & ~x54) + (x32 & x54) + workingKey[i+3]);
+ x54 = rotateWordLeft(x54, 13) - ((x76 & ~x32) + (x10 & x32) + workingKey[i+2]);
+ x32 = rotateWordLeft(x32, 14) - ((x54 & ~x10) + (x76 & x10) + workingKey[i+1]);
+ x10 = rotateWordLeft(x10, 15) - ((x32 & ~x76) + (x54 & x76) + workingKey[i ]);
+ }
+
+ out[outOff + 0] = (byte)x10;
+ out[outOff + 1] = (byte)(x10 >> 8);
+ out[outOff + 2] = (byte)x32;
+ out[outOff + 3] = (byte)(x32 >> 8);
+ out[outOff + 4] = (byte)x54;
+ out[outOff + 5] = (byte)(x54 >> 8);
+ out[outOff + 6] = (byte)x76;
+ out[outOff + 7] = (byte)(x76 >> 8);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RC2WrapEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RC2WrapEngine.java
new file mode 100644
index 00000000..185387d8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/RC2WrapEngine.java
@@ -0,0 +1,383 @@
+package org.bouncycastle.crypto.engines;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.Wrapper;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Wrap keys according to RFC 3217 - RC2 mechanism
+ */
+public class RC2WrapEngine
+ implements Wrapper
+{
+ /** Field engine */
+ private CBCBlockCipher engine;
+
+ /** Field param */
+ private CipherParameters param;
+
+ /** Field paramPlusIV */
+ private ParametersWithIV paramPlusIV;
+
+ /** Field iv */
+ private byte[] iv;
+
+ /** Field forWrapping */
+ private boolean forWrapping;
+
+ private SecureRandom sr;
+
+ /** Field IV2 */
+ private static final byte[] IV2 = { (byte) 0x4a, (byte) 0xdd, (byte) 0xa2,
+ (byte) 0x2c, (byte) 0x79, (byte) 0xe8,
+ (byte) 0x21, (byte) 0x05 };
+
+ //
+ // checksum digest
+ //
+ Digest sha1 = new SHA1Digest();
+ byte[] digest = new byte[20];
+
+ /**
+ * Method init
+ *
+ * @param forWrapping
+ * @param param
+ */
+ public void init(boolean forWrapping, CipherParameters param)
+ {
+ this.forWrapping = forWrapping;
+ this.engine = new CBCBlockCipher(new RC2Engine());
+
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom pWithR = (ParametersWithRandom)param;
+ sr = pWithR.getRandom();
+ param = pWithR.getParameters();
+ }
+ else
+ {
+ sr = new SecureRandom();
+ }
+
+ if (param instanceof ParametersWithIV)
+ {
+ this.paramPlusIV = (ParametersWithIV)param;
+ this.iv = this.paramPlusIV.getIV();
+ this.param = this.paramPlusIV.getParameters();
+
+ if (this.forWrapping)
+ {
+ if ((this.iv == null) || (this.iv.length != 8))
+ {
+ throw new IllegalArgumentException("IV is not 8 octets");
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "You should not supply an IV for unwrapping");
+ }
+ }
+ else
+ {
+ this.param = param;
+
+ if (this.forWrapping)
+ {
+
+ // Hm, we have no IV but we want to wrap ?!?
+ // well, then we have to create our own IV.
+ this.iv = new byte[8];
+
+ sr.nextBytes(iv);
+
+ this.paramPlusIV = new ParametersWithIV(this.param, this.iv);
+ }
+ }
+
+ }
+
+ /**
+ * Method getAlgorithmName
+ *
+ * @return the algorithm name "RC2".
+ */
+ public String getAlgorithmName()
+ {
+ return "RC2";
+ }
+
+ /**
+ * Method wrap
+ *
+ * @param in
+ * @param inOff
+ * @param inLen
+ * @return the wrapped bytes.
+ */
+ public byte[] wrap(byte[] in, int inOff, int inLen)
+ {
+
+ if (!forWrapping)
+ {
+ throw new IllegalStateException("Not initialized for wrapping");
+ }
+
+ int length = inLen + 1;
+ if ((length % 8) != 0)
+ {
+ length += 8 - (length % 8);
+ }
+
+ byte keyToBeWrapped[] = new byte[length];
+
+ keyToBeWrapped[0] = (byte)inLen;
+ System.arraycopy(in, inOff, keyToBeWrapped, 1, inLen);
+
+ byte[] pad = new byte[keyToBeWrapped.length - inLen - 1];
+
+ if (pad.length > 0)
+ {
+ sr.nextBytes(pad);
+ System.arraycopy(pad, 0, keyToBeWrapped, inLen + 1, pad.length);
+ }
+
+ // Compute the CMS Key Checksum, (section 5.6.1), call this CKS.
+ byte[] CKS = calculateCMSKeyChecksum(keyToBeWrapped);
+
+ // Let WKCKS = WK || CKS where || is concatenation.
+ byte[] WKCKS = new byte[keyToBeWrapped.length + CKS.length];
+
+ System.arraycopy(keyToBeWrapped, 0, WKCKS, 0, keyToBeWrapped.length);
+ System.arraycopy(CKS, 0, WKCKS, keyToBeWrapped.length, CKS.length);
+
+ // Encrypt WKCKS in CBC mode using KEK as the key and IV as the
+ // initialization vector. Call the results TEMP1.
+ byte TEMP1[] = new byte[WKCKS.length];
+
+ System.arraycopy(WKCKS, 0, TEMP1, 0, WKCKS.length);
+
+ int noOfBlocks = WKCKS.length / engine.getBlockSize();
+ int extraBytes = WKCKS.length % engine.getBlockSize();
+
+ if (extraBytes != 0)
+ {
+ throw new IllegalStateException("Not multiple of block length");
+ }
+
+ engine.init(true, paramPlusIV);
+
+ for (int i = 0; i < noOfBlocks; i++)
+ {
+ int currentBytePos = i * engine.getBlockSize();
+
+ engine.processBlock(TEMP1, currentBytePos, TEMP1, currentBytePos);
+ }
+
+ // Left TEMP2 = IV || TEMP1.
+ byte[] TEMP2 = new byte[this.iv.length + TEMP1.length];
+
+ System.arraycopy(this.iv, 0, TEMP2, 0, this.iv.length);
+ System.arraycopy(TEMP1, 0, TEMP2, this.iv.length, TEMP1.length);
+
+ // Reverse the order of the octets in TEMP2 and call the result TEMP3.
+ byte[] TEMP3 = new byte[TEMP2.length];
+
+ for (int i = 0; i < TEMP2.length; i++)
+ {
+ TEMP3[i] = TEMP2[TEMP2.length - (i + 1)];
+ }
+
+ // Encrypt TEMP3 in CBC mode using the KEK and an initialization vector
+ // of 0x 4a dd a2 2c 79 e8 21 05. The resulting cipher text is the
+ // desired
+ // result. It is 40 octets long if a 168 bit key is being wrapped.
+ ParametersWithIV param2 = new ParametersWithIV(this.param, IV2);
+
+ this.engine.init(true, param2);
+
+ for (int i = 0; i < noOfBlocks + 1; i++)
+ {
+ int currentBytePos = i * engine.getBlockSize();
+
+ engine.processBlock(TEMP3, currentBytePos, TEMP3, currentBytePos);
+ }
+
+ return TEMP3;
+ }
+
+ /**
+ * Method unwrap
+ *
+ * @param in
+ * @param inOff
+ * @param inLen
+ * @return the unwrapped bytes.
+ * @throws InvalidCipherTextException
+ */
+ public byte[] unwrap(byte[] in, int inOff, int inLen)
+ throws InvalidCipherTextException
+ {
+
+ if (forWrapping)
+ {
+ throw new IllegalStateException("Not set for unwrapping");
+ }
+
+ if (in == null)
+ {
+ throw new InvalidCipherTextException("Null pointer as ciphertext");
+ }
+
+ if (inLen % engine.getBlockSize() != 0)
+ {
+ throw new InvalidCipherTextException("Ciphertext not multiple of "
+ + engine.getBlockSize());
+ }
+
+ /*
+ * // Check if the length of the cipher text is reasonable given the key //
+ * type. It must be 40 bytes for a 168 bit key and either 32, 40, or //
+ * 48 bytes for a 128, 192, or 256 bit key. If the length is not
+ * supported // or inconsistent with the algorithm for which the key is
+ * intended, // return error. // // we do not accept 168 bit keys. it
+ * has to be 192 bit. int lengthA = (estimatedKeyLengthInBit / 8) + 16;
+ * int lengthB = estimatedKeyLengthInBit % 8;
+ *
+ * if ((lengthA != keyToBeUnwrapped.length) || (lengthB != 0)) { throw
+ * new XMLSecurityException("empty"); }
+ */
+
+ // Decrypt the cipher text with TRIPLedeS in CBC mode using the KEK
+ // and an initialization vector (IV) of 0x4adda22c79e82105. Call the
+ // output TEMP3.
+ ParametersWithIV param2 = new ParametersWithIV(this.param, IV2);
+
+ this.engine.init(false, param2);
+
+ byte TEMP3[] = new byte[inLen];
+
+ System.arraycopy(in, inOff, TEMP3, 0, inLen);
+
+ for (int i = 0; i < (TEMP3.length / engine.getBlockSize()); i++)
+ {
+ int currentBytePos = i * engine.getBlockSize();
+
+ engine.processBlock(TEMP3, currentBytePos, TEMP3, currentBytePos);
+ }
+
+ // Reverse the order of the octets in TEMP3 and call the result TEMP2.
+ byte[] TEMP2 = new byte[TEMP3.length];
+
+ for (int i = 0; i < TEMP3.length; i++)
+ {
+ TEMP2[i] = TEMP3[TEMP3.length - (i + 1)];
+ }
+
+ // Decompose TEMP2 into IV, the first 8 octets, and TEMP1, the remaining
+ // octets.
+ this.iv = new byte[8];
+
+ byte[] TEMP1 = new byte[TEMP2.length - 8];
+
+ System.arraycopy(TEMP2, 0, this.iv, 0, 8);
+ System.arraycopy(TEMP2, 8, TEMP1, 0, TEMP2.length - 8);
+
+ // Decrypt TEMP1 using TRIPLedeS in CBC mode using the KEK and the IV
+ // found in the previous step. Call the result WKCKS.
+ this.paramPlusIV = new ParametersWithIV(this.param, this.iv);
+
+ this.engine.init(false, this.paramPlusIV);
+
+ byte[] LCEKPADICV = new byte[TEMP1.length];
+
+ System.arraycopy(TEMP1, 0, LCEKPADICV, 0, TEMP1.length);
+
+ for (int i = 0; i < (LCEKPADICV.length / engine.getBlockSize()); i++)
+ {
+ int currentBytePos = i * engine.getBlockSize();
+
+ engine.processBlock(LCEKPADICV, currentBytePos, LCEKPADICV,
+ currentBytePos);
+ }
+
+ // Decompose LCEKPADICV. CKS is the last 8 octets and WK, the wrapped
+ // key, are
+ // those octets before the CKS.
+ byte[] result = new byte[LCEKPADICV.length - 8];
+ byte[] CKStoBeVerified = new byte[8];
+
+ System.arraycopy(LCEKPADICV, 0, result, 0, LCEKPADICV.length - 8);
+ System.arraycopy(LCEKPADICV, LCEKPADICV.length - 8, CKStoBeVerified, 0,
+ 8);
+
+ // Calculate a CMS Key Checksum, (section 5.6.1), over the WK and
+ // compare
+ // with the CKS extracted in the above step. If they are not equal,
+ // return error.
+ if (!checkCMSKeyChecksum(result, CKStoBeVerified))
+ {
+ throw new InvalidCipherTextException(
+ "Checksum inside ciphertext is corrupted");
+ }
+
+ if ((result.length - ((result[0] & 0xff) + 1)) > 7)
+ {
+ throw new InvalidCipherTextException("too many pad bytes ("
+ + (result.length - ((result[0] & 0xff) + 1)) + ")");
+ }
+
+ // CEK is the wrapped key, now extracted for use in data decryption.
+ byte[] CEK = new byte[result[0]];
+ System.arraycopy(result, 1, CEK, 0, CEK.length);
+ return CEK;
+ }
+
+ /**
+ * Some key wrap algorithms make use of the Key Checksum defined
+ * in CMS [CMS-Algorithms]. This is used to provide an integrity
+ * check value for the key being wrapped. The algorithm is
+ *
+ * - Compute the 20 octet SHA-1 hash on the key being wrapped.
+ * - Use the first 8 octets of this hash as the checksum value.
+ *
+ * @param key
+ * @return
+ * @throws RuntimeException
+ * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
+ */
+ private byte[] calculateCMSKeyChecksum(
+ byte[] key)
+ {
+ byte[] result = new byte[8];
+
+ sha1.update(key, 0, key.length);
+ sha1.doFinal(digest, 0);
+
+ System.arraycopy(digest, 0, result, 0, 8);
+
+ return result;
+ }
+
+ /**
+ * @param key
+ * @param checksum
+ * @return
+ * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
+ */
+ private boolean checkCMSKeyChecksum(
+ byte[] key,
+ byte[] checksum)
+ {
+ return Arrays.constantTimeAreEqual(calculateCMSKeyChecksum(key), checksum);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RC4Engine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RC4Engine.java
new file mode 100644
index 00000000..4de7ea69
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/RC4Engine.java
@@ -0,0 +1,144 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+public class RC4Engine implements StreamCipher
+{
+ private final static int STATE_LENGTH = 256;
+
+ /*
+ * variables to hold the state of the RC4 engine
+ * during encryption and decryption
+ */
+
+ private byte[] engineState = null;
+ private int x = 0;
+ private int y = 0;
+ private byte[] workingKey = null;
+
+ /**
+ * initialise a RC4 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params
+ )
+ {
+ if (params instanceof KeyParameter)
+ {
+ /*
+ * RC4 encryption and decryption is completely
+ * symmetrical, so the 'forEncryption' is
+ * irrelevant.
+ */
+ workingKey = ((KeyParameter)params).getKey();
+ setKey(workingKey);
+
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to RC4 init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "RC4";
+ }
+
+ public byte returnByte(byte in)
+ {
+ x = (x + 1) & 0xff;
+ y = (engineState[x] + y) & 0xff;
+
+ // swap
+ byte tmp = engineState[x];
+ engineState[x] = engineState[y];
+ engineState[y] = tmp;
+
+ // xor
+ return (byte)(in ^ engineState[(engineState[x] + engineState[y]) & 0xff]);
+ }
+
+ public void processBytes(
+ byte[] in,
+ int inOff,
+ int len,
+ byte[] out,
+ int outOff)
+ {
+ if ((inOff + len) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + len) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ for (int i = 0; i < len ; i++)
+ {
+ x = (x + 1) & 0xff;
+ y = (engineState[x] + y) & 0xff;
+
+ // swap
+ byte tmp = engineState[x];
+ engineState[x] = engineState[y];
+ engineState[y] = tmp;
+
+ // xor
+ out[i+outOff] = (byte)(in[i + inOff]
+ ^ engineState[(engineState[x] + engineState[y]) & 0xff]);
+ }
+ }
+
+ public void reset()
+ {
+ setKey(workingKey);
+ }
+
+ // Private implementation
+
+ private void setKey(byte[] keyBytes)
+ {
+ workingKey = keyBytes;
+
+ // System.out.println("the key length is ; "+ workingKey.length);
+
+ x = 0;
+ y = 0;
+
+ if (engineState == null)
+ {
+ engineState = new byte[STATE_LENGTH];
+ }
+
+ // reset the state of the engine
+ for (int i=0; i < STATE_LENGTH; i++)
+ {
+ engineState[i] = (byte)i;
+ }
+
+ int i1 = 0;
+ int i2 = 0;
+
+ for (int i=0; i < STATE_LENGTH; i++)
+ {
+ i2 = ((keyBytes[i1] & 0xff) + engineState[i] + i2) & 0xff;
+ // do the byte-swap inline
+ byte tmp = engineState[i];
+ engineState[i] = engineState[i2];
+ engineState[i2] = tmp;
+ i1 = (i1+1) % keyBytes.length;
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RC532Engine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RC532Engine.java
new file mode 100644
index 00000000..9fb6f550
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/RC532Engine.java
@@ -0,0 +1,287 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.RC5Parameters;
+
+/**
+ * The specification for RC5 came from the <code>RC5 Encryption Algorithm</code>
+ * publication in RSA CryptoBytes, Spring of 1995.
+ * <em>http://www.rsasecurity.com/rsalabs/cryptobytes</em>.
+ * <p>
+ * This implementation has a word size of 32 bits.
+ * <p>
+ * Implementation courtesy of Tito Pena.
+ */
+public class RC532Engine
+ implements BlockCipher
+{
+ /*
+ * the number of rounds to perform
+ */
+ private int _noRounds;
+
+ /*
+ * the expanded key array of size 2*(rounds + 1)
+ */
+ private int _S[];
+
+ /*
+ * our "magic constants" for 32 32
+ *
+ * Pw = Odd((e-2) * 2^wordsize)
+ * Qw = Odd((o-2) * 2^wordsize)
+ *
+ * where e is the base of natural logarithms (2.718281828...)
+ * and o is the golden ratio (1.61803398...)
+ */
+ private static final int P32 = 0xb7e15163;
+ private static final int Q32 = 0x9e3779b9;
+
+ private boolean forEncryption;
+
+ /**
+ * Create an instance of the RC5 encryption algorithm
+ * and set some defaults
+ */
+ public RC532Engine()
+ {
+ _noRounds = 12; // the default
+ _S = null;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "RC5-32";
+ }
+
+ public int getBlockSize()
+ {
+ return 2 * 4;
+ }
+
+ /**
+ * initialise a RC5-32 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (params instanceof RC5Parameters)
+ {
+ RC5Parameters p = (RC5Parameters)params;
+
+ _noRounds = p.getRounds();
+
+ setKey(p.getKey());
+ }
+ else if (params instanceof KeyParameter)
+ {
+ KeyParameter p = (KeyParameter)params;
+
+ setKey(p.getKey());
+ }
+ else
+ {
+ throw new IllegalArgumentException("invalid parameter passed to RC532 init - " + params.getClass().getName());
+ }
+
+ this.forEncryption = forEncryption;
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ return (forEncryption) ? encryptBlock(in, inOff, out, outOff)
+ : decryptBlock(in, inOff, out, outOff);
+ }
+
+ public void reset()
+ {
+ }
+
+ /**
+ * Re-key the cipher.
+ * <p>
+ * @param key the key to be used
+ */
+ private void setKey(
+ byte[] key)
+ {
+ //
+ // KEY EXPANSION:
+ //
+ // There are 3 phases to the key expansion.
+ //
+ // Phase 1:
+ // Copy the secret key K[0...b-1] into an array L[0..c-1] of
+ // c = ceil(b/u), where u = 32/8 in little-endian order.
+ // In other words, we fill up L using u consecutive key bytes
+ // of K. Any unfilled byte positions in L are zeroed. In the
+ // case that b = c = 0, set c = 1 and L[0] = 0.
+ //
+ int[] L = new int[(key.length + (4 - 1)) / 4];
+
+ for (int i = 0; i != key.length; i++)
+ {
+ L[i / 4] += (key[i] & 0xff) << (8 * (i % 4));
+ }
+
+ //
+ // Phase 2:
+ // Initialize S to a particular fixed pseudo-random bit pattern
+ // using an arithmetic progression modulo 2^wordsize determined
+ // by the magic numbers, Pw & Qw.
+ //
+ _S = new int[2*(_noRounds + 1)];
+
+ _S[0] = P32;
+ for (int i=1; i < _S.length; i++)
+ {
+ _S[i] = (_S[i-1] + Q32);
+ }
+
+ //
+ // Phase 3:
+ // Mix in the user's secret key in 3 passes over the arrays S & L.
+ // The max of the arrays sizes is used as the loop control
+ //
+ int iter;
+
+ if (L.length > _S.length)
+ {
+ iter = 3 * L.length;
+ }
+ else
+ {
+ iter = 3 * _S.length;
+ }
+
+ int A = 0, B = 0;
+ int i = 0, j = 0;
+
+ for (int k = 0; k < iter; k++)
+ {
+ A = _S[i] = rotateLeft(_S[i] + A + B, 3);
+ B = L[j] = rotateLeft(L[j] + A + B, A+B);
+ i = (i+1) % _S.length;
+ j = (j+1) % L.length;
+ }
+ }
+
+ /**
+ * Encrypt the given block starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ * <p>
+ * @param in in byte buffer containing data to encrypt
+ * @param inOff offset into src buffer
+ * @param out out buffer where encrypted data is written
+ * @param outOff offset into out buffer
+ */
+ private int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int A = bytesToWord(in, inOff) + _S[0];
+ int B = bytesToWord(in, inOff + 4) + _S[1];
+
+ for (int i = 1; i <= _noRounds; i++)
+ {
+ A = rotateLeft(A ^ B, B) + _S[2*i];
+ B = rotateLeft(B ^ A, A) + _S[2*i+1];
+ }
+
+ wordToBytes(A, out, outOff);
+ wordToBytes(B, out, outOff + 4);
+
+ return 2 * 4;
+ }
+
+ private int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int A = bytesToWord(in, inOff);
+ int B = bytesToWord(in, inOff + 4);
+
+ for (int i = _noRounds; i >= 1; i--)
+ {
+ B = rotateRight(B - _S[2*i+1], A) ^ A;
+ A = rotateRight(A - _S[2*i], B) ^ B;
+ }
+
+ wordToBytes(A - _S[0], out, outOff);
+ wordToBytes(B - _S[1], out, outOff + 4);
+
+ return 2 * 4;
+ }
+
+
+ //////////////////////////////////////////////////////////////
+ //
+ // PRIVATE Helper Methods
+ //
+ //////////////////////////////////////////////////////////////
+
+ /**
+ * Perform a left "spin" of the word. The rotation of the given
+ * word <em>x</em> is rotated left by <em>y</em> bits.
+ * Only the <em>lg(32)</em> low-order bits of <em>y</em>
+ * are used to determine the rotation amount. Here it is
+ * assumed that the wordsize used is a power of 2.
+ * <p>
+ * @param x word to rotate
+ * @param y number of bits to rotate % 32
+ */
+ private int rotateLeft(int x, int y)
+ {
+ return ((x << (y & (32-1))) | (x >>> (32 - (y & (32-1)))));
+ }
+
+ /**
+ * Perform a right "spin" of the word. The rotation of the given
+ * word <em>x</em> is rotated left by <em>y</em> bits.
+ * Only the <em>lg(32)</em> low-order bits of <em>y</em>
+ * are used to determine the rotation amount. Here it is
+ * assumed that the wordsize used is a power of 2.
+ * <p>
+ * @param x word to rotate
+ * @param y number of bits to rotate % 32
+ */
+ private int rotateRight(int x, int y)
+ {
+ return ((x >>> (y & (32-1))) | (x << (32 - (y & (32-1)))));
+ }
+
+ private int bytesToWord(
+ byte[] src,
+ int srcOff)
+ {
+ return (src[srcOff] & 0xff) | ((src[srcOff + 1] & 0xff) << 8)
+ | ((src[srcOff + 2] & 0xff) << 16) | ((src[srcOff + 3] & 0xff) << 24);
+ }
+
+ private void wordToBytes(
+ int word,
+ byte[] dst,
+ int dstOff)
+ {
+ dst[dstOff] = (byte)word;
+ dst[dstOff + 1] = (byte)(word >> 8);
+ dst[dstOff + 2] = (byte)(word >> 16);
+ dst[dstOff + 3] = (byte)(word >> 24);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RC564Engine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RC564Engine.java
new file mode 100644
index 00000000..2121a4bc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/RC564Engine.java
@@ -0,0 +1,288 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.RC5Parameters;
+
+/**
+ * The specification for RC5 came from the <code>RC5 Encryption Algorithm</code>
+ * publication in RSA CryptoBytes, Spring of 1995.
+ * <em>http://www.rsasecurity.com/rsalabs/cryptobytes</em>.
+ * <p>
+ * This implementation is set to work with a 64 bit word size.
+ * <p>
+ * Implementation courtesy of Tito Pena.
+ */
+public class RC564Engine
+ implements BlockCipher
+{
+ private static final int wordSize = 64;
+ private static final int bytesPerWord = wordSize / 8;
+
+ /*
+ * the number of rounds to perform
+ */
+ private int _noRounds;
+
+ /*
+ * the expanded key array of size 2*(rounds + 1)
+ */
+ private long _S[];
+
+ /*
+ * our "magic constants" for wordSize 62
+ *
+ * Pw = Odd((e-2) * 2^wordsize)
+ * Qw = Odd((o-2) * 2^wordsize)
+ *
+ * where e is the base of natural logarithms (2.718281828...)
+ * and o is the golden ratio (1.61803398...)
+ */
+ private static final long P64 = 0xb7e151628aed2a6bL;
+ private static final long Q64 = 0x9e3779b97f4a7c15L;
+
+ private boolean forEncryption;
+
+ /**
+ * Create an instance of the RC5 encryption algorithm
+ * and set some defaults
+ */
+ public RC564Engine()
+ {
+ _noRounds = 12;
+ _S = null;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "RC5-64";
+ }
+
+ public int getBlockSize()
+ {
+ return 2 * bytesPerWord;
+ }
+
+ /**
+ * initialise a RC5-64 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (!(params instanceof RC5Parameters))
+ {
+ throw new IllegalArgumentException("invalid parameter passed to RC564 init - " + params.getClass().getName());
+ }
+
+ RC5Parameters p = (RC5Parameters)params;
+
+ this.forEncryption = forEncryption;
+
+ _noRounds = p.getRounds();
+
+ setKey(p.getKey());
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ return (forEncryption) ? encryptBlock(in, inOff, out, outOff)
+ : decryptBlock(in, inOff, out, outOff);
+ }
+
+ public void reset()
+ {
+ }
+
+ /**
+ * Re-key the cipher.
+ * <p>
+ * @param key the key to be used
+ */
+ private void setKey(
+ byte[] key)
+ {
+ //
+ // KEY EXPANSION:
+ //
+ // There are 3 phases to the key expansion.
+ //
+ // Phase 1:
+ // Copy the secret key K[0...b-1] into an array L[0..c-1] of
+ // c = ceil(b/u), where u = wordSize/8 in little-endian order.
+ // In other words, we fill up L using u consecutive key bytes
+ // of K. Any unfilled byte positions in L are zeroed. In the
+ // case that b = c = 0, set c = 1 and L[0] = 0.
+ //
+ long[] L = new long[(key.length + (bytesPerWord - 1)) / bytesPerWord];
+
+ for (int i = 0; i != key.length; i++)
+ {
+ L[i / bytesPerWord] += (long)(key[i] & 0xff) << (8 * (i % bytesPerWord));
+ }
+
+ //
+ // Phase 2:
+ // Initialize S to a particular fixed pseudo-random bit pattern
+ // using an arithmetic progression modulo 2^wordsize determined
+ // by the magic numbers, Pw & Qw.
+ //
+ _S = new long[2*(_noRounds + 1)];
+
+ _S[0] = P64;
+ for (int i=1; i < _S.length; i++)
+ {
+ _S[i] = (_S[i-1] + Q64);
+ }
+
+ //
+ // Phase 3:
+ // Mix in the user's secret key in 3 passes over the arrays S & L.
+ // The max of the arrays sizes is used as the loop control
+ //
+ int iter;
+
+ if (L.length > _S.length)
+ {
+ iter = 3 * L.length;
+ }
+ else
+ {
+ iter = 3 * _S.length;
+ }
+
+ long A = 0, B = 0;
+ int i = 0, j = 0;
+
+ for (int k = 0; k < iter; k++)
+ {
+ A = _S[i] = rotateLeft(_S[i] + A + B, 3);
+ B = L[j] = rotateLeft(L[j] + A + B, A+B);
+ i = (i+1) % _S.length;
+ j = (j+1) % L.length;
+ }
+ }
+
+ /**
+ * Encrypt the given block starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ * <p>
+ * @param in in byte buffer containing data to encrypt
+ * @param inOff offset into src buffer
+ * @param out out buffer where encrypted data is written
+ * @param outOff offset into out buffer
+ */
+ private int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ long A = bytesToWord(in, inOff) + _S[0];
+ long B = bytesToWord(in, inOff + bytesPerWord) + _S[1];
+
+ for (int i = 1; i <= _noRounds; i++)
+ {
+ A = rotateLeft(A ^ B, B) + _S[2*i];
+ B = rotateLeft(B ^ A, A) + _S[2*i+1];
+ }
+
+ wordToBytes(A, out, outOff);
+ wordToBytes(B, out, outOff + bytesPerWord);
+
+ return 2 * bytesPerWord;
+ }
+
+ private int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ long A = bytesToWord(in, inOff);
+ long B = bytesToWord(in, inOff + bytesPerWord);
+
+ for (int i = _noRounds; i >= 1; i--)
+ {
+ B = rotateRight(B - _S[2*i+1], A) ^ A;
+ A = rotateRight(A - _S[2*i], B) ^ B;
+ }
+
+ wordToBytes(A - _S[0], out, outOff);
+ wordToBytes(B - _S[1], out, outOff + bytesPerWord);
+
+ return 2 * bytesPerWord;
+ }
+
+
+ //////////////////////////////////////////////////////////////
+ //
+ // PRIVATE Helper Methods
+ //
+ //////////////////////////////////////////////////////////////
+
+ /**
+ * Perform a left "spin" of the word. The rotation of the given
+ * word <em>x</em> is rotated left by <em>y</em> bits.
+ * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em>
+ * are used to determine the rotation amount. Here it is
+ * assumed that the wordsize used is a power of 2.
+ * <p>
+ * @param x word to rotate
+ * @param y number of bits to rotate % wordSize
+ */
+ private long rotateLeft(long x, long y)
+ {
+ return ((x << (y & (wordSize-1))) | (x >>> (wordSize - (y & (wordSize-1)))));
+ }
+
+ /**
+ * Perform a right "spin" of the word. The rotation of the given
+ * word <em>x</em> is rotated left by <em>y</em> bits.
+ * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em>
+ * are used to determine the rotation amount. Here it is
+ * assumed that the wordsize used is a power of 2.
+ * <p>
+ * @param x word to rotate
+ * @param y number of bits to rotate % wordSize
+ */
+ private long rotateRight(long x, long y)
+ {
+ return ((x >>> (y & (wordSize-1))) | (x << (wordSize - (y & (wordSize-1)))));
+ }
+
+ private long bytesToWord(
+ byte[] src,
+ int srcOff)
+ {
+ long word = 0;
+
+ for (int i = bytesPerWord - 1; i >= 0; i--)
+ {
+ word = (word << 8) + (src[i + srcOff] & 0xff);
+ }
+
+ return word;
+ }
+
+ private void wordToBytes(
+ long word,
+ byte[] dst,
+ int dstOff)
+ {
+ for (int i = 0; i < bytesPerWord; i++)
+ {
+ dst[i + dstOff] = (byte)word;
+ word >>>= 8;
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RC6Engine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RC6Engine.java
new file mode 100644
index 00000000..bbf5d306
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/RC6Engine.java
@@ -0,0 +1,363 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * An RC6 engine.
+ */
+public class RC6Engine
+ implements BlockCipher
+{
+ private static final int wordSize = 32;
+ private static final int bytesPerWord = wordSize / 8;
+
+ /*
+ * the number of rounds to perform
+ */
+ private static final int _noRounds = 20;
+
+ /*
+ * the expanded key array of size 2*(rounds + 1)
+ */
+ private int _S[];
+
+ /*
+ * our "magic constants" for wordSize 32
+ *
+ * Pw = Odd((e-2) * 2^wordsize)
+ * Qw = Odd((o-2) * 2^wordsize)
+ *
+ * where e is the base of natural logarithms (2.718281828...)
+ * and o is the golden ratio (1.61803398...)
+ */
+ private static final int P32 = 0xb7e15163;
+ private static final int Q32 = 0x9e3779b9;
+
+ private static final int LGW = 5; // log2(32)
+
+ private boolean forEncryption;
+
+ /**
+ * Create an instance of the RC6 encryption algorithm
+ * and set some defaults
+ */
+ public RC6Engine()
+ {
+ _S = null;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "RC6";
+ }
+
+ public int getBlockSize()
+ {
+ return 4 * bytesPerWord;
+ }
+
+ /**
+ * initialise a RC5-32 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("invalid parameter passed to RC6 init - " + params.getClass().getName());
+ }
+
+ KeyParameter p = (KeyParameter)params;
+ this.forEncryption = forEncryption;
+ setKey(p.getKey());
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int blockSize = getBlockSize();
+ if (_S == null)
+ {
+ throw new IllegalStateException("RC6 engine not initialised");
+ }
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ return (forEncryption)
+ ? encryptBlock(in, inOff, out, outOff)
+ : decryptBlock(in, inOff, out, outOff);
+ }
+
+ public void reset()
+ {
+ }
+
+ /**
+ * Re-key the cipher.
+ * <p>
+ * @param key the key to be used
+ */
+ private void setKey(
+ byte[] key)
+ {
+
+ //
+ // KEY EXPANSION:
+ //
+ // There are 3 phases to the key expansion.
+ //
+ // Phase 1:
+ // Copy the secret key K[0...b-1] into an array L[0..c-1] of
+ // c = ceil(b/u), where u = wordSize/8 in little-endian order.
+ // In other words, we fill up L using u consecutive key bytes
+ // of K. Any unfilled byte positions in L are zeroed. In the
+ // case that b = c = 0, set c = 1 and L[0] = 0.
+ //
+ // compute number of dwords
+ int c = (key.length + (bytesPerWord - 1)) / bytesPerWord;
+ if (c == 0)
+ {
+ c = 1;
+ }
+ int[] L = new int[(key.length + bytesPerWord - 1) / bytesPerWord];
+
+ // load all key bytes into array of key dwords
+ for (int i = key.length - 1; i >= 0; i--)
+ {
+ L[i / bytesPerWord] = (L[i / bytesPerWord] << 8) + (key[i] & 0xff);
+ }
+
+ //
+ // Phase 2:
+ // Key schedule is placed in a array of 2+2*ROUNDS+2 = 44 dwords.
+ // Initialize S to a particular fixed pseudo-random bit pattern
+ // using an arithmetic progression modulo 2^wordsize determined
+ // by the magic numbers, Pw & Qw.
+ //
+ _S = new int[2+2*_noRounds+2];
+
+ _S[0] = P32;
+ for (int i=1; i < _S.length; i++)
+ {
+ _S[i] = (_S[i-1] + Q32);
+ }
+
+ //
+ // Phase 3:
+ // Mix in the user's secret key in 3 passes over the arrays S & L.
+ // The max of the arrays sizes is used as the loop control
+ //
+ int iter;
+
+ if (L.length > _S.length)
+ {
+ iter = 3 * L.length;
+ }
+ else
+ {
+ iter = 3 * _S.length;
+ }
+
+ int A = 0;
+ int B = 0;
+ int i = 0, j = 0;
+
+ for (int k = 0; k < iter; k++)
+ {
+ A = _S[i] = rotateLeft(_S[i] + A + B, 3);
+ B = L[j] = rotateLeft(L[j] + A + B, A+B);
+ i = (i+1) % _S.length;
+ j = (j+1) % L.length;
+ }
+ }
+
+ private int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ // load A,B,C and D registers from in.
+ int A = bytesToWord(in, inOff);
+ int B = bytesToWord(in, inOff + bytesPerWord);
+ int C = bytesToWord(in, inOff + bytesPerWord*2);
+ int D = bytesToWord(in, inOff + bytesPerWord*3);
+
+ // Do pseudo-round #0: pre-whitening of B and D
+ B += _S[0];
+ D += _S[1];
+
+ // perform round #1,#2 ... #ROUNDS of encryption
+ for (int i = 1; i <= _noRounds; i++)
+ {
+ int t = 0,u = 0;
+
+ t = B*(2*B+1);
+ t = rotateLeft(t,5);
+
+ u = D*(2*D+1);
+ u = rotateLeft(u,5);
+
+ A ^= t;
+ A = rotateLeft(A,u);
+ A += _S[2*i];
+
+ C ^= u;
+ C = rotateLeft(C,t);
+ C += _S[2*i+1];
+
+ int temp = A;
+ A = B;
+ B = C;
+ C = D;
+ D = temp;
+ }
+ // do pseudo-round #(ROUNDS+1) : post-whitening of A and C
+ A += _S[2*_noRounds+2];
+ C += _S[2*_noRounds+3];
+
+ // store A, B, C and D registers to out
+ wordToBytes(A, out, outOff);
+ wordToBytes(B, out, outOff + bytesPerWord);
+ wordToBytes(C, out, outOff + bytesPerWord*2);
+ wordToBytes(D, out, outOff + bytesPerWord*3);
+
+ return 4 * bytesPerWord;
+ }
+
+ private int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ // load A,B,C and D registers from out.
+ int A = bytesToWord(in, inOff);
+ int B = bytesToWord(in, inOff + bytesPerWord);
+ int C = bytesToWord(in, inOff + bytesPerWord*2);
+ int D = bytesToWord(in, inOff + bytesPerWord*3);
+
+ // Undo pseudo-round #(ROUNDS+1) : post whitening of A and C
+ C -= _S[2*_noRounds+3];
+ A -= _S[2*_noRounds+2];
+
+ // Undo round #ROUNDS, .., #2,#1 of encryption
+ for (int i = _noRounds; i >= 1; i--)
+ {
+ int t=0,u = 0;
+
+ int temp = D;
+ D = C;
+ C = B;
+ B = A;
+ A = temp;
+
+ t = B*(2*B+1);
+ t = rotateLeft(t, LGW);
+
+ u = D*(2*D+1);
+ u = rotateLeft(u, LGW);
+
+ C -= _S[2*i+1];
+ C = rotateRight(C,t);
+ C ^= u;
+
+ A -= _S[2*i];
+ A = rotateRight(A,u);
+ A ^= t;
+
+ }
+ // Undo pseudo-round #0: pre-whitening of B and D
+ D -= _S[1];
+ B -= _S[0];
+
+ wordToBytes(A, out, outOff);
+ wordToBytes(B, out, outOff + bytesPerWord);
+ wordToBytes(C, out, outOff + bytesPerWord*2);
+ wordToBytes(D, out, outOff + bytesPerWord*3);
+
+ return 4 * bytesPerWord;
+ }
+
+
+ //////////////////////////////////////////////////////////////
+ //
+ // PRIVATE Helper Methods
+ //
+ //////////////////////////////////////////////////////////////
+
+ /**
+ * Perform a left "spin" of the word. The rotation of the given
+ * word <em>x</em> is rotated left by <em>y</em> bits.
+ * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em>
+ * are used to determine the rotation amount. Here it is
+ * assumed that the wordsize used is 32.
+ * <p>
+ * @param x word to rotate
+ * @param y number of bits to rotate % wordSize
+ */
+ private int rotateLeft(int x, int y)
+ {
+ return (x << y) | (x >>> -y);
+ }
+
+ /**
+ * Perform a right "spin" of the word. The rotation of the given
+ * word <em>x</em> is rotated left by <em>y</em> bits.
+ * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em>
+ * are used to determine the rotation amount. Here it is
+ * assumed that the wordsize used is a power of 2.
+ * <p>
+ * @param x word to rotate
+ * @param y number of bits to rotate % wordSize
+ */
+ private int rotateRight(int x, int y)
+ {
+ return (x >>> y) | (x << -y);
+ }
+
+ private int bytesToWord(
+ byte[] src,
+ int srcOff)
+ {
+ int word = 0;
+
+ for (int i = bytesPerWord - 1; i >= 0; i--)
+ {
+ word = (word << 8) + (src[i + srcOff] & 0xff);
+ }
+
+ return word;
+ }
+
+ private void wordToBytes(
+ int word,
+ byte[] dst,
+ int dstOff)
+ {
+ for (int i = 0; i < bytesPerWord; i++)
+ {
+ dst[i + dstOff] = (byte)word;
+ word >>>= 8;
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RFC3211WrapEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RFC3211WrapEngine.java
new file mode 100644
index 00000000..0d10eeb5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/RFC3211WrapEngine.java
@@ -0,0 +1,175 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.Wrapper;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+
+import java.security.SecureRandom;
+
+/**
+ * an implementation of the RFC 3211 Key Wrap
+ * Specification.
+ */
+public class RFC3211WrapEngine
+ implements Wrapper
+{
+ private CBCBlockCipher engine;
+ private ParametersWithIV param;
+ private boolean forWrapping;
+ private SecureRandom rand;
+
+ public RFC3211WrapEngine(BlockCipher engine)
+ {
+ this.engine = new CBCBlockCipher(engine);
+ }
+
+ public void init(
+ boolean forWrapping,
+ CipherParameters param)
+ {
+ this.forWrapping = forWrapping;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom p = (ParametersWithRandom)param;
+
+ rand = p.getRandom();
+ this.param = (ParametersWithIV)p.getParameters();
+ }
+ else
+ {
+ if (forWrapping)
+ {
+ rand = new SecureRandom();
+ }
+
+ this.param = (ParametersWithIV)param;
+ }
+ }
+
+ public String getAlgorithmName()
+ {
+ return engine.getUnderlyingCipher().getAlgorithmName() + "/RFC3211Wrap";
+ }
+
+ public byte[] wrap(
+ byte[] in,
+ int inOff,
+ int inLen)
+ {
+ if (!forWrapping)
+ {
+ throw new IllegalStateException("not set for wrapping");
+ }
+
+ engine.init(true, param);
+
+ int blockSize = engine.getBlockSize();
+ byte[] cekBlock;
+
+ if (inLen + 4 < blockSize * 2)
+ {
+ cekBlock = new byte[blockSize * 2];
+ }
+ else
+ {
+ cekBlock = new byte[(inLen + 4) % blockSize == 0 ? inLen + 4 : ((inLen + 4) / blockSize + 1) * blockSize];
+ }
+
+ cekBlock[0] = (byte)inLen;
+ cekBlock[1] = (byte)~in[inOff];
+ cekBlock[2] = (byte)~in[inOff + 1];
+ cekBlock[3] = (byte)~in[inOff + 2];
+
+ System.arraycopy(in, inOff, cekBlock, 4, inLen);
+
+ for (int i = inLen + 4; i < cekBlock.length; i++)
+ {
+ cekBlock[i] = (byte)rand.nextInt();
+ }
+
+ for (int i = 0; i < cekBlock.length; i += blockSize)
+ {
+ engine.processBlock(cekBlock, i, cekBlock, i);
+ }
+
+ for (int i = 0; i < cekBlock.length; i += blockSize)
+ {
+ engine.processBlock(cekBlock, i, cekBlock, i);
+ }
+
+ return cekBlock;
+ }
+
+ public byte[] unwrap(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ if (forWrapping)
+ {
+ throw new IllegalStateException("not set for unwrapping");
+ }
+
+ int blockSize = engine.getBlockSize();
+
+ if (inLen < 2 * blockSize)
+ {
+ throw new InvalidCipherTextException("input too short");
+ }
+
+ byte[] cekBlock = new byte[inLen];
+ byte[] iv = new byte[blockSize];
+
+ System.arraycopy(in, inOff, cekBlock, 0, inLen);
+ System.arraycopy(in, inOff, iv, 0, iv.length);
+
+ engine.init(false, new ParametersWithIV(param.getParameters(), iv));
+
+ for (int i = blockSize; i < cekBlock.length; i += blockSize)
+ {
+ engine.processBlock(cekBlock, i, cekBlock, i);
+ }
+
+ System.arraycopy(cekBlock, cekBlock.length - iv.length, iv, 0, iv.length);
+
+ engine.init(false, new ParametersWithIV(param.getParameters(), iv));
+
+ engine.processBlock(cekBlock, 0, cekBlock, 0);
+
+ engine.init(false, param);
+
+ for (int i = 0; i < cekBlock.length; i += blockSize)
+ {
+ engine.processBlock(cekBlock, i, cekBlock, i);
+ }
+
+ if ((cekBlock[0] & 0xff) > cekBlock.length - 4)
+ {
+ throw new InvalidCipherTextException("wrapped key corrupted");
+ }
+
+ byte[] key = new byte[cekBlock[0] & 0xff];
+
+ System.arraycopy(cekBlock, 4, key, 0, cekBlock[0]);
+
+ // Note: Using constant time comparison
+ int nonEqual = 0;
+ for (int i = 0; i != 3; i++)
+ {
+ byte check = (byte)~cekBlock[1 + i];
+ nonEqual |= (check ^ key[i]);
+ }
+ if (nonEqual != 0)
+ {
+ throw new InvalidCipherTextException("wrapped key fails checksum");
+ }
+
+ return key;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
new file mode 100644
index 00000000..540bd25d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
@@ -0,0 +1,177 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.Wrapper;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * an implementation of the AES Key Wrapper from the NIST Key Wrap
+ * Specification as described in RFC 3394.
+ * <p>
+ * For further details see: <a href="http://www.ietf.org/rfc/rfc3394.txt">http://www.ietf.org/rfc/rfc3394.txt</a>
+ * and <a href="http://csrc.nist.gov/encryption/kms/key-wrap.pdf">http://csrc.nist.gov/encryption/kms/key-wrap.pdf</a>.
+ */
+public class RFC3394WrapEngine
+ implements Wrapper
+{
+ private BlockCipher engine;
+ private KeyParameter param;
+ private boolean forWrapping;
+
+ private byte[] iv = {
+ (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6,
+ (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6 };
+
+ public RFC3394WrapEngine(BlockCipher engine)
+ {
+ this.engine = engine;
+ }
+
+ public void init(
+ boolean forWrapping,
+ CipherParameters param)
+ {
+ this.forWrapping = forWrapping;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ param = ((ParametersWithRandom) param).getParameters();
+ }
+
+ if (param instanceof KeyParameter)
+ {
+ this.param = (KeyParameter)param;
+ }
+ else if (param instanceof ParametersWithIV)
+ {
+ this.iv = ((ParametersWithIV)param).getIV();
+ this.param = (KeyParameter)((ParametersWithIV) param).getParameters();
+ if (this.iv.length != 8)
+ {
+ throw new IllegalArgumentException("IV not equal to 8");
+ }
+ }
+ }
+
+ public String getAlgorithmName()
+ {
+ return engine.getAlgorithmName();
+ }
+
+ public byte[] wrap(
+ byte[] in,
+ int inOff,
+ int inLen)
+ {
+ if (!forWrapping)
+ {
+ throw new IllegalStateException("not set for wrapping");
+ }
+
+ int n = inLen / 8;
+
+ if ((n * 8) != inLen)
+ {
+ throw new DataLengthException("wrap data must be a multiple of 8 bytes");
+ }
+
+ byte[] block = new byte[inLen + iv.length];
+ byte[] buf = new byte[8 + iv.length];
+
+ System.arraycopy(iv, 0, block, 0, iv.length);
+ System.arraycopy(in, 0, block, iv.length, inLen);
+
+ engine.init(true, param);
+
+ for (int j = 0; j != 6; j++)
+ {
+ for (int i = 1; i <= n; i++)
+ {
+ System.arraycopy(block, 0, buf, 0, iv.length);
+ System.arraycopy(block, 8 * i, buf, iv.length, 8);
+ engine.processBlock(buf, 0, buf, 0);
+
+ int t = n * j + i;
+ for (int k = 1; t != 0; k++)
+ {
+ byte v = (byte)t;
+
+ buf[iv.length - k] ^= v;
+
+ t >>>= 8;
+ }
+
+ System.arraycopy(buf, 0, block, 0, 8);
+ System.arraycopy(buf, 8, block, 8 * i, 8);
+ }
+ }
+
+ return block;
+ }
+
+ public byte[] unwrap(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ if (forWrapping)
+ {
+ throw new IllegalStateException("not set for unwrapping");
+ }
+
+ int n = inLen / 8;
+
+ if ((n * 8) != inLen)
+ {
+ throw new InvalidCipherTextException("unwrap data must be a multiple of 8 bytes");
+ }
+
+ byte[] block = new byte[inLen - iv.length];
+ byte[] a = new byte[iv.length];
+ byte[] buf = new byte[8 + iv.length];
+
+ System.arraycopy(in, 0, a, 0, iv.length);
+ System.arraycopy(in, iv.length, block, 0, inLen - iv.length);
+
+ engine.init(false, param);
+
+ n = n - 1;
+
+ for (int j = 5; j >= 0; j--)
+ {
+ for (int i = n; i >= 1; i--)
+ {
+ System.arraycopy(a, 0, buf, 0, iv.length);
+ System.arraycopy(block, 8 * (i - 1), buf, iv.length, 8);
+
+ int t = n * j + i;
+ for (int k = 1; t != 0; k++)
+ {
+ byte v = (byte)t;
+
+ buf[iv.length - k] ^= v;
+
+ t >>>= 8;
+ }
+
+ engine.processBlock(buf, 0, buf, 0);
+ System.arraycopy(buf, 0, a, 0, 8);
+ System.arraycopy(buf, 8, block, 8 * (i - 1), 8);
+ }
+ }
+
+ if (!Arrays.constantTimeAreEqual(a, iv))
+ {
+ throw new InvalidCipherTextException("checksum failed");
+ }
+
+ return block;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java
new file mode 100644
index 00000000..e7fb9432
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java
@@ -0,0 +1,126 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.bouncycastle.util.BigIntegers;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/**
+ * this does your basic RSA algorithm with blinding
+ */
+public class RSABlindedEngine
+ implements AsymmetricBlockCipher
+{
+ private static BigInteger ONE = BigInteger.valueOf(1);
+
+ private RSACoreEngine core = new RSACoreEngine();
+ private RSAKeyParameters key;
+ private SecureRandom random;
+
+ /**
+ * initialise the RSA engine.
+ *
+ * @param forEncryption true if we are encrypting, false otherwise.
+ * @param param the necessary RSA key parameters.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters param)
+ {
+ core.init(forEncryption, param);
+
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ key = (RSAKeyParameters)rParam.getParameters();
+ random = rParam.getRandom();
+ }
+ else
+ {
+ key = (RSAKeyParameters)param;
+ random = new SecureRandom();
+ }
+ }
+
+ /**
+ * Return the maximum size for an input block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * encryption, and the same length as the key size on decryption.
+ *
+ * @return maximum size for an input block.
+ */
+ public int getInputBlockSize()
+ {
+ return core.getInputBlockSize();
+ }
+
+ /**
+ * Return the maximum size for an output block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * decryption, and the same length as the key size on encryption.
+ *
+ * @return maximum size for an output block.
+ */
+ public int getOutputBlockSize()
+ {
+ return core.getOutputBlockSize();
+ }
+
+ /**
+ * Process a single block using the basic RSA algorithm.
+ *
+ * @param in the input array.
+ * @param inOff the offset into the input buffer where the data starts.
+ * @param inLen the length of the data to be processed.
+ * @return the result of the RSA process.
+ * @exception DataLengthException the input block is too large.
+ */
+ public byte[] processBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ {
+ if (key == null)
+ {
+ throw new IllegalStateException("RSA engine not initialised");
+ }
+
+ BigInteger input = core.convertInput(in, inOff, inLen);
+
+ BigInteger result;
+ if (key instanceof RSAPrivateCrtKeyParameters)
+ {
+ RSAPrivateCrtKeyParameters k = (RSAPrivateCrtKeyParameters)key;
+
+ BigInteger e = k.getPublicExponent();
+ if (e != null) // can't do blinding without a public exponent
+ {
+ BigInteger m = k.getModulus();
+ BigInteger r = BigIntegers.createRandomInRange(ONE, m.subtract(ONE), random);
+
+ BigInteger blindedInput = r.modPow(e, m).multiply(input).mod(m);
+ BigInteger blindedResult = core.processBlock(blindedInput);
+
+ BigInteger rInv = r.modInverse(m);
+ result = blindedResult.multiply(rInv).mod(m);
+ }
+ else
+ {
+ result = core.processBlock(input);
+ }
+ }
+ else
+ {
+ result = core.processBlock(input);
+ }
+
+ return core.convertOutput(result);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RSABlindingEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RSABlindingEngine.java
new file mode 100644
index 00000000..a8ecb9bf
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/RSABlindingEngine.java
@@ -0,0 +1,137 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.RSABlindingParameters;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+
+import java.math.BigInteger;
+
+/**
+ * This does your basic RSA Chaum's blinding and unblinding as outlined in
+ * "Handbook of Applied Cryptography", page 475. You need to use this if you are
+ * trying to get another party to generate signatures without them being aware
+ * of the message they are signing.
+ */
+public class RSABlindingEngine
+ implements AsymmetricBlockCipher
+{
+ private RSACoreEngine core = new RSACoreEngine();
+
+ private RSAKeyParameters key;
+ private BigInteger blindingFactor;
+
+ private boolean forEncryption;
+
+ /**
+ * Initialise the blinding engine.
+ *
+ * @param forEncryption true if we are encrypting (blinding), false otherwise.
+ * @param param the necessary RSA key parameters.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters param)
+ {
+ RSABlindingParameters p;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ p = (RSABlindingParameters)rParam.getParameters();
+ }
+ else
+ {
+ p = (RSABlindingParameters)param;
+ }
+
+ core.init(forEncryption, p.getPublicKey());
+
+ this.forEncryption = forEncryption;
+ this.key = p.getPublicKey();
+ this.blindingFactor = p.getBlindingFactor();
+ }
+
+ /**
+ * Return the maximum size for an input block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * encryption, and the same length as the key size on decryption.
+ *
+ * @return maximum size for an input block.
+ */
+ public int getInputBlockSize()
+ {
+ return core.getInputBlockSize();
+ }
+
+ /**
+ * Return the maximum size for an output block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * decryption, and the same length as the key size on encryption.
+ *
+ * @return maximum size for an output block.
+ */
+ public int getOutputBlockSize()
+ {
+ return core.getOutputBlockSize();
+ }
+
+ /**
+ * Process a single block using the RSA blinding algorithm.
+ *
+ * @param in the input array.
+ * @param inOff the offset into the input buffer where the data starts.
+ * @param inLen the length of the data to be processed.
+ * @return the result of the RSA process.
+ * @throws DataLengthException the input block is too large.
+ */
+ public byte[] processBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ {
+ BigInteger msg = core.convertInput(in, inOff, inLen);
+
+ if (forEncryption)
+ {
+ msg = blindMessage(msg);
+ }
+ else
+ {
+ msg = unblindMessage(msg);
+ }
+
+ return core.convertOutput(msg);
+ }
+
+ /*
+ * Blind message with the blind factor.
+ */
+ private BigInteger blindMessage(
+ BigInteger msg)
+ {
+ BigInteger blindMsg = blindingFactor;
+ blindMsg = msg.multiply(blindMsg.modPow(key.getExponent(), key.getModulus()));
+ blindMsg = blindMsg.mod(key.getModulus());
+
+ return blindMsg;
+ }
+
+ /*
+ * Unblind the message blinded with the blind factor.
+ */
+ private BigInteger unblindMessage(
+ BigInteger blindedMsg)
+ {
+ BigInteger m = key.getModulus();
+ BigInteger msg = blindedMsg;
+ BigInteger blindFactorInverse = blindingFactor.modInverse(m);
+ msg = msg.multiply(blindFactorInverse);
+ msg = msg.mod(m);
+
+ return msg;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RSACoreEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RSACoreEngine.java
new file mode 100644
index 00000000..510cd5a5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/RSACoreEngine.java
@@ -0,0 +1,203 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+
+import java.math.BigInteger;
+
+/**
+ * this does your basic RSA algorithm.
+ */
+class RSACoreEngine
+{
+ private RSAKeyParameters key;
+ private boolean forEncryption;
+
+ /**
+ * initialise the RSA engine.
+ *
+ * @param forEncryption true if we are encrypting, false otherwise.
+ * @param param the necessary RSA key parameters.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters param)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ key = (RSAKeyParameters)rParam.getParameters();
+ }
+ else
+ {
+ key = (RSAKeyParameters)param;
+ }
+
+ this.forEncryption = forEncryption;
+ }
+
+ /**
+ * Return the maximum size for an input block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * encryption, and the same length as the key size on decryption.
+ *
+ * @return maximum size for an input block.
+ */
+ public int getInputBlockSize()
+ {
+ int bitSize = key.getModulus().bitLength();
+
+ if (forEncryption)
+ {
+ return (bitSize + 7) / 8 - 1;
+ }
+ else
+ {
+ return (bitSize + 7) / 8;
+ }
+ }
+
+ /**
+ * Return the maximum size for an output block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * decryption, and the same length as the key size on encryption.
+ *
+ * @return maximum size for an output block.
+ */
+ public int getOutputBlockSize()
+ {
+ int bitSize = key.getModulus().bitLength();
+
+ if (forEncryption)
+ {
+ return (bitSize + 7) / 8;
+ }
+ else
+ {
+ return (bitSize + 7) / 8 - 1;
+ }
+ }
+
+ public BigInteger convertInput(
+ byte[] in,
+ int inOff,
+ int inLen)
+ {
+ if (inLen > (getInputBlockSize() + 1))
+ {
+ throw new DataLengthException("input too large for RSA cipher.");
+ }
+ else if (inLen == (getInputBlockSize() + 1) && !forEncryption)
+ {
+ throw new DataLengthException("input too large for RSA cipher.");
+ }
+
+ byte[] block;
+
+ if (inOff != 0 || inLen != in.length)
+ {
+ block = new byte[inLen];
+
+ System.arraycopy(in, inOff, block, 0, inLen);
+ }
+ else
+ {
+ block = in;
+ }
+
+ BigInteger res = new BigInteger(1, block);
+ if (res.compareTo(key.getModulus()) >= 0)
+ {
+ throw new DataLengthException("input too large for RSA cipher.");
+ }
+
+ return res;
+ }
+
+ public byte[] convertOutput(
+ BigInteger result)
+ {
+ byte[] output = result.toByteArray();
+
+ if (forEncryption)
+ {
+ if (output[0] == 0 && output.length > getOutputBlockSize()) // have ended up with an extra zero byte, copy down.
+ {
+ byte[] tmp = new byte[output.length - 1];
+
+ System.arraycopy(output, 1, tmp, 0, tmp.length);
+
+ return tmp;
+ }
+
+ if (output.length < getOutputBlockSize()) // have ended up with less bytes than normal, lengthen
+ {
+ byte[] tmp = new byte[getOutputBlockSize()];
+
+ System.arraycopy(output, 0, tmp, tmp.length - output.length, output.length);
+
+ return tmp;
+ }
+ }
+ else
+ {
+ if (output[0] == 0) // have ended up with an extra zero byte, copy down.
+ {
+ byte[] tmp = new byte[output.length - 1];
+
+ System.arraycopy(output, 1, tmp, 0, tmp.length);
+
+ return tmp;
+ }
+ }
+
+ return output;
+ }
+
+ public BigInteger processBlock(BigInteger input)
+ {
+ if (key instanceof RSAPrivateCrtKeyParameters)
+ {
+ //
+ // we have the extra factors, use the Chinese Remainder Theorem - the author
+ // wishes to express his thanks to Dirk Bonekaemper at rtsffm.com for
+ // advice regarding the expression of this.
+ //
+ RSAPrivateCrtKeyParameters crtKey = (RSAPrivateCrtKeyParameters)key;
+
+ BigInteger p = crtKey.getP();
+ BigInteger q = crtKey.getQ();
+ BigInteger dP = crtKey.getDP();
+ BigInteger dQ = crtKey.getDQ();
+ BigInteger qInv = crtKey.getQInv();
+
+ BigInteger mP, mQ, h, m;
+
+ // mP = ((input mod p) ^ dP)) mod p
+ mP = (input.remainder(p)).modPow(dP, p);
+
+ // mQ = ((input mod q) ^ dQ)) mod q
+ mQ = (input.remainder(q)).modPow(dQ, q);
+
+ // h = qInv * (mP - mQ) mod p
+ h = mP.subtract(mQ);
+ h = h.multiply(qInv);
+ h = h.mod(p); // mod (in Java) returns the positive residual
+
+ // m = h * q + mQ
+ m = h.multiply(q);
+ m = m.add(mQ);
+
+ return m;
+ }
+ else
+ {
+ return input.modPow(
+ key.getExponent(), key.getModulus());
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RSAEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RSAEngine.java
new file mode 100644
index 00000000..009dcd43
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/RSAEngine.java
@@ -0,0 +1,78 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+
+/**
+ * this does your basic RSA algorithm.
+ */
+public class RSAEngine
+ implements AsymmetricBlockCipher
+{
+ private RSACoreEngine core;
+
+ /**
+ * initialise the RSA engine.
+ *
+ * @param forEncryption true if we are encrypting, false otherwise.
+ * @param param the necessary RSA key parameters.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters param)
+ {
+ if (core == null)
+ {
+ core = new RSACoreEngine();
+ }
+
+ core.init(forEncryption, param);
+ }
+
+ /**
+ * Return the maximum size for an input block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * encryption, and the same length as the key size on decryption.
+ *
+ * @return maximum size for an input block.
+ */
+ public int getInputBlockSize()
+ {
+ return core.getInputBlockSize();
+ }
+
+ /**
+ * Return the maximum size for an output block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * decryption, and the same length as the key size on encryption.
+ *
+ * @return maximum size for an output block.
+ */
+ public int getOutputBlockSize()
+ {
+ return core.getOutputBlockSize();
+ }
+
+ /**
+ * Process a single block using the basic RSA algorithm.
+ *
+ * @param in the input array.
+ * @param inOff the offset into the input buffer where the data starts.
+ * @param inLen the length of the data to be processed.
+ * @return the result of the RSA process.
+ * @exception DataLengthException the input block is too large.
+ */
+ public byte[] processBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ {
+ if (core == null)
+ {
+ throw new IllegalStateException("RSA engine not initialised");
+ }
+
+ return core.convertOutput(core.processBlock(core.convertInput(in, inOff, inLen)));
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RijndaelEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RijndaelEngine.java
new file mode 100644
index 00000000..c80f6655
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/RijndaelEngine.java
@@ -0,0 +1,725 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * an implementation of Rijndael, based on the documentation and reference implementation
+ * by Paulo Barreto, Vincent Rijmen, for v2.0 August '99.
+ * <p>
+ * Note: this implementation is based on information prior to final NIST publication.
+ */
+public class RijndaelEngine
+ implements BlockCipher
+{
+ private static final int MAXROUNDS = 14;
+
+ private static final int MAXKC = (256/4);
+
+ private static final byte[] logtable = {
+ (byte)0, (byte)0, (byte)25, (byte)1, (byte)50, (byte)2, (byte)26, (byte)198,
+ (byte)75, (byte)199, (byte)27, (byte)104, (byte)51, (byte)238, (byte)223, (byte)3,
+ (byte)100, (byte)4, (byte)224, (byte)14, (byte)52, (byte)141, (byte)129, (byte)239,
+ (byte)76, (byte)113, (byte)8, (byte)200, (byte)248, (byte)105, (byte)28, (byte)193,
+ (byte)125, (byte)194, (byte)29, (byte)181, (byte)249, (byte)185, (byte)39, (byte)106,
+ (byte)77, (byte)228, (byte)166, (byte)114, (byte)154, (byte)201, (byte)9, (byte)120,
+ (byte)101, (byte)47, (byte)138, (byte)5, (byte)33, (byte)15, (byte)225, (byte)36,
+ (byte)18, (byte)240, (byte)130, (byte)69, (byte)53, (byte)147, (byte)218, (byte)142,
+ (byte)150, (byte)143, (byte)219, (byte)189, (byte)54, (byte)208, (byte)206, (byte)148,
+ (byte)19, (byte)92, (byte)210, (byte)241, (byte)64, (byte)70, (byte)131, (byte)56,
+ (byte)102, (byte)221, (byte)253, (byte)48, (byte)191, (byte)6, (byte)139, (byte)98,
+ (byte)179, (byte)37, (byte)226, (byte)152, (byte)34, (byte)136, (byte)145, (byte)16,
+ (byte)126, (byte)110, (byte)72, (byte)195, (byte)163, (byte)182, (byte)30, (byte)66,
+ (byte)58, (byte)107, (byte)40, (byte)84, (byte)250, (byte)133, (byte)61, (byte)186,
+ (byte)43, (byte)121, (byte)10, (byte)21, (byte)155, (byte)159, (byte)94, (byte)202,
+ (byte)78, (byte)212, (byte)172, (byte)229, (byte)243, (byte)115, (byte)167, (byte)87,
+ (byte)175, (byte)88, (byte)168, (byte)80, (byte)244, (byte)234, (byte)214, (byte)116,
+ (byte)79, (byte)174, (byte)233, (byte)213, (byte)231, (byte)230, (byte)173, (byte)232,
+ (byte)44, (byte)215, (byte)117, (byte)122, (byte)235, (byte)22, (byte)11, (byte)245,
+ (byte)89, (byte)203, (byte)95, (byte)176, (byte)156, (byte)169, (byte)81, (byte)160,
+ (byte)127, (byte)12, (byte)246, (byte)111, (byte)23, (byte)196, (byte)73, (byte)236,
+ (byte)216, (byte)67, (byte)31, (byte)45, (byte)164, (byte)118, (byte)123, (byte)183,
+ (byte)204, (byte)187, (byte)62, (byte)90, (byte)251, (byte)96, (byte)177, (byte)134,
+ (byte)59, (byte)82, (byte)161, (byte)108, (byte)170, (byte)85, (byte)41, (byte)157,
+ (byte)151, (byte)178, (byte)135, (byte)144, (byte)97, (byte)190, (byte)220, (byte)252,
+ (byte)188, (byte)149, (byte)207, (byte)205, (byte)55, (byte)63, (byte)91, (byte)209,
+ (byte)83, (byte)57, (byte)132, (byte)60, (byte)65, (byte)162, (byte)109, (byte)71,
+ (byte)20, (byte)42, (byte)158, (byte)93, (byte)86, (byte)242, (byte)211, (byte)171,
+ (byte)68, (byte)17, (byte)146, (byte)217, (byte)35, (byte)32, (byte)46, (byte)137,
+ (byte)180, (byte)124, (byte)184, (byte)38, (byte)119, (byte)153, (byte)227, (byte)165,
+ (byte)103, (byte)74, (byte)237, (byte)222, (byte)197, (byte)49, (byte)254, (byte)24,
+ (byte)13, (byte)99, (byte)140, (byte)128, (byte)192, (byte)247, (byte)112, (byte)7
+ };
+
+ private static final byte[] aLogtable = {
+ (byte)0, (byte)3, (byte)5, (byte)15, (byte)17, (byte)51, (byte)85, (byte)255, (byte)26, (byte)46, (byte)114, (byte)150, (byte)161, (byte)248, (byte)19, (byte)53,
+ (byte)95, (byte)225, (byte)56, (byte)72, (byte)216, (byte)115, (byte)149, (byte)164, (byte)247, (byte)2, (byte)6, (byte)10, (byte)30, (byte)34, (byte)102, (byte)170,
+ (byte)229, (byte)52, (byte)92, (byte)228, (byte)55, (byte)89, (byte)235, (byte)38, (byte)106, (byte)190, (byte)217, (byte)112, (byte)144, (byte)171, (byte)230, (byte)49,
+ (byte)83, (byte)245, (byte)4, (byte)12, (byte)20, (byte)60, (byte)68, (byte)204, (byte)79, (byte)209, (byte)104, (byte)184, (byte)211, (byte)110, (byte)178, (byte)205,
+ (byte)76, (byte)212, (byte)103, (byte)169, (byte)224, (byte)59, (byte)77, (byte)215, (byte)98, (byte)166, (byte)241, (byte)8, (byte)24, (byte)40, (byte)120, (byte)136,
+ (byte)131, (byte)158, (byte)185, (byte)208, (byte)107, (byte)189, (byte)220, (byte)127, (byte)129, (byte)152, (byte)179, (byte)206, (byte)73, (byte)219, (byte)118, (byte)154,
+ (byte)181, (byte)196, (byte)87, (byte)249, (byte)16, (byte)48, (byte)80, (byte)240, (byte)11, (byte)29, (byte)39, (byte)105, (byte)187, (byte)214, (byte)97, (byte)163,
+ (byte)254, (byte)25, (byte)43, (byte)125, (byte)135, (byte)146, (byte)173, (byte)236, (byte)47, (byte)113, (byte)147, (byte)174, (byte)233, (byte)32, (byte)96, (byte)160,
+ (byte)251, (byte)22, (byte)58, (byte)78, (byte)210, (byte)109, (byte)183, (byte)194, (byte)93, (byte)231, (byte)50, (byte)86, (byte)250, (byte)21, (byte)63, (byte)65,
+ (byte)195, (byte)94, (byte)226, (byte)61, (byte)71, (byte)201, (byte)64, (byte)192, (byte)91, (byte)237, (byte)44, (byte)116, (byte)156, (byte)191, (byte)218, (byte)117,
+ (byte)159, (byte)186, (byte)213, (byte)100, (byte)172, (byte)239, (byte)42, (byte)126, (byte)130, (byte)157, (byte)188, (byte)223, (byte)122, (byte)142, (byte)137, (byte)128,
+ (byte)155, (byte)182, (byte)193, (byte)88, (byte)232, (byte)35, (byte)101, (byte)175, (byte)234, (byte)37, (byte)111, (byte)177, (byte)200, (byte)67, (byte)197, (byte)84,
+ (byte)252, (byte)31, (byte)33, (byte)99, (byte)165, (byte)244, (byte)7, (byte)9, (byte)27, (byte)45, (byte)119, (byte)153, (byte)176, (byte)203, (byte)70, (byte)202,
+ (byte)69, (byte)207, (byte)74, (byte)222, (byte)121, (byte)139, (byte)134, (byte)145, (byte)168, (byte)227, (byte)62, (byte)66, (byte)198, (byte)81, (byte)243, (byte)14,
+ (byte)18, (byte)54, (byte)90, (byte)238, (byte)41, (byte)123, (byte)141, (byte)140, (byte)143, (byte)138, (byte)133, (byte)148, (byte)167, (byte)242, (byte)13, (byte)23,
+ (byte)57, (byte)75, (byte)221, (byte)124, (byte)132, (byte)151, (byte)162, (byte)253, (byte)28, (byte)36, (byte)108, (byte)180, (byte)199, (byte)82, (byte)246, (byte)1,
+ (byte)3, (byte)5, (byte)15, (byte)17, (byte)51, (byte)85, (byte)255, (byte)26, (byte)46, (byte)114, (byte)150, (byte)161, (byte)248, (byte)19, (byte)53,
+ (byte)95, (byte)225, (byte)56, (byte)72, (byte)216, (byte)115, (byte)149, (byte)164, (byte)247, (byte)2, (byte)6, (byte)10, (byte)30, (byte)34, (byte)102, (byte)170,
+ (byte)229, (byte)52, (byte)92, (byte)228, (byte)55, (byte)89, (byte)235, (byte)38, (byte)106, (byte)190, (byte)217, (byte)112, (byte)144, (byte)171, (byte)230, (byte)49,
+ (byte)83, (byte)245, (byte)4, (byte)12, (byte)20, (byte)60, (byte)68, (byte)204, (byte)79, (byte)209, (byte)104, (byte)184, (byte)211, (byte)110, (byte)178, (byte)205,
+ (byte)76, (byte)212, (byte)103, (byte)169, (byte)224, (byte)59, (byte)77, (byte)215, (byte)98, (byte)166, (byte)241, (byte)8, (byte)24, (byte)40, (byte)120, (byte)136,
+ (byte)131, (byte)158, (byte)185, (byte)208, (byte)107, (byte)189, (byte)220, (byte)127, (byte)129, (byte)152, (byte)179, (byte)206, (byte)73, (byte)219, (byte)118, (byte)154,
+ (byte)181, (byte)196, (byte)87, (byte)249, (byte)16, (byte)48, (byte)80, (byte)240, (byte)11, (byte)29, (byte)39, (byte)105, (byte)187, (byte)214, (byte)97, (byte)163,
+ (byte)254, (byte)25, (byte)43, (byte)125, (byte)135, (byte)146, (byte)173, (byte)236, (byte)47, (byte)113, (byte)147, (byte)174, (byte)233, (byte)32, (byte)96, (byte)160,
+ (byte)251, (byte)22, (byte)58, (byte)78, (byte)210, (byte)109, (byte)183, (byte)194, (byte)93, (byte)231, (byte)50, (byte)86, (byte)250, (byte)21, (byte)63, (byte)65,
+ (byte)195, (byte)94, (byte)226, (byte)61, (byte)71, (byte)201, (byte)64, (byte)192, (byte)91, (byte)237, (byte)44, (byte)116, (byte)156, (byte)191, (byte)218, (byte)117,
+ (byte)159, (byte)186, (byte)213, (byte)100, (byte)172, (byte)239, (byte)42, (byte)126, (byte)130, (byte)157, (byte)188, (byte)223, (byte)122, (byte)142, (byte)137, (byte)128,
+ (byte)155, (byte)182, (byte)193, (byte)88, (byte)232, (byte)35, (byte)101, (byte)175, (byte)234, (byte)37, (byte)111, (byte)177, (byte)200, (byte)67, (byte)197, (byte)84,
+ (byte)252, (byte)31, (byte)33, (byte)99, (byte)165, (byte)244, (byte)7, (byte)9, (byte)27, (byte)45, (byte)119, (byte)153, (byte)176, (byte)203, (byte)70, (byte)202,
+ (byte)69, (byte)207, (byte)74, (byte)222, (byte)121, (byte)139, (byte)134, (byte)145, (byte)168, (byte)227, (byte)62, (byte)66, (byte)198, (byte)81, (byte)243, (byte)14,
+ (byte)18, (byte)54, (byte)90, (byte)238, (byte)41, (byte)123, (byte)141, (byte)140, (byte)143, (byte)138, (byte)133, (byte)148, (byte)167, (byte)242, (byte)13, (byte)23,
+ (byte)57, (byte)75, (byte)221, (byte)124, (byte)132, (byte)151, (byte)162, (byte)253, (byte)28, (byte)36, (byte)108, (byte)180, (byte)199, (byte)82, (byte)246, (byte)1,
+ };
+
+ private static final byte[] S = {
+ (byte)99, (byte)124, (byte)119, (byte)123, (byte)242, (byte)107, (byte)111, (byte)197, (byte)48, (byte)1, (byte)103, (byte)43, (byte)254, (byte)215, (byte)171, (byte)118,
+ (byte)202, (byte)130, (byte)201, (byte)125, (byte)250, (byte)89, (byte)71, (byte)240, (byte)173, (byte)212, (byte)162, (byte)175, (byte)156, (byte)164, (byte)114, (byte)192,
+ (byte)183, (byte)253, (byte)147, (byte)38, (byte)54, (byte)63, (byte)247, (byte)204, (byte)52, (byte)165, (byte)229, (byte)241, (byte)113, (byte)216, (byte)49, (byte)21,
+ (byte)4, (byte)199, (byte)35, (byte)195, (byte)24, (byte)150, (byte)5, (byte)154, (byte)7, (byte)18, (byte)128, (byte)226, (byte)235, (byte)39, (byte)178, (byte)117,
+ (byte)9, (byte)131, (byte)44, (byte)26, (byte)27, (byte)110, (byte)90, (byte)160, (byte)82, (byte)59, (byte)214, (byte)179, (byte)41, (byte)227, (byte)47, (byte)132,
+ (byte)83, (byte)209, (byte)0, (byte)237, (byte)32, (byte)252, (byte)177, (byte)91, (byte)106, (byte)203, (byte)190, (byte)57, (byte)74, (byte)76, (byte)88, (byte)207,
+ (byte)208, (byte)239, (byte)170, (byte)251, (byte)67, (byte)77, (byte)51, (byte)133, (byte)69, (byte)249, (byte)2, (byte)127, (byte)80, (byte)60, (byte)159, (byte)168,
+ (byte)81, (byte)163, (byte)64, (byte)143, (byte)146, (byte)157, (byte)56, (byte)245, (byte)188, (byte)182, (byte)218, (byte)33, (byte)16, (byte)255, (byte)243, (byte)210,
+ (byte)205, (byte)12, (byte)19, (byte)236, (byte)95, (byte)151, (byte)68, (byte)23, (byte)196, (byte)167, (byte)126, (byte)61, (byte)100, (byte)93, (byte)25, (byte)115,
+ (byte)96, (byte)129, (byte)79, (byte)220, (byte)34, (byte)42, (byte)144, (byte)136, (byte)70, (byte)238, (byte)184, (byte)20, (byte)222, (byte)94, (byte)11, (byte)219,
+ (byte)224, (byte)50, (byte)58, (byte)10, (byte)73, (byte)6, (byte)36, (byte)92, (byte)194, (byte)211, (byte)172, (byte)98, (byte)145, (byte)149, (byte)228, (byte)121,
+ (byte)231, (byte)200, (byte)55, (byte)109, (byte)141, (byte)213, (byte)78, (byte)169, (byte)108, (byte)86, (byte)244, (byte)234, (byte)101, (byte)122, (byte)174, (byte)8,
+ (byte)186, (byte)120, (byte)37, (byte)46, (byte)28, (byte)166, (byte)180, (byte)198, (byte)232, (byte)221, (byte)116, (byte)31, (byte)75, (byte)189, (byte)139, (byte)138,
+ (byte)112, (byte)62, (byte)181, (byte)102, (byte)72, (byte)3, (byte)246, (byte)14, (byte)97, (byte)53, (byte)87, (byte)185, (byte)134, (byte)193, (byte)29, (byte)158,
+ (byte)225, (byte)248, (byte)152, (byte)17, (byte)105, (byte)217, (byte)142, (byte)148, (byte)155, (byte)30, (byte)135, (byte)233, (byte)206, (byte)85, (byte)40, (byte)223,
+ (byte)140, (byte)161, (byte)137, (byte)13, (byte)191, (byte)230, (byte)66, (byte)104, (byte)65, (byte)153, (byte)45, (byte)15, (byte)176, (byte)84, (byte)187, (byte)22,
+ };
+
+ private static final byte[] Si = {
+ (byte)82, (byte)9, (byte)106, (byte)213, (byte)48, (byte)54, (byte)165, (byte)56, (byte)191, (byte)64, (byte)163, (byte)158, (byte)129, (byte)243, (byte)215, (byte)251,
+ (byte)124, (byte)227, (byte)57, (byte)130, (byte)155, (byte)47, (byte)255, (byte)135, (byte)52, (byte)142, (byte)67, (byte)68, (byte)196, (byte)222, (byte)233, (byte)203,
+ (byte)84, (byte)123, (byte)148, (byte)50, (byte)166, (byte)194, (byte)35, (byte)61, (byte)238, (byte)76, (byte)149, (byte)11, (byte)66, (byte)250, (byte)195, (byte)78,
+ (byte)8, (byte)46, (byte)161, (byte)102, (byte)40, (byte)217, (byte)36, (byte)178, (byte)118, (byte)91, (byte)162, (byte)73, (byte)109, (byte)139, (byte)209, (byte)37,
+ (byte)114, (byte)248, (byte)246, (byte)100, (byte)134, (byte)104, (byte)152, (byte)22, (byte)212, (byte)164, (byte)92, (byte)204, (byte)93, (byte)101, (byte)182, (byte)146,
+ (byte)108, (byte)112, (byte)72, (byte)80, (byte)253, (byte)237, (byte)185, (byte)218, (byte)94, (byte)21, (byte)70, (byte)87, (byte)167, (byte)141, (byte)157, (byte)132,
+ (byte)144, (byte)216, (byte)171, (byte)0, (byte)140, (byte)188, (byte)211, (byte)10, (byte)247, (byte)228, (byte)88, (byte)5, (byte)184, (byte)179, (byte)69, (byte)6,
+ (byte)208, (byte)44, (byte)30, (byte)143, (byte)202, (byte)63, (byte)15, (byte)2, (byte)193, (byte)175, (byte)189, (byte)3, (byte)1, (byte)19, (byte)138, (byte)107,
+ (byte)58, (byte)145, (byte)17, (byte)65, (byte)79, (byte)103, (byte)220, (byte)234, (byte)151, (byte)242, (byte)207, (byte)206, (byte)240, (byte)180, (byte)230, (byte)115,
+ (byte)150, (byte)172, (byte)116, (byte)34, (byte)231, (byte)173, (byte)53, (byte)133, (byte)226, (byte)249, (byte)55, (byte)232, (byte)28, (byte)117, (byte)223, (byte)110,
+ (byte)71, (byte)241, (byte)26, (byte)113, (byte)29, (byte)41, (byte)197, (byte)137, (byte)111, (byte)183, (byte)98, (byte)14, (byte)170, (byte)24, (byte)190, (byte)27,
+ (byte)252, (byte)86, (byte)62, (byte)75, (byte)198, (byte)210, (byte)121, (byte)32, (byte)154, (byte)219, (byte)192, (byte)254, (byte)120, (byte)205, (byte)90, (byte)244,
+ (byte)31, (byte)221, (byte)168, (byte)51, (byte)136, (byte)7, (byte)199, (byte)49, (byte)177, (byte)18, (byte)16, (byte)89, (byte)39, (byte)128, (byte)236, (byte)95,
+ (byte)96, (byte)81, (byte)127, (byte)169, (byte)25, (byte)181, (byte)74, (byte)13, (byte)45, (byte)229, (byte)122, (byte)159, (byte)147, (byte)201, (byte)156, (byte)239,
+ (byte)160, (byte)224, (byte)59, (byte)77, (byte)174, (byte)42, (byte)245, (byte)176, (byte)200, (byte)235, (byte)187, (byte)60, (byte)131, (byte)83, (byte)153, (byte)97,
+ (byte)23, (byte)43, (byte)4, (byte)126, (byte)186, (byte)119, (byte)214, (byte)38, (byte)225, (byte)105, (byte)20, (byte)99, (byte)85, (byte)33, (byte)12, (byte)125,
+ };
+
+ private static final int[] rcon = {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 };
+
+ static byte[][] shifts0 =
+ {
+ { 0, 8, 16, 24 },
+ { 0, 8, 16, 24 },
+ { 0, 8, 16, 24 },
+ { 0, 8, 16, 32 },
+ { 0, 8, 24, 32 }
+ };
+
+ static byte[][] shifts1 =
+ {
+ { 0, 24, 16, 8 },
+ { 0, 32, 24, 16 },
+ { 0, 40, 32, 24 },
+ { 0, 48, 40, 24 },
+ { 0, 56, 40, 32 }
+ };
+
+ /**
+ * multiply two elements of GF(2^m)
+ * needed for MixColumn and InvMixColumn
+ */
+ private byte mul0x2(
+ int b)
+ {
+ if (b != 0)
+ {
+ return aLogtable[25 + (logtable[b] & 0xff)];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ private byte mul0x3(
+ int b)
+ {
+ if (b != 0)
+ {
+ return aLogtable[1 + (logtable[b] & 0xff)];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ private byte mul0x9(
+ int b)
+ {
+ if (b >= 0)
+ {
+ return aLogtable[199 + b];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ private byte mul0xb(
+ int b)
+ {
+ if (b >= 0)
+ {
+ return aLogtable[104 + b];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ private byte mul0xd(
+ int b)
+ {
+ if (b >= 0)
+ {
+ return aLogtable[238 + b];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ private byte mul0xe(
+ int b)
+ {
+ if (b >= 0)
+ {
+ return aLogtable[223 + b];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ /**
+ * xor corresponding text input and round key input bytes
+ */
+ private void KeyAddition(
+ long[] rk)
+ {
+ A0 ^= rk[0];
+ A1 ^= rk[1];
+ A2 ^= rk[2];
+ A3 ^= rk[3];
+ }
+
+ private long shift(
+ long r,
+ int shift)
+ {
+ return (((r >>> shift) | (r << (BC - shift)))) & BC_MASK;
+ }
+
+ /**
+ * Row 0 remains unchanged
+ * The other three rows are shifted a variable amount
+ */
+ private void ShiftRow(
+ byte[] shiftsSC)
+ {
+ A1 = shift(A1, shiftsSC[1]);
+ A2 = shift(A2, shiftsSC[2]);
+ A3 = shift(A3, shiftsSC[3]);
+ }
+
+ private long applyS(
+ long r,
+ byte[] box)
+ {
+ long res = 0;
+
+ for (int j = 0; j < BC; j += 8)
+ {
+ res |= (long)(box[(int)((r >> j) & 0xff)] & 0xff) << j;
+ }
+
+ return res;
+ }
+
+ /**
+ * Replace every byte of the input by the byte at that place
+ * in the nonlinear S-box
+ */
+ private void Substitution(
+ byte[] box)
+ {
+ A0 = applyS(A0, box);
+ A1 = applyS(A1, box);
+ A2 = applyS(A2, box);
+ A3 = applyS(A3, box);
+ }
+
+ /**
+ * Mix the bytes of every column in a linear way
+ */
+ private void MixColumn()
+ {
+ long r0, r1, r2, r3;
+
+ r0 = r1 = r2 = r3 = 0;
+
+ for (int j = 0; j < BC; j += 8)
+ {
+ int a0 = (int)((A0 >> j) & 0xff);
+ int a1 = (int)((A1 >> j) & 0xff);
+ int a2 = (int)((A2 >> j) & 0xff);
+ int a3 = (int)((A3 >> j) & 0xff);
+
+ r0 |= (long)((mul0x2(a0) ^ mul0x3(a1) ^ a2 ^ a3) & 0xff) << j;
+
+ r1 |= (long)((mul0x2(a1) ^ mul0x3(a2) ^ a3 ^ a0) & 0xff) << j;
+
+ r2 |= (long)((mul0x2(a2) ^ mul0x3(a3) ^ a0 ^ a1) & 0xff) << j;
+
+ r3 |= (long)((mul0x2(a3) ^ mul0x3(a0) ^ a1 ^ a2) & 0xff) << j;
+ }
+
+ A0 = r0;
+ A1 = r1;
+ A2 = r2;
+ A3 = r3;
+ }
+
+ /**
+ * Mix the bytes of every column in a linear way
+ * This is the opposite operation of Mixcolumn
+ */
+ private void InvMixColumn()
+ {
+ long r0, r1, r2, r3;
+
+ r0 = r1 = r2 = r3 = 0;
+ for (int j = 0; j < BC; j += 8)
+ {
+ int a0 = (int)((A0 >> j) & 0xff);
+ int a1 = (int)((A1 >> j) & 0xff);
+ int a2 = (int)((A2 >> j) & 0xff);
+ int a3 = (int)((A3 >> j) & 0xff);
+
+ //
+ // pre-lookup the log table
+ //
+ a0 = (a0 != 0) ? (logtable[a0 & 0xff] & 0xff) : -1;
+ a1 = (a1 != 0) ? (logtable[a1 & 0xff] & 0xff) : -1;
+ a2 = (a2 != 0) ? (logtable[a2 & 0xff] & 0xff) : -1;
+ a3 = (a3 != 0) ? (logtable[a3 & 0xff] & 0xff) : -1;
+
+ r0 |= (long)((mul0xe(a0) ^ mul0xb(a1) ^ mul0xd(a2) ^ mul0x9(a3)) & 0xff) << j;
+
+ r1 |= (long)((mul0xe(a1) ^ mul0xb(a2) ^ mul0xd(a3) ^ mul0x9(a0)) & 0xff) << j;
+
+ r2 |= (long)((mul0xe(a2) ^ mul0xb(a3) ^ mul0xd(a0) ^ mul0x9(a1)) & 0xff) << j;
+
+ r3 |= (long)((mul0xe(a3) ^ mul0xb(a0) ^ mul0xd(a1) ^ mul0x9(a2)) & 0xff) << j;
+ }
+
+ A0 = r0;
+ A1 = r1;
+ A2 = r2;
+ A3 = r3;
+ }
+
+ /**
+ * Calculate the necessary round keys
+ * The number of calculations depends on keyBits and blockBits
+ */
+ private long[][] generateWorkingKey(
+ byte[] key)
+ {
+ int KC;
+ int t, rconpointer = 0;
+ int keyBits = key.length * 8;
+ byte[][] tk = new byte[4][MAXKC];
+ long[][] W = new long[MAXROUNDS+1][4];
+
+ switch (keyBits)
+ {
+ case 128:
+ KC = 4;
+ break;
+ case 160:
+ KC = 5;
+ break;
+ case 192:
+ KC = 6;
+ break;
+ case 224:
+ KC = 7;
+ break;
+ case 256:
+ KC = 8;
+ break;
+ default :
+ throw new IllegalArgumentException("Key length not 128/160/192/224/256 bits.");
+ }
+
+ if (keyBits >= blockBits)
+ {
+ ROUNDS = KC + 6;
+ }
+ else
+ {
+ ROUNDS = (BC / 8) + 6;
+ }
+
+ //
+ // copy the key into the processing area
+ //
+ int index = 0;
+
+ for (int i = 0; i < key.length; i++)
+ {
+ tk[i % 4][i / 4] = key[index++];
+ }
+
+ t = 0;
+
+ //
+ // copy values into round key array
+ //
+ for (int j = 0; (j < KC) && (t < (ROUNDS+1)*(BC / 8)); j++, t++)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ W[t / (BC / 8)][i] |= (long)(tk[i][j] & 0xff) << ((t * 8) % BC);
+ }
+ }
+
+ //
+ // while not enough round key material calculated
+ // calculate new values
+ //
+ while (t < (ROUNDS+1)*(BC/8))
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ tk[i][0] ^= S[tk[(i+1)%4][KC-1] & 0xff];
+ }
+ tk[0][0] ^= rcon[rconpointer++];
+
+ if (KC <= 6)
+ {
+ for (int j = 1; j < KC; j++)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ tk[i][j] ^= tk[i][j-1];
+ }
+ }
+ }
+ else
+ {
+ for (int j = 1; j < 4; j++)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ tk[i][j] ^= tk[i][j-1];
+ }
+ }
+ for (int i = 0; i < 4; i++)
+ {
+ tk[i][4] ^= S[tk[i][3] & 0xff];
+ }
+ for (int j = 5; j < KC; j++)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ tk[i][j] ^= tk[i][j-1];
+ }
+ }
+ }
+
+ //
+ // copy values into round key array
+ //
+ for (int j = 0; (j < KC) && (t < (ROUNDS+1)*(BC/8)); j++, t++)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ W[t / (BC/8)][i] |= (long)(tk[i][j] & 0xff) << ((t * 8) % (BC));
+ }
+ }
+ }
+
+ return W;
+ }
+
+ private int BC;
+ private long BC_MASK;
+ private int ROUNDS;
+ private int blockBits;
+ private long[][] workingKey;
+ private long A0, A1, A2, A3;
+ private boolean forEncryption;
+ private byte[] shifts0SC;
+ private byte[] shifts1SC;
+
+ /**
+ * default constructor - 128 bit block size.
+ */
+ public RijndaelEngine()
+ {
+ this(128);
+ }
+
+ /**
+ * basic constructor - set the cipher up for a given blocksize
+ *
+ * @param blockBits the blocksize in bits, must be 128, 192, or 256.
+ */
+ public RijndaelEngine(
+ int blockBits)
+ {
+ switch (blockBits)
+ {
+ case 128:
+ BC = 32;
+ BC_MASK = 0xffffffffL;
+ shifts0SC = shifts0[0];
+ shifts1SC = shifts1[0];
+ break;
+ case 160:
+ BC = 40;
+ BC_MASK = 0xffffffffffL;
+ shifts0SC = shifts0[1];
+ shifts1SC = shifts1[1];
+ break;
+ case 192:
+ BC = 48;
+ BC_MASK = 0xffffffffffffL;
+ shifts0SC = shifts0[2];
+ shifts1SC = shifts1[2];
+ break;
+ case 224:
+ BC = 56;
+ BC_MASK = 0xffffffffffffffL;
+ shifts0SC = shifts0[3];
+ shifts1SC = shifts1[3];
+ break;
+ case 256:
+ BC = 64;
+ BC_MASK = 0xffffffffffffffffL;
+ shifts0SC = shifts0[4];
+ shifts1SC = shifts1[4];
+ break;
+ default:
+ throw new IllegalArgumentException("unknown blocksize to Rijndael");
+ }
+
+ this.blockBits = blockBits;
+ }
+
+ /**
+ * initialise a Rijndael cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ workingKey = generateWorkingKey(((KeyParameter)params).getKey());
+ this.forEncryption = forEncryption;
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to Rijndael init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Rijndael";
+ }
+
+ public int getBlockSize()
+ {
+ return BC / 2;
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new IllegalStateException("Rijndael engine not initialised");
+ }
+
+ if ((inOff + (BC / 2)) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + (BC / 2)) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (forEncryption)
+ {
+ unpackBlock(in, inOff);
+ encryptBlock(workingKey);
+ packBlock(out, outOff);
+ }
+ else
+ {
+ unpackBlock(in, inOff);
+ decryptBlock(workingKey);
+ packBlock(out, outOff);
+ }
+
+ return BC / 2;
+ }
+
+ public void reset()
+ {
+ }
+
+ private void unpackBlock(
+ byte[] bytes,
+ int off)
+ {
+ int index = off;
+
+ A0 = (bytes[index++] & 0xff);
+ A1 = (bytes[index++] & 0xff);
+ A2 = (bytes[index++] & 0xff);
+ A3 = (bytes[index++] & 0xff);
+
+ for (int j = 8; j != BC; j += 8)
+ {
+ A0 |= (long)(bytes[index++] & 0xff) << j;
+ A1 |= (long)(bytes[index++] & 0xff) << j;
+ A2 |= (long)(bytes[index++] & 0xff) << j;
+ A3 |= (long)(bytes[index++] & 0xff) << j;
+ }
+ }
+
+ private void packBlock(
+ byte[] bytes,
+ int off)
+ {
+ int index = off;
+
+ for (int j = 0; j != BC; j += 8)
+ {
+ bytes[index++] = (byte)(A0 >> j);
+ bytes[index++] = (byte)(A1 >> j);
+ bytes[index++] = (byte)(A2 >> j);
+ bytes[index++] = (byte)(A3 >> j);
+ }
+ }
+
+ private void encryptBlock(
+ long[][] rk)
+ {
+ int r;
+
+ //
+ // begin with a key addition
+ //
+ KeyAddition(rk[0]);
+
+ //
+ // ROUNDS-1 ordinary rounds
+ //
+ for (r = 1; r < ROUNDS; r++)
+ {
+ Substitution(S);
+ ShiftRow(shifts0SC);
+ MixColumn();
+ KeyAddition(rk[r]);
+ }
+
+ //
+ // Last round is special: there is no MixColumn
+ //
+ Substitution(S);
+ ShiftRow(shifts0SC);
+ KeyAddition(rk[ROUNDS]);
+ }
+
+ private void decryptBlock(
+ long[][] rk)
+ {
+ int r;
+
+ // To decrypt: apply the inverse operations of the encrypt routine,
+ // in opposite order
+ //
+ // (KeyAddition is an involution: it 's equal to its inverse)
+ // (the inverse of Substitution with table S is Substitution with the inverse table of S)
+ // (the inverse of Shiftrow is Shiftrow over a suitable distance)
+ //
+
+ // First the special round:
+ // without InvMixColumn
+ // with extra KeyAddition
+ //
+ KeyAddition(rk[ROUNDS]);
+ Substitution(Si);
+ ShiftRow(shifts1SC);
+
+ //
+ // ROUNDS-1 ordinary rounds
+ //
+ for (r = ROUNDS-1; r > 0; r--)
+ {
+ KeyAddition(rk[r]);
+ InvMixColumn();
+ Substitution(Si);
+ ShiftRow(shifts1SC);
+ }
+
+ //
+ // End with the extra key addition
+ //
+ KeyAddition(rk[0]);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/SEEDEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/SEEDEngine.java
new file mode 100644
index 00000000..43872ed3
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/SEEDEngine.java
@@ -0,0 +1,346 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * Implementation of the SEED algorithm as described in RFC 4009
+ */
+public class SEEDEngine
+ implements BlockCipher
+{
+ private final int BLOCK_SIZE = 16;
+
+ private static final int[] SS0 =
+ {
+ 0x2989a1a8, 0x05858184, 0x16c6d2d4, 0x13c3d3d0, 0x14445054, 0x1d0d111c, 0x2c8ca0ac, 0x25052124,
+ 0x1d4d515c, 0x03434340, 0x18081018, 0x1e0e121c, 0x11415150, 0x3cccf0fc, 0x0acac2c8, 0x23436360,
+ 0x28082028, 0x04444044, 0x20002020, 0x1d8d919c, 0x20c0e0e0, 0x22c2e2e0, 0x08c8c0c8, 0x17071314,
+ 0x2585a1a4, 0x0f8f838c, 0x03030300, 0x3b4b7378, 0x3b8bb3b8, 0x13031310, 0x12c2d2d0, 0x2ecee2ec,
+ 0x30407070, 0x0c8c808c, 0x3f0f333c, 0x2888a0a8, 0x32023230, 0x1dcdd1dc, 0x36c6f2f4, 0x34447074,
+ 0x2ccce0ec, 0x15859194, 0x0b0b0308, 0x17475354, 0x1c4c505c, 0x1b4b5358, 0x3d8db1bc, 0x01010100,
+ 0x24042024, 0x1c0c101c, 0x33437370, 0x18889098, 0x10001010, 0x0cccc0cc, 0x32c2f2f0, 0x19c9d1d8,
+ 0x2c0c202c, 0x27c7e3e4, 0x32427270, 0x03838380, 0x1b8b9398, 0x11c1d1d0, 0x06868284, 0x09c9c1c8,
+ 0x20406060, 0x10405050, 0x2383a3a0, 0x2bcbe3e8, 0x0d0d010c, 0x3686b2b4, 0x1e8e929c, 0x0f4f434c,
+ 0x3787b3b4, 0x1a4a5258, 0x06c6c2c4, 0x38487078, 0x2686a2a4, 0x12021210, 0x2f8fa3ac, 0x15c5d1d4,
+ 0x21416160, 0x03c3c3c0, 0x3484b0b4, 0x01414140, 0x12425250, 0x3d4d717c, 0x0d8d818c, 0x08080008,
+ 0x1f0f131c, 0x19899198, 0x00000000, 0x19091118, 0x04040004, 0x13435350, 0x37c7f3f4, 0x21c1e1e0,
+ 0x3dcdf1fc, 0x36467274, 0x2f0f232c, 0x27072324, 0x3080b0b0, 0x0b8b8388, 0x0e0e020c, 0x2b8ba3a8,
+ 0x2282a2a0, 0x2e4e626c, 0x13839390, 0x0d4d414c, 0x29496168, 0x3c4c707c, 0x09090108, 0x0a0a0208,
+ 0x3f8fb3bc, 0x2fcfe3ec, 0x33c3f3f0, 0x05c5c1c4, 0x07878384, 0x14041014, 0x3ecef2fc, 0x24446064,
+ 0x1eced2dc, 0x2e0e222c, 0x0b4b4348, 0x1a0a1218, 0x06060204, 0x21012120, 0x2b4b6368, 0x26466264,
+ 0x02020200, 0x35c5f1f4, 0x12829290, 0x0a8a8288, 0x0c0c000c, 0x3383b3b0, 0x3e4e727c, 0x10c0d0d0,
+ 0x3a4a7278, 0x07474344, 0x16869294, 0x25c5e1e4, 0x26062224, 0x00808080, 0x2d8da1ac, 0x1fcfd3dc,
+ 0x2181a1a0, 0x30003030, 0x37073334, 0x2e8ea2ac, 0x36063234, 0x15051114, 0x22022220, 0x38083038,
+ 0x34c4f0f4, 0x2787a3a4, 0x05454144, 0x0c4c404c, 0x01818180, 0x29c9e1e8, 0x04848084, 0x17879394,
+ 0x35053134, 0x0bcbc3c8, 0x0ecec2cc, 0x3c0c303c, 0x31417170, 0x11011110, 0x07c7c3c4, 0x09898188,
+ 0x35457174, 0x3bcbf3f8, 0x1acad2d8, 0x38c8f0f8, 0x14849094, 0x19495158, 0x02828280, 0x04c4c0c4,
+ 0x3fcff3fc, 0x09494148, 0x39093138, 0x27476364, 0x00c0c0c0, 0x0fcfc3cc, 0x17c7d3d4, 0x3888b0b8,
+ 0x0f0f030c, 0x0e8e828c, 0x02424240, 0x23032320, 0x11819190, 0x2c4c606c, 0x1bcbd3d8, 0x2484a0a4,
+ 0x34043034, 0x31c1f1f0, 0x08484048, 0x02c2c2c0, 0x2f4f636c, 0x3d0d313c, 0x2d0d212c, 0x00404040,
+ 0x3e8eb2bc, 0x3e0e323c, 0x3c8cb0bc, 0x01c1c1c0, 0x2a8aa2a8, 0x3a8ab2b8, 0x0e4e424c, 0x15455154,
+ 0x3b0b3338, 0x1cccd0dc, 0x28486068, 0x3f4f737c, 0x1c8c909c, 0x18c8d0d8, 0x0a4a4248, 0x16465254,
+ 0x37477374, 0x2080a0a0, 0x2dcde1ec, 0x06464244, 0x3585b1b4, 0x2b0b2328, 0x25456164, 0x3acaf2f8,
+ 0x23c3e3e0, 0x3989b1b8, 0x3181b1b0, 0x1f8f939c, 0x1e4e525c, 0x39c9f1f8, 0x26c6e2e4, 0x3282b2b0,
+ 0x31013130, 0x2acae2e8, 0x2d4d616c, 0x1f4f535c, 0x24c4e0e4, 0x30c0f0f0, 0x0dcdc1cc, 0x08888088,
+ 0x16061214, 0x3a0a3238, 0x18485058, 0x14c4d0d4, 0x22426260, 0x29092128, 0x07070304, 0x33033330,
+ 0x28c8e0e8, 0x1b0b1318, 0x05050104, 0x39497178, 0x10809090, 0x2a4a6268, 0x2a0a2228, 0x1a8a9298
+ };
+
+ private static final int[] SS1 =
+ {
+
+ 0x38380830, 0xe828c8e0, 0x2c2d0d21, 0xa42686a2, 0xcc0fcfc3, 0xdc1eced2, 0xb03383b3, 0xb83888b0,
+ 0xac2f8fa3, 0x60204060, 0x54154551, 0xc407c7c3, 0x44044440, 0x6c2f4f63, 0x682b4b63, 0x581b4b53,
+ 0xc003c3c3, 0x60224262, 0x30330333, 0xb43585b1, 0x28290921, 0xa02080a0, 0xe022c2e2, 0xa42787a3,
+ 0xd013c3d3, 0x90118191, 0x10110111, 0x04060602, 0x1c1c0c10, 0xbc3c8cb0, 0x34360632, 0x480b4b43,
+ 0xec2fcfe3, 0x88088880, 0x6c2c4c60, 0xa82888a0, 0x14170713, 0xc404c4c0, 0x14160612, 0xf434c4f0,
+ 0xc002c2c2, 0x44054541, 0xe021c1e1, 0xd416c6d2, 0x3c3f0f33, 0x3c3d0d31, 0x8c0e8e82, 0x98188890,
+ 0x28280820, 0x4c0e4e42, 0xf436c6f2, 0x3c3e0e32, 0xa42585a1, 0xf839c9f1, 0x0c0d0d01, 0xdc1fcfd3,
+ 0xd818c8d0, 0x282b0b23, 0x64264662, 0x783a4a72, 0x24270723, 0x2c2f0f23, 0xf031c1f1, 0x70324272,
+ 0x40024242, 0xd414c4d0, 0x40014141, 0xc000c0c0, 0x70334373, 0x64274763, 0xac2c8ca0, 0x880b8b83,
+ 0xf437c7f3, 0xac2d8da1, 0x80008080, 0x1c1f0f13, 0xc80acac2, 0x2c2c0c20, 0xa82a8aa2, 0x34340430,
+ 0xd012c2d2, 0x080b0b03, 0xec2ecee2, 0xe829c9e1, 0x5c1d4d51, 0x94148490, 0x18180810, 0xf838c8f0,
+ 0x54174753, 0xac2e8ea2, 0x08080800, 0xc405c5c1, 0x10130313, 0xcc0dcdc1, 0x84068682, 0xb83989b1,
+ 0xfc3fcff3, 0x7c3d4d71, 0xc001c1c1, 0x30310131, 0xf435c5f1, 0x880a8a82, 0x682a4a62, 0xb03181b1,
+ 0xd011c1d1, 0x20200020, 0xd417c7d3, 0x00020202, 0x20220222, 0x04040400, 0x68284860, 0x70314171,
+ 0x04070703, 0xd81bcbd3, 0x9c1d8d91, 0x98198991, 0x60214161, 0xbc3e8eb2, 0xe426c6e2, 0x58194951,
+ 0xdc1dcdd1, 0x50114151, 0x90108090, 0xdc1cccd0, 0x981a8a92, 0xa02383a3, 0xa82b8ba3, 0xd010c0d0,
+ 0x80018181, 0x0c0f0f03, 0x44074743, 0x181a0a12, 0xe023c3e3, 0xec2ccce0, 0x8c0d8d81, 0xbc3f8fb3,
+ 0x94168692, 0x783b4b73, 0x5c1c4c50, 0xa02282a2, 0xa02181a1, 0x60234363, 0x20230323, 0x4c0d4d41,
+ 0xc808c8c0, 0x9c1e8e92, 0x9c1c8c90, 0x383a0a32, 0x0c0c0c00, 0x2c2e0e22, 0xb83a8ab2, 0x6c2e4e62,
+ 0x9c1f8f93, 0x581a4a52, 0xf032c2f2, 0x90128292, 0xf033c3f3, 0x48094941, 0x78384870, 0xcc0cccc0,
+ 0x14150511, 0xf83bcbf3, 0x70304070, 0x74354571, 0x7c3f4f73, 0x34350531, 0x10100010, 0x00030303,
+ 0x64244460, 0x6c2d4d61, 0xc406c6c2, 0x74344470, 0xd415c5d1, 0xb43484b0, 0xe82acae2, 0x08090901,
+ 0x74364672, 0x18190911, 0xfc3ecef2, 0x40004040, 0x10120212, 0xe020c0e0, 0xbc3d8db1, 0x04050501,
+ 0xf83acaf2, 0x00010101, 0xf030c0f0, 0x282a0a22, 0x5c1e4e52, 0xa82989a1, 0x54164652, 0x40034343,
+ 0x84058581, 0x14140410, 0x88098981, 0x981b8b93, 0xb03080b0, 0xe425c5e1, 0x48084840, 0x78394971,
+ 0x94178793, 0xfc3cccf0, 0x1c1e0e12, 0x80028282, 0x20210121, 0x8c0c8c80, 0x181b0b13, 0x5c1f4f53,
+ 0x74374773, 0x54144450, 0xb03282b2, 0x1c1d0d11, 0x24250521, 0x4c0f4f43, 0x00000000, 0x44064642,
+ 0xec2dcde1, 0x58184850, 0x50124252, 0xe82bcbe3, 0x7c3e4e72, 0xd81acad2, 0xc809c9c1, 0xfc3dcdf1,
+ 0x30300030, 0x94158591, 0x64254561, 0x3c3c0c30, 0xb43686b2, 0xe424c4e0, 0xb83b8bb3, 0x7c3c4c70,
+ 0x0c0e0e02, 0x50104050, 0x38390931, 0x24260622, 0x30320232, 0x84048480, 0x68294961, 0x90138393,
+ 0x34370733, 0xe427c7e3, 0x24240420, 0xa42484a0, 0xc80bcbc3, 0x50134353, 0x080a0a02, 0x84078783,
+ 0xd819c9d1, 0x4c0c4c40, 0x80038383, 0x8c0f8f83, 0xcc0ecec2, 0x383b0b33, 0x480a4a42, 0xb43787b3
+ };
+
+ private static final int[] SS2 =
+ {
+
+ 0xa1a82989, 0x81840585, 0xd2d416c6, 0xd3d013c3, 0x50541444, 0x111c1d0d, 0xa0ac2c8c, 0x21242505,
+ 0x515c1d4d, 0x43400343, 0x10181808, 0x121c1e0e, 0x51501141, 0xf0fc3ccc, 0xc2c80aca, 0x63602343,
+ 0x20282808, 0x40440444, 0x20202000, 0x919c1d8d, 0xe0e020c0, 0xe2e022c2, 0xc0c808c8, 0x13141707,
+ 0xa1a42585, 0x838c0f8f, 0x03000303, 0x73783b4b, 0xb3b83b8b, 0x13101303, 0xd2d012c2, 0xe2ec2ece,
+ 0x70703040, 0x808c0c8c, 0x333c3f0f, 0xa0a82888, 0x32303202, 0xd1dc1dcd, 0xf2f436c6, 0x70743444,
+ 0xe0ec2ccc, 0x91941585, 0x03080b0b, 0x53541747, 0x505c1c4c, 0x53581b4b, 0xb1bc3d8d, 0x01000101,
+ 0x20242404, 0x101c1c0c, 0x73703343, 0x90981888, 0x10101000, 0xc0cc0ccc, 0xf2f032c2, 0xd1d819c9,
+ 0x202c2c0c, 0xe3e427c7, 0x72703242, 0x83800383, 0x93981b8b, 0xd1d011c1, 0x82840686, 0xc1c809c9,
+ 0x60602040, 0x50501040, 0xa3a02383, 0xe3e82bcb, 0x010c0d0d, 0xb2b43686, 0x929c1e8e, 0x434c0f4f,
+ 0xb3b43787, 0x52581a4a, 0xc2c406c6, 0x70783848, 0xa2a42686, 0x12101202, 0xa3ac2f8f, 0xd1d415c5,
+ 0x61602141, 0xc3c003c3, 0xb0b43484, 0x41400141, 0x52501242, 0x717c3d4d, 0x818c0d8d, 0x00080808,
+ 0x131c1f0f, 0x91981989, 0x00000000, 0x11181909, 0x00040404, 0x53501343, 0xf3f437c7, 0xe1e021c1,
+ 0xf1fc3dcd, 0x72743646, 0x232c2f0f, 0x23242707, 0xb0b03080, 0x83880b8b, 0x020c0e0e, 0xa3a82b8b,
+ 0xa2a02282, 0x626c2e4e, 0x93901383, 0x414c0d4d, 0x61682949, 0x707c3c4c, 0x01080909, 0x02080a0a,
+ 0xb3bc3f8f, 0xe3ec2fcf, 0xf3f033c3, 0xc1c405c5, 0x83840787, 0x10141404, 0xf2fc3ece, 0x60642444,
+ 0xd2dc1ece, 0x222c2e0e, 0x43480b4b, 0x12181a0a, 0x02040606, 0x21202101, 0x63682b4b, 0x62642646,
+ 0x02000202, 0xf1f435c5, 0x92901282, 0x82880a8a, 0x000c0c0c, 0xb3b03383, 0x727c3e4e, 0xd0d010c0,
+ 0x72783a4a, 0x43440747, 0x92941686, 0xe1e425c5, 0x22242606, 0x80800080, 0xa1ac2d8d, 0xd3dc1fcf,
+ 0xa1a02181, 0x30303000, 0x33343707, 0xa2ac2e8e, 0x32343606, 0x11141505, 0x22202202, 0x30383808,
+ 0xf0f434c4, 0xa3a42787, 0x41440545, 0x404c0c4c, 0x81800181, 0xe1e829c9, 0x80840484, 0x93941787,
+ 0x31343505, 0xc3c80bcb, 0xc2cc0ece, 0x303c3c0c, 0x71703141, 0x11101101, 0xc3c407c7, 0x81880989,
+ 0x71743545, 0xf3f83bcb, 0xd2d81aca, 0xf0f838c8, 0x90941484, 0x51581949, 0x82800282, 0xc0c404c4,
+ 0xf3fc3fcf, 0x41480949, 0x31383909, 0x63642747, 0xc0c000c0, 0xc3cc0fcf, 0xd3d417c7, 0xb0b83888,
+ 0x030c0f0f, 0x828c0e8e, 0x42400242, 0x23202303, 0x91901181, 0x606c2c4c, 0xd3d81bcb, 0xa0a42484,
+ 0x30343404, 0xf1f031c1, 0x40480848, 0xc2c002c2, 0x636c2f4f, 0x313c3d0d, 0x212c2d0d, 0x40400040,
+ 0xb2bc3e8e, 0x323c3e0e, 0xb0bc3c8c, 0xc1c001c1, 0xa2a82a8a, 0xb2b83a8a, 0x424c0e4e, 0x51541545,
+ 0x33383b0b, 0xd0dc1ccc, 0x60682848, 0x737c3f4f, 0x909c1c8c, 0xd0d818c8, 0x42480a4a, 0x52541646,
+ 0x73743747, 0xa0a02080, 0xe1ec2dcd, 0x42440646, 0xb1b43585, 0x23282b0b, 0x61642545, 0xf2f83aca,
+ 0xe3e023c3, 0xb1b83989, 0xb1b03181, 0x939c1f8f, 0x525c1e4e, 0xf1f839c9, 0xe2e426c6, 0xb2b03282,
+ 0x31303101, 0xe2e82aca, 0x616c2d4d, 0x535c1f4f, 0xe0e424c4, 0xf0f030c0, 0xc1cc0dcd, 0x80880888,
+ 0x12141606, 0x32383a0a, 0x50581848, 0xd0d414c4, 0x62602242, 0x21282909, 0x03040707, 0x33303303,
+ 0xe0e828c8, 0x13181b0b, 0x01040505, 0x71783949, 0x90901080, 0x62682a4a, 0x22282a0a, 0x92981a8a
+ };
+
+
+ private static final int[] SS3 =
+ {
+
+ 0x08303838, 0xc8e0e828, 0x0d212c2d, 0x86a2a426, 0xcfc3cc0f, 0xced2dc1e, 0x83b3b033, 0x88b0b838,
+ 0x8fa3ac2f, 0x40606020, 0x45515415, 0xc7c3c407, 0x44404404, 0x4f636c2f, 0x4b63682b, 0x4b53581b,
+ 0xc3c3c003, 0x42626022, 0x03333033, 0x85b1b435, 0x09212829, 0x80a0a020, 0xc2e2e022, 0x87a3a427,
+ 0xc3d3d013, 0x81919011, 0x01111011, 0x06020406, 0x0c101c1c, 0x8cb0bc3c, 0x06323436, 0x4b43480b,
+ 0xcfe3ec2f, 0x88808808, 0x4c606c2c, 0x88a0a828, 0x07131417, 0xc4c0c404, 0x06121416, 0xc4f0f434,
+ 0xc2c2c002, 0x45414405, 0xc1e1e021, 0xc6d2d416, 0x0f333c3f, 0x0d313c3d, 0x8e828c0e, 0x88909818,
+ 0x08202828, 0x4e424c0e, 0xc6f2f436, 0x0e323c3e, 0x85a1a425, 0xc9f1f839, 0x0d010c0d, 0xcfd3dc1f,
+ 0xc8d0d818, 0x0b23282b, 0x46626426, 0x4a72783a, 0x07232427, 0x0f232c2f, 0xc1f1f031, 0x42727032,
+ 0x42424002, 0xc4d0d414, 0x41414001, 0xc0c0c000, 0x43737033, 0x47636427, 0x8ca0ac2c, 0x8b83880b,
+ 0xc7f3f437, 0x8da1ac2d, 0x80808000, 0x0f131c1f, 0xcac2c80a, 0x0c202c2c, 0x8aa2a82a, 0x04303434,
+ 0xc2d2d012, 0x0b03080b, 0xcee2ec2e, 0xc9e1e829, 0x4d515c1d, 0x84909414, 0x08101818, 0xc8f0f838,
+ 0x47535417, 0x8ea2ac2e, 0x08000808, 0xc5c1c405, 0x03131013, 0xcdc1cc0d, 0x86828406, 0x89b1b839,
+ 0xcff3fc3f, 0x4d717c3d, 0xc1c1c001, 0x01313031, 0xc5f1f435, 0x8a82880a, 0x4a62682a, 0x81b1b031,
+ 0xc1d1d011, 0x00202020, 0xc7d3d417, 0x02020002, 0x02222022, 0x04000404, 0x48606828, 0x41717031,
+ 0x07030407, 0xcbd3d81b, 0x8d919c1d, 0x89919819, 0x41616021, 0x8eb2bc3e, 0xc6e2e426, 0x49515819,
+ 0xcdd1dc1d, 0x41515011, 0x80909010, 0xccd0dc1c, 0x8a92981a, 0x83a3a023, 0x8ba3a82b, 0xc0d0d010,
+ 0x81818001, 0x0f030c0f, 0x47434407, 0x0a12181a, 0xc3e3e023, 0xcce0ec2c, 0x8d818c0d, 0x8fb3bc3f,
+ 0x86929416, 0x4b73783b, 0x4c505c1c, 0x82a2a022, 0x81a1a021, 0x43636023, 0x03232023, 0x4d414c0d,
+ 0xc8c0c808, 0x8e929c1e, 0x8c909c1c, 0x0a32383a, 0x0c000c0c, 0x0e222c2e, 0x8ab2b83a, 0x4e626c2e,
+ 0x8f939c1f, 0x4a52581a, 0xc2f2f032, 0x82929012, 0xc3f3f033, 0x49414809, 0x48707838, 0xccc0cc0c,
+ 0x05111415, 0xcbf3f83b, 0x40707030, 0x45717435, 0x4f737c3f, 0x05313435, 0x00101010, 0x03030003,
+ 0x44606424, 0x4d616c2d, 0xc6c2c406, 0x44707434, 0xc5d1d415, 0x84b0b434, 0xcae2e82a, 0x09010809,
+ 0x46727436, 0x09111819, 0xcef2fc3e, 0x40404000, 0x02121012, 0xc0e0e020, 0x8db1bc3d, 0x05010405,
+ 0xcaf2f83a, 0x01010001, 0xc0f0f030, 0x0a22282a, 0x4e525c1e, 0x89a1a829, 0x46525416, 0x43434003,
+ 0x85818405, 0x04101414, 0x89818809, 0x8b93981b, 0x80b0b030, 0xc5e1e425, 0x48404808, 0x49717839,
+ 0x87939417, 0xccf0fc3c, 0x0e121c1e, 0x82828002, 0x01212021, 0x8c808c0c, 0x0b13181b, 0x4f535c1f,
+ 0x47737437, 0x44505414, 0x82b2b032, 0x0d111c1d, 0x05212425, 0x4f434c0f, 0x00000000, 0x46424406,
+ 0xcde1ec2d, 0x48505818, 0x42525012, 0xcbe3e82b, 0x4e727c3e, 0xcad2d81a, 0xc9c1c809, 0xcdf1fc3d,
+ 0x00303030, 0x85919415, 0x45616425, 0x0c303c3c, 0x86b2b436, 0xc4e0e424, 0x8bb3b83b, 0x4c707c3c,
+ 0x0e020c0e, 0x40505010, 0x09313839, 0x06222426, 0x02323032, 0x84808404, 0x49616829, 0x83939013,
+ 0x07333437, 0xc7e3e427, 0x04202424, 0x84a0a424, 0xcbc3c80b, 0x43535013, 0x0a02080a, 0x87838407,
+ 0xc9d1d819, 0x4c404c0c, 0x83838003, 0x8f838c0f, 0xcec2cc0e, 0x0b33383b, 0x4a42480a, 0x87b3b437
+ };
+
+
+ private static final int[] KC =
+ {
+ 0x9e3779b9, 0x3c6ef373, 0x78dde6e6, 0xf1bbcdcc,
+ 0xe3779b99, 0xc6ef3733, 0x8dde6e67, 0x1bbcdccf,
+ 0x3779b99e, 0x6ef3733c, 0xdde6e678, 0xbbcdccf1,
+ 0x779b99e3, 0xef3733c6, 0xde6e678d, 0xbcdccf1b
+ };
+
+ private int[] wKey;
+ private boolean forEncryption;
+
+ public void init(boolean forEncryption, CipherParameters params) throws IllegalArgumentException
+ {
+ this.forEncryption = forEncryption;
+ wKey = createWorkingKey(((KeyParameter)params).getKey());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "SEED";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff) throws DataLengthException, IllegalStateException
+ {
+ if (wKey == null)
+ {
+ throw new IllegalStateException("SEED engine not initialised");
+ }
+
+ if (inOff + BLOCK_SIZE > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if (outOff + BLOCK_SIZE > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ long l = bytesToLong(in, inOff + 0);
+ long r = bytesToLong(in, inOff + 8);
+
+ if (forEncryption)
+ {
+ for (int i = 0; i < 16; i++)
+ {
+ long nl = r;
+
+ r = l ^ F(wKey[2 * i], wKey[(2 * i) + 1], r);
+ l = nl;
+ }
+ }
+ else
+ {
+ for (int i = 15; i >= 0; i--)
+ {
+ long nl = r;
+
+ r = l ^ F(wKey[2 * i], wKey[(2 * i) + 1], r);
+ l = nl;
+ }
+ }
+
+ longToBytes(out, outOff + 0, r);
+ longToBytes(out, outOff + 8, l);
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+
+ private int[] createWorkingKey(byte[] inKey)
+ {
+ int[] key = new int[32];
+ long lower = bytesToLong(inKey, 0);
+ long upper = bytesToLong(inKey, 8);
+
+ int key0 = extractW0(lower);
+ int key1 = extractW1(lower);
+ int key2 = extractW0(upper);
+ int key3 = extractW1(upper);
+
+ for (int i = 0; i < 16; i++)
+ {
+ key[2 * i] = G(key0 + key2 - KC[i]);
+ key[2 * i + 1] = G(key1 - key3 + KC[i]);
+
+ if (i % 2 == 0)
+ {
+ lower = rotateRight8(lower);
+ key0 = extractW0(lower);
+ key1 = extractW1(lower);
+ }
+ else
+ {
+ upper = rotateLeft8(upper);
+ key2 = extractW0(upper);
+ key3 = extractW1(upper);
+ }
+ }
+
+ return key;
+ }
+
+ private int extractW1(long lVal)
+ {
+ return (int)lVal;
+ }
+
+ private int extractW0(long lVal)
+ {
+ return (int)(lVal >> 32);
+ }
+
+ private long rotateLeft8(long x)
+ {
+ return (x << 8) | (x >>> 56);
+ }
+
+ private long rotateRight8(long x)
+ {
+ return (x >>> 8) | (x << 56);
+ }
+
+ private long bytesToLong(
+ byte[] src,
+ int srcOff)
+ {
+ long word = 0;
+
+ for (int i = 0; i <= 7; i++)
+ {
+ word = (word << 8) + (src[i + srcOff] & 0xff);
+ }
+
+ return word;
+ }
+
+ private void longToBytes(
+ byte[] dest,
+ int destOff,
+ long value)
+ {
+ for (int i = 0; i < 8; i++)
+ {
+ dest[i + destOff] = (byte)(value >> ((7 - i) * 8));
+ }
+ }
+
+ private int G(int x)
+ {
+ return SS0[x & 0xff] ^ SS1[(x >> 8) & 0xff] ^ SS2[(x >> 16) & 0xff] ^ SS3[(x >> 24) & 0xff];
+ }
+
+ private long F(int ki0, int ki1, long r)
+ {
+ int r0 = (int)(r >> 32);
+ int r1 = (int)r;
+ int rd1 = phaseCalc2(r0, ki0, r1, ki1);
+ int rd0 = rd1 + phaseCalc1(r0, ki0, r1, ki1);
+
+ return ((long)rd0 << 32) | (rd1 & 0xffffffffL);
+ }
+
+ private int phaseCalc1(int r0, int ki0, int r1, int ki1)
+ {
+ return G(G((r0 ^ ki0) ^ (r1 ^ ki1)) + (r0 ^ ki0));
+ }
+
+ private int phaseCalc2(int r0, int ki0, int r1, int ki1)
+ {
+ return G(phaseCalc1(r0, ki0, r1, ki1) + G((r0 ^ ki0) ^ (r1 ^ ki1)));
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/SEEDWrapEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/SEEDWrapEngine.java
new file mode 100644
index 00000000..5b65b00c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/SEEDWrapEngine.java
@@ -0,0 +1,15 @@
+package org.bouncycastle.crypto.engines;
+
+/**
+ * An implementation of the SEED key wrapper based on RFC 4010/RFC 3394.
+ * <p>
+ * For further details see: <a href="http://www.ietf.org/rfc/rfc4010.txt">http://www.ietf.org/rfc/rfc4010.txt</a>.
+ */
+public class SEEDWrapEngine
+ extends RFC3394WrapEngine
+{
+ public SEEDWrapEngine()
+ {
+ super(new SEEDEngine());
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java b/core/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java
new file mode 100644
index 00000000..6d4210d6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java
@@ -0,0 +1,321 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.MaxBytesExceededException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.util.Pack;
+import org.bouncycastle.util.Strings;
+
+/**
+ * Implementation of Daniel J. Bernstein's Salsa20 stream cipher, Snuffle 2005
+ */
+
+public class Salsa20Engine
+ implements StreamCipher
+{
+ /** Constants */
+ private final static int STATE_SIZE = 16; // 16, 32 bit ints = 64 bytes
+
+ private final static byte[]
+ sigma = Strings.toByteArray("expand 32-byte k"),
+ tau = Strings.toByteArray("expand 16-byte k");
+
+ /*
+ * variables to hold the state of the engine
+ * during encryption and decryption
+ */
+ private int index = 0;
+ private int[] engineState = new int[STATE_SIZE]; // state
+ private int[] x = new int[STATE_SIZE] ; // internal buffer
+ private byte[] keyStream = new byte[STATE_SIZE * 4], // expanded state, 64 bytes
+ workingKey = null,
+ workingIV = null;
+ private boolean initialised = false;
+
+ /*
+ * internal counter
+ */
+ private int cW0, cW1, cW2;
+
+ /**
+ * initialise a Salsa20 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ /*
+ * Salsa20 encryption and decryption is completely
+ * symmetrical, so the 'forEncryption' is
+ * irrelevant. (Like 90% of stream ciphers)
+ */
+
+ if (!(params instanceof ParametersWithIV))
+ {
+ throw new IllegalArgumentException("Salsa20 Init parameters must include an IV");
+ }
+
+ ParametersWithIV ivParams = (ParametersWithIV) params;
+
+ byte[] iv = ivParams.getIV();
+
+ if (iv == null || iv.length != 8)
+ {
+ throw new IllegalArgumentException("Salsa20 requires exactly 8 bytes of IV");
+ }
+
+ if (!(ivParams.getParameters() instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("Salsa20 Init parameters must include a key");
+ }
+
+ KeyParameter key = (KeyParameter) ivParams.getParameters();
+
+ workingKey = key.getKey();
+ workingIV = iv;
+
+ setKey(workingKey, workingIV);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Salsa20";
+ }
+
+ public byte returnByte(byte in)
+ {
+ if (limitExceeded())
+ {
+ throw new MaxBytesExceededException("2^70 byte limit per IV; Change IV");
+ }
+
+ if (index == 0)
+ {
+ generateKeyStream(keyStream);
+
+ if (++engineState[8] == 0)
+ {
+ ++engineState[9];
+ }
+ }
+
+ byte out = (byte)(keyStream[index]^in);
+ index = (index + 1) & 63;
+
+ return out;
+ }
+
+ public void processBytes(
+ byte[] in,
+ int inOff,
+ int len,
+ byte[] out,
+ int outOff)
+ {
+ if (!initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName()+" not initialised");
+ }
+
+ if ((inOff + len) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + len) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (limitExceeded(len))
+ {
+ throw new MaxBytesExceededException("2^70 byte limit per IV would be exceeded; Change IV");
+ }
+
+ for (int i = 0; i < len; i++)
+ {
+ if (index == 0)
+ {
+ generateKeyStream(keyStream);
+
+ if (++engineState[8] == 0)
+ {
+ ++engineState[9];
+ }
+ }
+
+ out[i+outOff] = (byte)(keyStream[index]^in[i+inOff]);
+ index = (index + 1) & 63;
+ }
+ }
+
+ public void reset()
+ {
+ setKey(workingKey, workingIV);
+ }
+
+ // Private implementation
+
+ private void setKey(byte[] keyBytes, byte[] ivBytes)
+ {
+ workingKey = keyBytes;
+ workingIV = ivBytes;
+
+ index = 0;
+ resetCounter();
+ int offset = 0;
+ byte[] constants;
+
+ // Key
+ engineState[1] = Pack.littleEndianToInt(workingKey, 0);
+ engineState[2] = Pack.littleEndianToInt(workingKey, 4);
+ engineState[3] = Pack.littleEndianToInt(workingKey, 8);
+ engineState[4] = Pack.littleEndianToInt(workingKey, 12);
+
+ if (workingKey.length == 32)
+ {
+ constants = sigma;
+ offset = 16;
+ }
+ else
+ {
+ constants = tau;
+ }
+
+ engineState[11] = Pack.littleEndianToInt(workingKey, offset);
+ engineState[12] = Pack.littleEndianToInt(workingKey, offset+4);
+ engineState[13] = Pack.littleEndianToInt(workingKey, offset+8);
+ engineState[14] = Pack.littleEndianToInt(workingKey, offset+12);
+ engineState[0 ] = Pack.littleEndianToInt(constants, 0);
+ engineState[5 ] = Pack.littleEndianToInt(constants, 4);
+ engineState[10] = Pack.littleEndianToInt(constants, 8);
+ engineState[15] = Pack.littleEndianToInt(constants, 12);
+
+ // IV
+ engineState[6] = Pack.littleEndianToInt(workingIV, 0);
+ engineState[7] = Pack.littleEndianToInt(workingIV, 4);
+ engineState[8] = engineState[9] = 0;
+
+ initialised = true;
+ }
+
+ private void generateKeyStream(byte[] output)
+ {
+ salsaCore(20, engineState, x);
+ Pack.intToLittleEndian(x, output, 0);
+ }
+
+ /**
+ * Salsa20 function
+ *
+ * @param input input data
+ *
+ * @return keystream
+ */
+ public static void salsaCore(int rounds, int[] input, int[] x)
+ {
+ // TODO Exception if rounds odd?
+
+ System.arraycopy(input, 0, x, 0, input.length);
+
+ for (int i = rounds; i > 0; i -= 2)
+ {
+ x[ 4] ^= rotl((x[ 0]+x[12]), 7);
+ x[ 8] ^= rotl((x[ 4]+x[ 0]), 9);
+ x[12] ^= rotl((x[ 8]+x[ 4]),13);
+ x[ 0] ^= rotl((x[12]+x[ 8]),18);
+ x[ 9] ^= rotl((x[ 5]+x[ 1]), 7);
+ x[13] ^= rotl((x[ 9]+x[ 5]), 9);
+ x[ 1] ^= rotl((x[13]+x[ 9]),13);
+ x[ 5] ^= rotl((x[ 1]+x[13]),18);
+ x[14] ^= rotl((x[10]+x[ 6]), 7);
+ x[ 2] ^= rotl((x[14]+x[10]), 9);
+ x[ 6] ^= rotl((x[ 2]+x[14]),13);
+ x[10] ^= rotl((x[ 6]+x[ 2]),18);
+ x[ 3] ^= rotl((x[15]+x[11]), 7);
+ x[ 7] ^= rotl((x[ 3]+x[15]), 9);
+ x[11] ^= rotl((x[ 7]+x[ 3]),13);
+ x[15] ^= rotl((x[11]+x[ 7]),18);
+ x[ 1] ^= rotl((x[ 0]+x[ 3]), 7);
+ x[ 2] ^= rotl((x[ 1]+x[ 0]), 9);
+ x[ 3] ^= rotl((x[ 2]+x[ 1]),13);
+ x[ 0] ^= rotl((x[ 3]+x[ 2]),18);
+ x[ 6] ^= rotl((x[ 5]+x[ 4]), 7);
+ x[ 7] ^= rotl((x[ 6]+x[ 5]), 9);
+ x[ 4] ^= rotl((x[ 7]+x[ 6]),13);
+ x[ 5] ^= rotl((x[ 4]+x[ 7]),18);
+ x[11] ^= rotl((x[10]+x[ 9]), 7);
+ x[ 8] ^= rotl((x[11]+x[10]), 9);
+ x[ 9] ^= rotl((x[ 8]+x[11]),13);
+ x[10] ^= rotl((x[ 9]+x[ 8]),18);
+ x[12] ^= rotl((x[15]+x[14]), 7);
+ x[13] ^= rotl((x[12]+x[15]), 9);
+ x[14] ^= rotl((x[13]+x[12]),13);
+ x[15] ^= rotl((x[14]+x[13]),18);
+ }
+
+ for (int i = 0; i < STATE_SIZE; ++i)
+ {
+ x[i] += input[i];
+ }
+ }
+
+ /**
+ * Rotate left
+ *
+ * @param x value to rotate
+ * @param y amount to rotate x
+ *
+ * @return rotated x
+ */
+ private static int rotl(int x, int y)
+ {
+ return (x << y) | (x >>> -y);
+ }
+
+ private void resetCounter()
+ {
+ cW0 = 0;
+ cW1 = 0;
+ cW2 = 0;
+ }
+
+ private boolean limitExceeded()
+ {
+ if (++cW0 == 0)
+ {
+ if (++cW1 == 0)
+ {
+ return (++cW2 & 0x20) != 0; // 2^(32 + 32 + 6)
+ }
+ }
+
+ return false;
+ }
+
+ /*
+ * this relies on the fact len will always be positive.
+ */
+ private boolean limitExceeded(int len)
+ {
+ cW0 += len;
+ if (cW0 < len && cW0 >= 0)
+ {
+ if (++cW1 == 0)
+ {
+ return (++cW2 & 0x20) != 0; // 2^(32 + 32 + 6)
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/SerpentEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/SerpentEngine.java
new file mode 100644
index 00000000..9da23013
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/SerpentEngine.java
@@ -0,0 +1,783 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * Serpent is a 128-bit 32-round block cipher with variable key lengths,
+ * including 128, 192 and 256 bit keys conjectured to be at least as
+ * secure as three-key triple-DES.
+ * <p>
+ * Serpent was designed by Ross Anderson, Eli Biham and Lars Knudsen as a
+ * candidate algorithm for the NIST AES Quest.>
+ * <p>
+ * For full details see the <a href="http://www.cl.cam.ac.uk/~rja14/serpent.html">The Serpent home page</a>
+ */
+public class SerpentEngine
+ implements BlockCipher
+{
+ private static final int BLOCK_SIZE = 16;
+
+ static final int ROUNDS = 32;
+ static final int PHI = 0x9E3779B9; // (sqrt(5) - 1) * 2**31
+
+ private boolean encrypting;
+ private int[] wKey;
+
+ private int X0, X1, X2, X3; // registers
+
+ /**
+ * initialise a Serpent cipher.
+ *
+ * @param encrypting whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ this.encrypting = encrypting;
+ this.wKey = makeWorkingKey(((KeyParameter)params).getKey());
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to Serpent init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Serpent";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * Process one block of input from the array in and write it to
+ * the out array.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public final int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (wKey == null)
+ {
+ throw new IllegalStateException("Serpent not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (encrypting)
+ {
+ encryptBlock(in, inOff, out, outOff);
+ }
+ else
+ {
+ decryptBlock(in, inOff, out, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+
+ /**
+ * Expand a user-supplied key material into a session key.
+ *
+ * @param key The user-key bytes (multiples of 4) to use.
+ * @exception IllegalArgumentException
+ */
+ private int[] makeWorkingKey(
+ byte[] key)
+ throws IllegalArgumentException
+ {
+ //
+ // pad key to 256 bits
+ //
+ int[] kPad = new int[16];
+ int off = 0;
+ int length = 0;
+
+ for (off = key.length - 4; off > 0; off -= 4)
+ {
+ kPad[length++] = bytesToWord(key, off);
+ }
+
+ if (off == 0)
+ {
+ kPad[length++] = bytesToWord(key, 0);
+ if (length < 8)
+ {
+ kPad[length] = 1;
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("key must be a multiple of 4 bytes");
+ }
+
+ //
+ // expand the padded key up to 33 x 128 bits of key material
+ //
+ int amount = (ROUNDS + 1) * 4;
+ int[] w = new int[amount];
+
+ //
+ // compute w0 to w7 from w-8 to w-1
+ //
+ for (int i = 8; i < 16; i++)
+ {
+ kPad[i] = rotateLeft(kPad[i - 8] ^ kPad[i - 5] ^ kPad[i - 3] ^ kPad[i - 1] ^ PHI ^ (i - 8), 11);
+ }
+
+ System.arraycopy(kPad, 8, w, 0, 8);
+
+ //
+ // compute w8 to w136
+ //
+ for (int i = 8; i < amount; i++)
+ {
+ w[i] = rotateLeft(w[i - 8] ^ w[i - 5] ^ w[i - 3] ^ w[i - 1] ^ PHI ^ i, 11);
+ }
+
+ //
+ // create the working keys by processing w with the Sbox and IP
+ //
+ sb3(w[0], w[1], w[2], w[3]);
+ w[0] = X0; w[1] = X1; w[2] = X2; w[3] = X3;
+ sb2(w[4], w[5], w[6], w[7]);
+ w[4] = X0; w[5] = X1; w[6] = X2; w[7] = X3;
+ sb1(w[8], w[9], w[10], w[11]);
+ w[8] = X0; w[9] = X1; w[10] = X2; w[11] = X3;
+ sb0(w[12], w[13], w[14], w[15]);
+ w[12] = X0; w[13] = X1; w[14] = X2; w[15] = X3;
+ sb7(w[16], w[17], w[18], w[19]);
+ w[16] = X0; w[17] = X1; w[18] = X2; w[19] = X3;
+ sb6(w[20], w[21], w[22], w[23]);
+ w[20] = X0; w[21] = X1; w[22] = X2; w[23] = X3;
+ sb5(w[24], w[25], w[26], w[27]);
+ w[24] = X0; w[25] = X1; w[26] = X2; w[27] = X3;
+ sb4(w[28], w[29], w[30], w[31]);
+ w[28] = X0; w[29] = X1; w[30] = X2; w[31] = X3;
+ sb3(w[32], w[33], w[34], w[35]);
+ w[32] = X0; w[33] = X1; w[34] = X2; w[35] = X3;
+ sb2(w[36], w[37], w[38], w[39]);
+ w[36] = X0; w[37] = X1; w[38] = X2; w[39] = X3;
+ sb1(w[40], w[41], w[42], w[43]);
+ w[40] = X0; w[41] = X1; w[42] = X2; w[43] = X3;
+ sb0(w[44], w[45], w[46], w[47]);
+ w[44] = X0; w[45] = X1; w[46] = X2; w[47] = X3;
+ sb7(w[48], w[49], w[50], w[51]);
+ w[48] = X0; w[49] = X1; w[50] = X2; w[51] = X3;
+ sb6(w[52], w[53], w[54], w[55]);
+ w[52] = X0; w[53] = X1; w[54] = X2; w[55] = X3;
+ sb5(w[56], w[57], w[58], w[59]);
+ w[56] = X0; w[57] = X1; w[58] = X2; w[59] = X3;
+ sb4(w[60], w[61], w[62], w[63]);
+ w[60] = X0; w[61] = X1; w[62] = X2; w[63] = X3;
+ sb3(w[64], w[65], w[66], w[67]);
+ w[64] = X0; w[65] = X1; w[66] = X2; w[67] = X3;
+ sb2(w[68], w[69], w[70], w[71]);
+ w[68] = X0; w[69] = X1; w[70] = X2; w[71] = X3;
+ sb1(w[72], w[73], w[74], w[75]);
+ w[72] = X0; w[73] = X1; w[74] = X2; w[75] = X3;
+ sb0(w[76], w[77], w[78], w[79]);
+ w[76] = X0; w[77] = X1; w[78] = X2; w[79] = X3;
+ sb7(w[80], w[81], w[82], w[83]);
+ w[80] = X0; w[81] = X1; w[82] = X2; w[83] = X3;
+ sb6(w[84], w[85], w[86], w[87]);
+ w[84] = X0; w[85] = X1; w[86] = X2; w[87] = X3;
+ sb5(w[88], w[89], w[90], w[91]);
+ w[88] = X0; w[89] = X1; w[90] = X2; w[91] = X3;
+ sb4(w[92], w[93], w[94], w[95]);
+ w[92] = X0; w[93] = X1; w[94] = X2; w[95] = X3;
+ sb3(w[96], w[97], w[98], w[99]);
+ w[96] = X0; w[97] = X1; w[98] = X2; w[99] = X3;
+ sb2(w[100], w[101], w[102], w[103]);
+ w[100] = X0; w[101] = X1; w[102] = X2; w[103] = X3;
+ sb1(w[104], w[105], w[106], w[107]);
+ w[104] = X0; w[105] = X1; w[106] = X2; w[107] = X3;
+ sb0(w[108], w[109], w[110], w[111]);
+ w[108] = X0; w[109] = X1; w[110] = X2; w[111] = X3;
+ sb7(w[112], w[113], w[114], w[115]);
+ w[112] = X0; w[113] = X1; w[114] = X2; w[115] = X3;
+ sb6(w[116], w[117], w[118], w[119]);
+ w[116] = X0; w[117] = X1; w[118] = X2; w[119] = X3;
+ sb5(w[120], w[121], w[122], w[123]);
+ w[120] = X0; w[121] = X1; w[122] = X2; w[123] = X3;
+ sb4(w[124], w[125], w[126], w[127]);
+ w[124] = X0; w[125] = X1; w[126] = X2; w[127] = X3;
+ sb3(w[128], w[129], w[130], w[131]);
+ w[128] = X0; w[129] = X1; w[130] = X2; w[131] = X3;
+
+ return w;
+ }
+
+ private int rotateLeft(
+ int x,
+ int bits)
+ {
+ return (x << bits) | (x >>> -bits);
+ }
+
+ private int rotateRight(
+ int x,
+ int bits)
+ {
+ return (x >>> bits) | (x << -bits);
+ }
+
+ private int bytesToWord(
+ byte[] src,
+ int srcOff)
+ {
+ return (((src[srcOff] & 0xff) << 24) | ((src[srcOff + 1] & 0xff) << 16) |
+ ((src[srcOff + 2] & 0xff) << 8) | ((src[srcOff + 3] & 0xff)));
+ }
+
+ private void wordToBytes(
+ int word,
+ byte[] dst,
+ int dstOff)
+ {
+ dst[dstOff + 3] = (byte)(word);
+ dst[dstOff + 2] = (byte)(word >>> 8);
+ dst[dstOff + 1] = (byte)(word >>> 16);
+ dst[dstOff] = (byte)(word >>> 24);
+ }
+
+ /**
+ * Encrypt one block of plaintext.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ */
+ private void encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ X3 = bytesToWord(in, inOff);
+ X2 = bytesToWord(in, inOff + 4);
+ X1 = bytesToWord(in, inOff + 8);
+ X0 = bytesToWord(in, inOff + 12);
+
+ sb0(wKey[0] ^ X0, wKey[1] ^ X1, wKey[2] ^ X2, wKey[3] ^ X3); LT();
+ sb1(wKey[4] ^ X0, wKey[5] ^ X1, wKey[6] ^ X2, wKey[7] ^ X3); LT();
+ sb2(wKey[8] ^ X0, wKey[9] ^ X1, wKey[10] ^ X2, wKey[11] ^ X3); LT();
+ sb3(wKey[12] ^ X0, wKey[13] ^ X1, wKey[14] ^ X2, wKey[15] ^ X3); LT();
+ sb4(wKey[16] ^ X0, wKey[17] ^ X1, wKey[18] ^ X2, wKey[19] ^ X3); LT();
+ sb5(wKey[20] ^ X0, wKey[21] ^ X1, wKey[22] ^ X2, wKey[23] ^ X3); LT();
+ sb6(wKey[24] ^ X0, wKey[25] ^ X1, wKey[26] ^ X2, wKey[27] ^ X3); LT();
+ sb7(wKey[28] ^ X0, wKey[29] ^ X1, wKey[30] ^ X2, wKey[31] ^ X3); LT();
+ sb0(wKey[32] ^ X0, wKey[33] ^ X1, wKey[34] ^ X2, wKey[35] ^ X3); LT();
+ sb1(wKey[36] ^ X0, wKey[37] ^ X1, wKey[38] ^ X2, wKey[39] ^ X3); LT();
+ sb2(wKey[40] ^ X0, wKey[41] ^ X1, wKey[42] ^ X2, wKey[43] ^ X3); LT();
+ sb3(wKey[44] ^ X0, wKey[45] ^ X1, wKey[46] ^ X2, wKey[47] ^ X3); LT();
+ sb4(wKey[48] ^ X0, wKey[49] ^ X1, wKey[50] ^ X2, wKey[51] ^ X3); LT();
+ sb5(wKey[52] ^ X0, wKey[53] ^ X1, wKey[54] ^ X2, wKey[55] ^ X3); LT();
+ sb6(wKey[56] ^ X0, wKey[57] ^ X1, wKey[58] ^ X2, wKey[59] ^ X3); LT();
+ sb7(wKey[60] ^ X0, wKey[61] ^ X1, wKey[62] ^ X2, wKey[63] ^ X3); LT();
+ sb0(wKey[64] ^ X0, wKey[65] ^ X1, wKey[66] ^ X2, wKey[67] ^ X3); LT();
+ sb1(wKey[68] ^ X0, wKey[69] ^ X1, wKey[70] ^ X2, wKey[71] ^ X3); LT();
+ sb2(wKey[72] ^ X0, wKey[73] ^ X1, wKey[74] ^ X2, wKey[75] ^ X3); LT();
+ sb3(wKey[76] ^ X0, wKey[77] ^ X1, wKey[78] ^ X2, wKey[79] ^ X3); LT();
+ sb4(wKey[80] ^ X0, wKey[81] ^ X1, wKey[82] ^ X2, wKey[83] ^ X3); LT();
+ sb5(wKey[84] ^ X0, wKey[85] ^ X1, wKey[86] ^ X2, wKey[87] ^ X3); LT();
+ sb6(wKey[88] ^ X0, wKey[89] ^ X1, wKey[90] ^ X2, wKey[91] ^ X3); LT();
+ sb7(wKey[92] ^ X0, wKey[93] ^ X1, wKey[94] ^ X2, wKey[95] ^ X3); LT();
+ sb0(wKey[96] ^ X0, wKey[97] ^ X1, wKey[98] ^ X2, wKey[99] ^ X3); LT();
+ sb1(wKey[100] ^ X0, wKey[101] ^ X1, wKey[102] ^ X2, wKey[103] ^ X3); LT();
+ sb2(wKey[104] ^ X0, wKey[105] ^ X1, wKey[106] ^ X2, wKey[107] ^ X3); LT();
+ sb3(wKey[108] ^ X0, wKey[109] ^ X1, wKey[110] ^ X2, wKey[111] ^ X3); LT();
+ sb4(wKey[112] ^ X0, wKey[113] ^ X1, wKey[114] ^ X2, wKey[115] ^ X3); LT();
+ sb5(wKey[116] ^ X0, wKey[117] ^ X1, wKey[118] ^ X2, wKey[119] ^ X3); LT();
+ sb6(wKey[120] ^ X0, wKey[121] ^ X1, wKey[122] ^ X2, wKey[123] ^ X3); LT();
+ sb7(wKey[124] ^ X0, wKey[125] ^ X1, wKey[126] ^ X2, wKey[127] ^ X3);
+
+ wordToBytes(wKey[131] ^ X3, out, outOff);
+ wordToBytes(wKey[130] ^ X2, out, outOff + 4);
+ wordToBytes(wKey[129] ^ X1, out, outOff + 8);
+ wordToBytes(wKey[128] ^ X0, out, outOff + 12);
+ }
+
+ /**
+ * Decrypt one block of ciphertext.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ */
+ private void decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ X3 = wKey[131] ^ bytesToWord(in, inOff);
+ X2 = wKey[130] ^ bytesToWord(in, inOff + 4);
+ X1 = wKey[129] ^ bytesToWord(in, inOff + 8);
+ X0 = wKey[128] ^ bytesToWord(in, inOff + 12);
+
+ ib7(X0, X1, X2, X3);
+ X0 ^= wKey[124]; X1 ^= wKey[125]; X2 ^= wKey[126]; X3 ^= wKey[127];
+ inverseLT(); ib6(X0, X1, X2, X3);
+ X0 ^= wKey[120]; X1 ^= wKey[121]; X2 ^= wKey[122]; X3 ^= wKey[123];
+ inverseLT(); ib5(X0, X1, X2, X3);
+ X0 ^= wKey[116]; X1 ^= wKey[117]; X2 ^= wKey[118]; X3 ^= wKey[119];
+ inverseLT(); ib4(X0, X1, X2, X3);
+ X0 ^= wKey[112]; X1 ^= wKey[113]; X2 ^= wKey[114]; X3 ^= wKey[115];
+ inverseLT(); ib3(X0, X1, X2, X3);
+ X0 ^= wKey[108]; X1 ^= wKey[109]; X2 ^= wKey[110]; X3 ^= wKey[111];
+ inverseLT(); ib2(X0, X1, X2, X3);
+ X0 ^= wKey[104]; X1 ^= wKey[105]; X2 ^= wKey[106]; X3 ^= wKey[107];
+ inverseLT(); ib1(X0, X1, X2, X3);
+ X0 ^= wKey[100]; X1 ^= wKey[101]; X2 ^= wKey[102]; X3 ^= wKey[103];
+ inverseLT(); ib0(X0, X1, X2, X3);
+ X0 ^= wKey[96]; X1 ^= wKey[97]; X2 ^= wKey[98]; X3 ^= wKey[99];
+ inverseLT(); ib7(X0, X1, X2, X3);
+ X0 ^= wKey[92]; X1 ^= wKey[93]; X2 ^= wKey[94]; X3 ^= wKey[95];
+ inverseLT(); ib6(X0, X1, X2, X3);
+ X0 ^= wKey[88]; X1 ^= wKey[89]; X2 ^= wKey[90]; X3 ^= wKey[91];
+ inverseLT(); ib5(X0, X1, X2, X3);
+ X0 ^= wKey[84]; X1 ^= wKey[85]; X2 ^= wKey[86]; X3 ^= wKey[87];
+ inverseLT(); ib4(X0, X1, X2, X3);
+ X0 ^= wKey[80]; X1 ^= wKey[81]; X2 ^= wKey[82]; X3 ^= wKey[83];
+ inverseLT(); ib3(X0, X1, X2, X3);
+ X0 ^= wKey[76]; X1 ^= wKey[77]; X2 ^= wKey[78]; X3 ^= wKey[79];
+ inverseLT(); ib2(X0, X1, X2, X3);
+ X0 ^= wKey[72]; X1 ^= wKey[73]; X2 ^= wKey[74]; X3 ^= wKey[75];
+ inverseLT(); ib1(X0, X1, X2, X3);
+ X0 ^= wKey[68]; X1 ^= wKey[69]; X2 ^= wKey[70]; X3 ^= wKey[71];
+ inverseLT(); ib0(X0, X1, X2, X3);
+ X0 ^= wKey[64]; X1 ^= wKey[65]; X2 ^= wKey[66]; X3 ^= wKey[67];
+ inverseLT(); ib7(X0, X1, X2, X3);
+ X0 ^= wKey[60]; X1 ^= wKey[61]; X2 ^= wKey[62]; X3 ^= wKey[63];
+ inverseLT(); ib6(X0, X1, X2, X3);
+ X0 ^= wKey[56]; X1 ^= wKey[57]; X2 ^= wKey[58]; X3 ^= wKey[59];
+ inverseLT(); ib5(X0, X1, X2, X3);
+ X0 ^= wKey[52]; X1 ^= wKey[53]; X2 ^= wKey[54]; X3 ^= wKey[55];
+ inverseLT(); ib4(X0, X1, X2, X3);
+ X0 ^= wKey[48]; X1 ^= wKey[49]; X2 ^= wKey[50]; X3 ^= wKey[51];
+ inverseLT(); ib3(X0, X1, X2, X3);
+ X0 ^= wKey[44]; X1 ^= wKey[45]; X2 ^= wKey[46]; X3 ^= wKey[47];
+ inverseLT(); ib2(X0, X1, X2, X3);
+ X0 ^= wKey[40]; X1 ^= wKey[41]; X2 ^= wKey[42]; X3 ^= wKey[43];
+ inverseLT(); ib1(X0, X1, X2, X3);
+ X0 ^= wKey[36]; X1 ^= wKey[37]; X2 ^= wKey[38]; X3 ^= wKey[39];
+ inverseLT(); ib0(X0, X1, X2, X3);
+ X0 ^= wKey[32]; X1 ^= wKey[33]; X2 ^= wKey[34]; X3 ^= wKey[35];
+ inverseLT(); ib7(X0, X1, X2, X3);
+ X0 ^= wKey[28]; X1 ^= wKey[29]; X2 ^= wKey[30]; X3 ^= wKey[31];
+ inverseLT(); ib6(X0, X1, X2, X3);
+ X0 ^= wKey[24]; X1 ^= wKey[25]; X2 ^= wKey[26]; X3 ^= wKey[27];
+ inverseLT(); ib5(X0, X1, X2, X3);
+ X0 ^= wKey[20]; X1 ^= wKey[21]; X2 ^= wKey[22]; X3 ^= wKey[23];
+ inverseLT(); ib4(X0, X1, X2, X3);
+ X0 ^= wKey[16]; X1 ^= wKey[17]; X2 ^= wKey[18]; X3 ^= wKey[19];
+ inverseLT(); ib3(X0, X1, X2, X3);
+ X0 ^= wKey[12]; X1 ^= wKey[13]; X2 ^= wKey[14]; X3 ^= wKey[15];
+ inverseLT(); ib2(X0, X1, X2, X3);
+ X0 ^= wKey[8]; X1 ^= wKey[9]; X2 ^= wKey[10]; X3 ^= wKey[11];
+ inverseLT(); ib1(X0, X1, X2, X3);
+ X0 ^= wKey[4]; X1 ^= wKey[5]; X2 ^= wKey[6]; X3 ^= wKey[7];
+ inverseLT(); ib0(X0, X1, X2, X3);
+
+ wordToBytes(X3 ^ wKey[3], out, outOff);
+ wordToBytes(X2 ^ wKey[2], out, outOff + 4);
+ wordToBytes(X1 ^ wKey[1], out, outOff + 8);
+ wordToBytes(X0 ^ wKey[0], out, outOff + 12);
+ }
+
+ /**
+ * The sboxes below are based on the work of Brian Gladman and
+ * Sam Simpson, whose original notice appears below.
+ * <p>
+ * For further details see:
+ * http://fp.gladman.plus.com/cryptography_technology/serpent/
+ */
+
+ /* Partially optimised Serpent S Box boolean functions derived */
+ /* using a recursive descent analyser but without a full search */
+ /* of all subtrees. This set of S boxes is the result of work */
+ /* by Sam Simpson and Brian Gladman using the spare time on a */
+ /* cluster of high capacity servers to search for S boxes with */
+ /* this customised search engine. There are now an average of */
+ /* 15.375 terms per S box. */
+ /* */
+ /* Copyright: Dr B. R Gladman (gladman@seven77.demon.co.uk) */
+ /* and Sam Simpson (s.simpson@mia.co.uk) */
+ /* 17th December 1998 */
+ /* */
+ /* We hereby give permission for information in this file to be */
+ /* used freely subject only to acknowledgement of its origin. */
+
+ /**
+ * S0 - { 3, 8,15, 1,10, 6, 5,11,14,13, 4, 2, 7, 0, 9,12 } - 15 terms.
+ */
+ private void sb0(int a, int b, int c, int d)
+ {
+ int t1 = a ^ d;
+ int t3 = c ^ t1;
+ int t4 = b ^ t3;
+ X3 = (a & d) ^ t4;
+ int t7 = a ^ (b & t1);
+ X2 = t4 ^ (c | t7);
+ int t12 = X3 & (t3 ^ t7);
+ X1 = (~t3) ^ t12;
+ X0 = t12 ^ (~t7);
+ }
+
+ /**
+ * InvSO - {13, 3,11, 0,10, 6, 5,12, 1,14, 4, 7,15, 9, 8, 2 } - 15 terms.
+ */
+ private void ib0(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = a ^ b;
+ int t4 = d ^ (t1 | t2);
+ int t5 = c ^ t4;
+ X2 = t2 ^ t5;
+ int t8 = t1 ^ (d & t2);
+ X1 = t4 ^ (X2 & t8);
+ X3 = (a & t4) ^ (t5 | X1);
+ X0 = X3 ^ (t5 ^ t8);
+ }
+
+ /**
+ * S1 - {15,12, 2, 7, 9, 0, 5,10, 1,11,14, 8, 6,13, 3, 4 } - 14 terms.
+ */
+ private void sb1(int a, int b, int c, int d)
+ {
+ int t2 = b ^ (~a);
+ int t5 = c ^ (a | t2);
+ X2 = d ^ t5;
+ int t7 = b ^ (d | t2);
+ int t8 = t2 ^ X2;
+ X3 = t8 ^ (t5 & t7);
+ int t11 = t5 ^ t7;
+ X1 = X3 ^ t11;
+ X0 = t5 ^ (t8 & t11);
+ }
+
+ /**
+ * InvS1 - { 5, 8, 2,14,15, 6,12, 3,11, 4, 7, 9, 1,13,10, 0 } - 14 steps.
+ */
+ private void ib1(int a, int b, int c, int d)
+ {
+ int t1 = b ^ d;
+ int t3 = a ^ (b & t1);
+ int t4 = t1 ^ t3;
+ X3 = c ^ t4;
+ int t7 = b ^ (t1 & t3);
+ int t8 = X3 | t7;
+ X1 = t3 ^ t8;
+ int t10 = ~X1;
+ int t11 = X3 ^ t7;
+ X0 = t10 ^ t11;
+ X2 = t4 ^ (t10 | t11);
+ }
+
+ /**
+ * S2 - { 8, 6, 7, 9, 3,12,10,15,13, 1,14, 4, 0,11, 5, 2 } - 16 terms.
+ */
+ private void sb2(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = b ^ d;
+ int t3 = c & t1;
+ X0 = t2 ^ t3;
+ int t5 = c ^ t1;
+ int t6 = c ^ X0;
+ int t7 = b & t6;
+ X3 = t5 ^ t7;
+ X2 = a ^ ((d | t7) & (X0 | t5));
+ X1 = (t2 ^ X3) ^ (X2 ^ (d | t1));
+ }
+
+ /**
+ * InvS2 - {12, 9,15, 4,11,14, 1, 2, 0, 3, 6,13, 5, 8,10, 7 } - 16 steps.
+ */
+ private void ib2(int a, int b, int c, int d)
+ {
+ int t1 = b ^ d;
+ int t2 = ~t1;
+ int t3 = a ^ c;
+ int t4 = c ^ t1;
+ int t5 = b & t4;
+ X0 = t3 ^ t5;
+ int t7 = a | t2;
+ int t8 = d ^ t7;
+ int t9 = t3 | t8;
+ X3 = t1 ^ t9;
+ int t11 = ~t4;
+ int t12 = X0 | X3;
+ X1 = t11 ^ t12;
+ X2 = (d & t11) ^ (t3 ^ t12);
+ }
+
+ /**
+ * S3 - { 0,15,11, 8,12, 9, 6, 3,13, 1, 2, 4,10, 7, 5,14 } - 16 terms.
+ */
+ private void sb3(int a, int b, int c, int d)
+ {
+ int t1 = a ^ b;
+ int t2 = a & c;
+ int t3 = a | d;
+ int t4 = c ^ d;
+ int t5 = t1 & t3;
+ int t6 = t2 | t5;
+ X2 = t4 ^ t6;
+ int t8 = b ^ t3;
+ int t9 = t6 ^ t8;
+ int t10 = t4 & t9;
+ X0 = t1 ^ t10;
+ int t12 = X2 & X0;
+ X1 = t9 ^ t12;
+ X3 = (b | d) ^ (t4 ^ t12);
+ }
+
+ /**
+ * InvS3 - { 0, 9,10, 7,11,14, 6,13, 3, 5,12, 2, 4, 8,15, 1 } - 15 terms
+ */
+ private void ib3(int a, int b, int c, int d)
+ {
+ int t1 = a | b;
+ int t2 = b ^ c;
+ int t3 = b & t2;
+ int t4 = a ^ t3;
+ int t5 = c ^ t4;
+ int t6 = d | t4;
+ X0 = t2 ^ t6;
+ int t8 = t2 | t6;
+ int t9 = d ^ t8;
+ X2 = t5 ^ t9;
+ int t11 = t1 ^ t9;
+ int t12 = X0 & t11;
+ X3 = t4 ^ t12;
+ X1 = X3 ^ (X0 ^ t11);
+ }
+
+ /**
+ * S4 - { 1,15, 8, 3,12, 0,11, 6, 2, 5, 4,10, 9,14, 7,13 } - 15 terms.
+ */
+ private void sb4(int a, int b, int c, int d)
+ {
+ int t1 = a ^ d;
+ int t2 = d & t1;
+ int t3 = c ^ t2;
+ int t4 = b | t3;
+ X3 = t1 ^ t4;
+ int t6 = ~b;
+ int t7 = t1 | t6;
+ X0 = t3 ^ t7;
+ int t9 = a & X0;
+ int t10 = t1 ^ t6;
+ int t11 = t4 & t10;
+ X2 = t9 ^ t11;
+ X1 = (a ^ t3) ^ (t10 & X2);
+ }
+
+ /**
+ * InvS4 - { 5, 0, 8, 3,10, 9, 7,14, 2,12,11, 6, 4,15,13, 1 } - 15 terms.
+ */
+ private void ib4(int a, int b, int c, int d)
+ {
+ int t1 = c | d;
+ int t2 = a & t1;
+ int t3 = b ^ t2;
+ int t4 = a & t3;
+ int t5 = c ^ t4;
+ X1 = d ^ t5;
+ int t7 = ~a;
+ int t8 = t5 & X1;
+ X3 = t3 ^ t8;
+ int t10 = X1 | t7;
+ int t11 = d ^ t10;
+ X0 = X3 ^ t11;
+ X2 = (t3 & t11) ^ (X1 ^ t7);
+ }
+
+ /**
+ * S5 - {15, 5, 2,11, 4,10, 9,12, 0, 3,14, 8,13, 6, 7, 1 } - 16 terms.
+ */
+ private void sb5(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = a ^ b;
+ int t3 = a ^ d;
+ int t4 = c ^ t1;
+ int t5 = t2 | t3;
+ X0 = t4 ^ t5;
+ int t7 = d & X0;
+ int t8 = t2 ^ X0;
+ X1 = t7 ^ t8;
+ int t10 = t1 | X0;
+ int t11 = t2 | t7;
+ int t12 = t3 ^ t10;
+ X2 = t11 ^ t12;
+ X3 = (b ^ t7) ^ (X1 & t12);
+ }
+
+ /**
+ * InvS5 - { 8,15, 2, 9, 4, 1,13,14,11, 6, 5, 3, 7,12,10, 0 } - 16 terms.
+ */
+ private void ib5(int a, int b, int c, int d)
+ {
+ int t1 = ~c;
+ int t2 = b & t1;
+ int t3 = d ^ t2;
+ int t4 = a & t3;
+ int t5 = b ^ t1;
+ X3 = t4 ^ t5;
+ int t7 = b | X3;
+ int t8 = a & t7;
+ X1 = t3 ^ t8;
+ int t10 = a | d;
+ int t11 = t1 ^ t7;
+ X0 = t10 ^ t11;
+ X2 = (b & t10) ^ (t4 | (a ^ c));
+ }
+
+ /**
+ * S6 - { 7, 2,12, 5, 8, 4, 6,11,14, 9, 1,15,13, 3,10, 0 } - 15 terms.
+ */
+ private void sb6(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = a ^ d;
+ int t3 = b ^ t2;
+ int t4 = t1 | t2;
+ int t5 = c ^ t4;
+ X1 = b ^ t5;
+ int t7 = t2 | X1;
+ int t8 = d ^ t7;
+ int t9 = t5 & t8;
+ X2 = t3 ^ t9;
+ int t11 = t5 ^ t8;
+ X0 = X2 ^ t11;
+ X3 = (~t5) ^ (t3 & t11);
+ }
+
+ /**
+ * InvS6 - {15,10, 1,13, 5, 3, 6, 0, 4, 9,14, 7, 2,12, 8,11 } - 15 terms.
+ */
+ private void ib6(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = a ^ b;
+ int t3 = c ^ t2;
+ int t4 = c | t1;
+ int t5 = d ^ t4;
+ X1 = t3 ^ t5;
+ int t7 = t3 & t5;
+ int t8 = t2 ^ t7;
+ int t9 = b | t8;
+ X3 = t5 ^ t9;
+ int t11 = b | X3;
+ X0 = t8 ^ t11;
+ X2 = (d & t1) ^ (t3 ^ t11);
+ }
+
+ /**
+ * S7 - { 1,13,15, 0,14, 8, 2,11, 7, 4,12,10, 9, 3, 5, 6 } - 16 terms.
+ */
+ private void sb7(int a, int b, int c, int d)
+ {
+ int t1 = b ^ c;
+ int t2 = c & t1;
+ int t3 = d ^ t2;
+ int t4 = a ^ t3;
+ int t5 = d | t1;
+ int t6 = t4 & t5;
+ X1 = b ^ t6;
+ int t8 = t3 | X1;
+ int t9 = a & t4;
+ X3 = t1 ^ t9;
+ int t11 = t4 ^ t8;
+ int t12 = X3 & t11;
+ X2 = t3 ^ t12;
+ X0 = (~t11) ^ (X3 & X2);
+ }
+
+ /**
+ * InvS7 - { 3, 0, 6,13, 9,14,15, 8, 5,12,11, 7,10, 1, 4, 2 } - 17 terms.
+ */
+ private void ib7(int a, int b, int c, int d)
+ {
+ int t3 = c | (a & b);
+ int t4 = d & (a | b);
+ X3 = t3 ^ t4;
+ int t6 = ~d;
+ int t7 = b ^ t4;
+ int t9 = t7 | (X3 ^ t6);
+ X1 = a ^ t9;
+ X0 = (c ^ t7) ^ (d | X1);
+ X2 = (t3 ^ X1) ^ (X0 ^ (a & X3));
+ }
+
+ /**
+ * Apply the linear transformation to the register set.
+ */
+ private void LT()
+ {
+ int x0 = rotateLeft(X0, 13);
+ int x2 = rotateLeft(X2, 3);
+ int x1 = X1 ^ x0 ^ x2 ;
+ int x3 = X3 ^ x2 ^ x0 << 3;
+
+ X1 = rotateLeft(x1, 1);
+ X3 = rotateLeft(x3, 7);
+ X0 = rotateLeft(x0 ^ X1 ^ X3, 5);
+ X2 = rotateLeft(x2 ^ X3 ^ (X1 << 7), 22);
+ }
+
+ /**
+ * Apply the inverse of the linear transformation to the register set.
+ */
+ private void inverseLT()
+ {
+ int x2 = rotateRight(X2, 22) ^ X3 ^ (X1 << 7);
+ int x0 = rotateRight(X0, 5) ^ X1 ^ X3;
+ int x3 = rotateRight(X3, 7);
+ int x1 = rotateRight(X1, 1);
+ X3 = x3 ^ x2 ^ x0 << 3;
+ X1 = x1 ^ x0 ^ x2;
+ X2 = rotateRight(x2, 3);
+ X0 = rotateRight(x0, 13);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/SkipjackEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/SkipjackEngine.java
new file mode 100644
index 00000000..1fac5362
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/SkipjackEngine.java
@@ -0,0 +1,260 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * a class that provides a basic SKIPJACK engine.
+ */
+public class SkipjackEngine
+ implements BlockCipher
+{
+ static final int BLOCK_SIZE = 8;
+
+ static short ftable[] =
+ {
+ 0xa3, 0xd7, 0x09, 0x83, 0xf8, 0x48, 0xf6, 0xf4, 0xb3, 0x21, 0x15, 0x78, 0x99, 0xb1, 0xaf, 0xf9,
+ 0xe7, 0x2d, 0x4d, 0x8a, 0xce, 0x4c, 0xca, 0x2e, 0x52, 0x95, 0xd9, 0x1e, 0x4e, 0x38, 0x44, 0x28,
+ 0x0a, 0xdf, 0x02, 0xa0, 0x17, 0xf1, 0x60, 0x68, 0x12, 0xb7, 0x7a, 0xc3, 0xe9, 0xfa, 0x3d, 0x53,
+ 0x96, 0x84, 0x6b, 0xba, 0xf2, 0x63, 0x9a, 0x19, 0x7c, 0xae, 0xe5, 0xf5, 0xf7, 0x16, 0x6a, 0xa2,
+ 0x39, 0xb6, 0x7b, 0x0f, 0xc1, 0x93, 0x81, 0x1b, 0xee, 0xb4, 0x1a, 0xea, 0xd0, 0x91, 0x2f, 0xb8,
+ 0x55, 0xb9, 0xda, 0x85, 0x3f, 0x41, 0xbf, 0xe0, 0x5a, 0x58, 0x80, 0x5f, 0x66, 0x0b, 0xd8, 0x90,
+ 0x35, 0xd5, 0xc0, 0xa7, 0x33, 0x06, 0x65, 0x69, 0x45, 0x00, 0x94, 0x56, 0x6d, 0x98, 0x9b, 0x76,
+ 0x97, 0xfc, 0xb2, 0xc2, 0xb0, 0xfe, 0xdb, 0x20, 0xe1, 0xeb, 0xd6, 0xe4, 0xdd, 0x47, 0x4a, 0x1d,
+ 0x42, 0xed, 0x9e, 0x6e, 0x49, 0x3c, 0xcd, 0x43, 0x27, 0xd2, 0x07, 0xd4, 0xde, 0xc7, 0x67, 0x18,
+ 0x89, 0xcb, 0x30, 0x1f, 0x8d, 0xc6, 0x8f, 0xaa, 0xc8, 0x74, 0xdc, 0xc9, 0x5d, 0x5c, 0x31, 0xa4,
+ 0x70, 0x88, 0x61, 0x2c, 0x9f, 0x0d, 0x2b, 0x87, 0x50, 0x82, 0x54, 0x64, 0x26, 0x7d, 0x03, 0x40,
+ 0x34, 0x4b, 0x1c, 0x73, 0xd1, 0xc4, 0xfd, 0x3b, 0xcc, 0xfb, 0x7f, 0xab, 0xe6, 0x3e, 0x5b, 0xa5,
+ 0xad, 0x04, 0x23, 0x9c, 0x14, 0x51, 0x22, 0xf0, 0x29, 0x79, 0x71, 0x7e, 0xff, 0x8c, 0x0e, 0xe2,
+ 0x0c, 0xef, 0xbc, 0x72, 0x75, 0x6f, 0x37, 0xa1, 0xec, 0xd3, 0x8e, 0x62, 0x8b, 0x86, 0x10, 0xe8,
+ 0x08, 0x77, 0x11, 0xbe, 0x92, 0x4f, 0x24, 0xc5, 0x32, 0x36, 0x9d, 0xcf, 0xf3, 0xa6, 0xbb, 0xac,
+ 0x5e, 0x6c, 0xa9, 0x13, 0x57, 0x25, 0xb5, 0xe3, 0xbd, 0xa8, 0x3a, 0x01, 0x05, 0x59, 0x2a, 0x46
+ };
+
+ private int[] key0, key1, key2, key3;
+ private boolean encrypting;
+
+ /**
+ * initialise a SKIPJACK cipher.
+ *
+ * @param encrypting whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("invalid parameter passed to SKIPJACK init - " + params.getClass().getName());
+ }
+
+ byte[] keyBytes = ((KeyParameter)params).getKey();
+
+ this.encrypting = encrypting;
+ this.key0 = new int[32];
+ this.key1 = new int[32];
+ this.key2 = new int[32];
+ this.key3 = new int[32];
+
+ //
+ // expand the key to 128 bytes in 4 parts (saving us a modulo, multiply
+ // and an addition).
+ //
+ for (int i = 0; i < 32; i ++)
+ {
+ key0[i] = keyBytes[(i * 4) % 10] & 0xff;
+ key1[i] = keyBytes[(i * 4 + 1) % 10] & 0xff;
+ key2[i] = keyBytes[(i * 4 + 2) % 10] & 0xff;
+ key3[i] = keyBytes[(i * 4 + 3) % 10] & 0xff;
+ }
+ }
+
+ public String getAlgorithmName()
+ {
+ return "SKIPJACK";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (key1 == null)
+ {
+ throw new IllegalStateException("SKIPJACK engine not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (encrypting)
+ {
+ encryptBlock(in, inOff, out, outOff);
+ }
+ else
+ {
+ decryptBlock(in, inOff, out, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+
+ /**
+ * The G permutation
+ */
+ private int g(
+ int k,
+ int w)
+ {
+ int g1, g2, g3, g4, g5, g6;
+
+ g1 = (w >> 8) & 0xff;
+ g2 = w & 0xff;
+
+ g3 = ftable[g2 ^ key0[k]] ^ g1;
+ g4 = ftable[g3 ^ key1[k]] ^ g2;
+ g5 = ftable[g4 ^ key2[k]] ^ g3;
+ g6 = ftable[g5 ^ key3[k]] ^ g4;
+
+ return ((g5 << 8) + g6);
+ }
+
+ public int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int w1 = (in[inOff + 0] << 8) + (in[inOff + 1] & 0xff);
+ int w2 = (in[inOff + 2] << 8) + (in[inOff + 3] & 0xff);
+ int w3 = (in[inOff + 4] << 8) + (in[inOff + 5] & 0xff);
+ int w4 = (in[inOff + 6] << 8) + (in[inOff + 7] & 0xff);
+
+ int k = 0;
+
+ for (int t = 0; t < 2; t++)
+ {
+ for(int i = 0; i < 8; i++)
+ {
+ int tmp = w4;
+ w4 = w3;
+ w3 = w2;
+ w2 = g(k, w1);
+ w1 = w2 ^ tmp ^ (k + 1);
+ k++;
+ }
+
+ for(int i = 0; i < 8; i++)
+ {
+ int tmp = w4;
+ w4 = w3;
+ w3 = w1 ^ w2 ^ (k + 1);
+ w2 = g(k, w1);
+ w1 = tmp;
+ k++;
+ }
+ }
+
+ out[outOff + 0] = (byte)((w1 >> 8));
+ out[outOff + 1] = (byte)(w1);
+ out[outOff + 2] = (byte)((w2 >> 8));
+ out[outOff + 3] = (byte)(w2);
+ out[outOff + 4] = (byte)((w3 >> 8));
+ out[outOff + 5] = (byte)(w3);
+ out[outOff + 6] = (byte)((w4 >> 8));
+ out[outOff + 7] = (byte)(w4);
+
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * the inverse of the G permutation.
+ */
+ private int h(
+ int k,
+ int w)
+ {
+ int h1, h2, h3, h4, h5, h6;
+
+ h1 = w & 0xff;
+ h2 = (w >> 8) & 0xff;
+
+ h3 = ftable[h2 ^ key3[k]] ^ h1;
+ h4 = ftable[h3 ^ key2[k]] ^ h2;
+ h5 = ftable[h4 ^ key1[k]] ^ h3;
+ h6 = ftable[h5 ^ key0[k]] ^ h4;
+
+ return ((h6 << 8) + h5);
+ }
+
+ public int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int w2 = (in[inOff + 0] << 8) + (in[inOff + 1] & 0xff);
+ int w1 = (in[inOff + 2] << 8) + (in[inOff + 3] & 0xff);
+ int w4 = (in[inOff + 4] << 8) + (in[inOff + 5] & 0xff);
+ int w3 = (in[inOff + 6] << 8) + (in[inOff + 7] & 0xff);
+
+ int k = 31;
+
+ for (int t = 0; t < 2; t++)
+ {
+ for(int i = 0; i < 8; i++)
+ {
+ int tmp = w4;
+ w4 = w3;
+ w3 = w2;
+ w2 = h(k, w1);
+ w1 = w2 ^ tmp ^ (k + 1);
+ k--;
+ }
+
+ for(int i = 0; i < 8; i++)
+ {
+ int tmp = w4;
+ w4 = w3;
+ w3 = w1 ^ w2 ^ (k + 1);
+ w2 = h(k, w1);
+ w1 = tmp;
+ k--;
+ }
+ }
+
+ out[outOff + 0] = (byte)((w2 >> 8));
+ out[outOff + 1] = (byte)(w2);
+ out[outOff + 2] = (byte)((w1 >> 8));
+ out[outOff + 3] = (byte)(w1);
+ out[outOff + 4] = (byte)((w4 >> 8));
+ out[outOff + 5] = (byte)(w4);
+ out[outOff + 6] = (byte)((w3 >> 8));
+ out[outOff + 7] = (byte)(w3);
+
+ return BLOCK_SIZE;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/TEAEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/TEAEngine.java
new file mode 100644
index 00000000..b09f1892
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/TEAEngine.java
@@ -0,0 +1,179 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * An TEA engine.
+ */
+public class TEAEngine
+ implements BlockCipher
+{
+ private static final int rounds = 32,
+ block_size = 8,
+// key_size = 16,
+ delta = 0x9E3779B9,
+ d_sum = 0xC6EF3720; // sum on decrypt
+ /*
+ * the expanded key array of 4 subkeys
+ */
+ private int _a, _b, _c, _d;
+ private boolean _initialised;
+ private boolean _forEncryption;
+
+ /**
+ * Create an instance of the TEA encryption algorithm
+ * and set some defaults
+ */
+ public TEAEngine()
+ {
+ _initialised = false;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "TEA";
+ }
+
+ public int getBlockSize()
+ {
+ return block_size;
+ }
+
+ /**
+ * initialise
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("invalid parameter passed to TEA init - " + params.getClass().getName());
+ }
+
+ _forEncryption = forEncryption;
+ _initialised = true;
+
+ KeyParameter p = (KeyParameter)params;
+
+ setKey(p.getKey());
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (!_initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName()+" not initialised");
+ }
+
+ if ((inOff + block_size) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + block_size) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ return (_forEncryption) ? encryptBlock(in, inOff, out, outOff)
+ : decryptBlock(in, inOff, out, outOff);
+ }
+
+ public void reset()
+ {
+ }
+
+ /**
+ * Re-key the cipher.
+ * <p>
+ * @param key the key to be used
+ */
+ private void setKey(
+ byte[] key)
+ {
+ _a = bytesToInt(key, 0);
+ _b = bytesToInt(key, 4);
+ _c = bytesToInt(key, 8);
+ _d = bytesToInt(key, 12);
+ }
+
+ private int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ // Pack bytes into integers
+ int v0 = bytesToInt(in, inOff);
+ int v1 = bytesToInt(in, inOff + 4);
+
+ int sum = 0;
+
+ for (int i = 0; i != rounds; i++)
+ {
+ sum += delta;
+ v0 += ((v1 << 4) + _a) ^ (v1 + sum) ^ ((v1 >>> 5) + _b);
+ v1 += ((v0 << 4) + _c) ^ (v0 + sum) ^ ((v0 >>> 5) + _d);
+ }
+
+ unpackInt(v0, out, outOff);
+ unpackInt(v1, out, outOff + 4);
+
+ return block_size;
+ }
+
+ private int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ // Pack bytes into integers
+ int v0 = bytesToInt(in, inOff);
+ int v1 = bytesToInt(in, inOff + 4);
+
+ int sum = d_sum;
+
+ for (int i = 0; i != rounds; i++)
+ {
+ v1 -= ((v0 << 4) + _c) ^ (v0 + sum) ^ ((v0 >>> 5) + _d);
+ v0 -= ((v1 << 4) + _a) ^ (v1 + sum) ^ ((v1 >>> 5) + _b);
+ sum -= delta;
+ }
+
+ unpackInt(v0, out, outOff);
+ unpackInt(v1, out, outOff + 4);
+
+ return block_size;
+ }
+
+ private int bytesToInt(byte[] in, int inOff)
+ {
+ return ((in[inOff++]) << 24) |
+ ((in[inOff++] & 255) << 16) |
+ ((in[inOff++] & 255) << 8) |
+ ((in[inOff] & 255));
+ }
+
+ private void unpackInt(int v, byte[] out, int outOff)
+ {
+ out[outOff++] = (byte)(v >>> 24);
+ out[outOff++] = (byte)(v >>> 16);
+ out[outOff++] = (byte)(v >>> 8);
+ out[outOff ] = (byte)v;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/TwofishEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/TwofishEngine.java
new file mode 100644
index 00000000..31ac0878
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/TwofishEngine.java
@@ -0,0 +1,680 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * A class that provides Twofish encryption operations.
+ *
+ * This Java implementation is based on the Java reference
+ * implementation provided by Bruce Schneier and developed
+ * by Raif S. Naffah.
+ */
+public final class TwofishEngine
+ implements BlockCipher
+{
+ private static final byte[][] P = {
+ { // p0
+ (byte) 0xA9, (byte) 0x67, (byte) 0xB3, (byte) 0xE8,
+ (byte) 0x04, (byte) 0xFD, (byte) 0xA3, (byte) 0x76,
+ (byte) 0x9A, (byte) 0x92, (byte) 0x80, (byte) 0x78,
+ (byte) 0xE4, (byte) 0xDD, (byte) 0xD1, (byte) 0x38,
+ (byte) 0x0D, (byte) 0xC6, (byte) 0x35, (byte) 0x98,
+ (byte) 0x18, (byte) 0xF7, (byte) 0xEC, (byte) 0x6C,
+ (byte) 0x43, (byte) 0x75, (byte) 0x37, (byte) 0x26,
+ (byte) 0xFA, (byte) 0x13, (byte) 0x94, (byte) 0x48,
+ (byte) 0xF2, (byte) 0xD0, (byte) 0x8B, (byte) 0x30,
+ (byte) 0x84, (byte) 0x54, (byte) 0xDF, (byte) 0x23,
+ (byte) 0x19, (byte) 0x5B, (byte) 0x3D, (byte) 0x59,
+ (byte) 0xF3, (byte) 0xAE, (byte) 0xA2, (byte) 0x82,
+ (byte) 0x63, (byte) 0x01, (byte) 0x83, (byte) 0x2E,
+ (byte) 0xD9, (byte) 0x51, (byte) 0x9B, (byte) 0x7C,
+ (byte) 0xA6, (byte) 0xEB, (byte) 0xA5, (byte) 0xBE,
+ (byte) 0x16, (byte) 0x0C, (byte) 0xE3, (byte) 0x61,
+ (byte) 0xC0, (byte) 0x8C, (byte) 0x3A, (byte) 0xF5,
+ (byte) 0x73, (byte) 0x2C, (byte) 0x25, (byte) 0x0B,
+ (byte) 0xBB, (byte) 0x4E, (byte) 0x89, (byte) 0x6B,
+ (byte) 0x53, (byte) 0x6A, (byte) 0xB4, (byte) 0xF1,
+ (byte) 0xE1, (byte) 0xE6, (byte) 0xBD, (byte) 0x45,
+ (byte) 0xE2, (byte) 0xF4, (byte) 0xB6, (byte) 0x66,
+ (byte) 0xCC, (byte) 0x95, (byte) 0x03, (byte) 0x56,
+ (byte) 0xD4, (byte) 0x1C, (byte) 0x1E, (byte) 0xD7,
+ (byte) 0xFB, (byte) 0xC3, (byte) 0x8E, (byte) 0xB5,
+ (byte) 0xE9, (byte) 0xCF, (byte) 0xBF, (byte) 0xBA,
+ (byte) 0xEA, (byte) 0x77, (byte) 0x39, (byte) 0xAF,
+ (byte) 0x33, (byte) 0xC9, (byte) 0x62, (byte) 0x71,
+ (byte) 0x81, (byte) 0x79, (byte) 0x09, (byte) 0xAD,
+ (byte) 0x24, (byte) 0xCD, (byte) 0xF9, (byte) 0xD8,
+ (byte) 0xE5, (byte) 0xC5, (byte) 0xB9, (byte) 0x4D,
+ (byte) 0x44, (byte) 0x08, (byte) 0x86, (byte) 0xE7,
+ (byte) 0xA1, (byte) 0x1D, (byte) 0xAA, (byte) 0xED,
+ (byte) 0x06, (byte) 0x70, (byte) 0xB2, (byte) 0xD2,
+ (byte) 0x41, (byte) 0x7B, (byte) 0xA0, (byte) 0x11,
+ (byte) 0x31, (byte) 0xC2, (byte) 0x27, (byte) 0x90,
+ (byte) 0x20, (byte) 0xF6, (byte) 0x60, (byte) 0xFF,
+ (byte) 0x96, (byte) 0x5C, (byte) 0xB1, (byte) 0xAB,
+ (byte) 0x9E, (byte) 0x9C, (byte) 0x52, (byte) 0x1B,
+ (byte) 0x5F, (byte) 0x93, (byte) 0x0A, (byte) 0xEF,
+ (byte) 0x91, (byte) 0x85, (byte) 0x49, (byte) 0xEE,
+ (byte) 0x2D, (byte) 0x4F, (byte) 0x8F, (byte) 0x3B,
+ (byte) 0x47, (byte) 0x87, (byte) 0x6D, (byte) 0x46,
+ (byte) 0xD6, (byte) 0x3E, (byte) 0x69, (byte) 0x64,
+ (byte) 0x2A, (byte) 0xCE, (byte) 0xCB, (byte) 0x2F,
+ (byte) 0xFC, (byte) 0x97, (byte) 0x05, (byte) 0x7A,
+ (byte) 0xAC, (byte) 0x7F, (byte) 0xD5, (byte) 0x1A,
+ (byte) 0x4B, (byte) 0x0E, (byte) 0xA7, (byte) 0x5A,
+ (byte) 0x28, (byte) 0x14, (byte) 0x3F, (byte) 0x29,
+ (byte) 0x88, (byte) 0x3C, (byte) 0x4C, (byte) 0x02,
+ (byte) 0xB8, (byte) 0xDA, (byte) 0xB0, (byte) 0x17,
+ (byte) 0x55, (byte) 0x1F, (byte) 0x8A, (byte) 0x7D,
+ (byte) 0x57, (byte) 0xC7, (byte) 0x8D, (byte) 0x74,
+ (byte) 0xB7, (byte) 0xC4, (byte) 0x9F, (byte) 0x72,
+ (byte) 0x7E, (byte) 0x15, (byte) 0x22, (byte) 0x12,
+ (byte) 0x58, (byte) 0x07, (byte) 0x99, (byte) 0x34,
+ (byte) 0x6E, (byte) 0x50, (byte) 0xDE, (byte) 0x68,
+ (byte) 0x65, (byte) 0xBC, (byte) 0xDB, (byte) 0xF8,
+ (byte) 0xC8, (byte) 0xA8, (byte) 0x2B, (byte) 0x40,
+ (byte) 0xDC, (byte) 0xFE, (byte) 0x32, (byte) 0xA4,
+ (byte) 0xCA, (byte) 0x10, (byte) 0x21, (byte) 0xF0,
+ (byte) 0xD3, (byte) 0x5D, (byte) 0x0F, (byte) 0x00,
+ (byte) 0x6F, (byte) 0x9D, (byte) 0x36, (byte) 0x42,
+ (byte) 0x4A, (byte) 0x5E, (byte) 0xC1, (byte) 0xE0 },
+ { // p1
+ (byte) 0x75, (byte) 0xF3, (byte) 0xC6, (byte) 0xF4,
+ (byte) 0xDB, (byte) 0x7B, (byte) 0xFB, (byte) 0xC8,
+ (byte) 0x4A, (byte) 0xD3, (byte) 0xE6, (byte) 0x6B,
+ (byte) 0x45, (byte) 0x7D, (byte) 0xE8, (byte) 0x4B,
+ (byte) 0xD6, (byte) 0x32, (byte) 0xD8, (byte) 0xFD,
+ (byte) 0x37, (byte) 0x71, (byte) 0xF1, (byte) 0xE1,
+ (byte) 0x30, (byte) 0x0F, (byte) 0xF8, (byte) 0x1B,
+ (byte) 0x87, (byte) 0xFA, (byte) 0x06, (byte) 0x3F,
+ (byte) 0x5E, (byte) 0xBA, (byte) 0xAE, (byte) 0x5B,
+ (byte) 0x8A, (byte) 0x00, (byte) 0xBC, (byte) 0x9D,
+ (byte) 0x6D, (byte) 0xC1, (byte) 0xB1, (byte) 0x0E,
+ (byte) 0x80, (byte) 0x5D, (byte) 0xD2, (byte) 0xD5,
+ (byte) 0xA0, (byte) 0x84, (byte) 0x07, (byte) 0x14,
+ (byte) 0xB5, (byte) 0x90, (byte) 0x2C, (byte) 0xA3,
+ (byte) 0xB2, (byte) 0x73, (byte) 0x4C, (byte) 0x54,
+ (byte) 0x92, (byte) 0x74, (byte) 0x36, (byte) 0x51,
+ (byte) 0x38, (byte) 0xB0, (byte) 0xBD, (byte) 0x5A,
+ (byte) 0xFC, (byte) 0x60, (byte) 0x62, (byte) 0x96,
+ (byte) 0x6C, (byte) 0x42, (byte) 0xF7, (byte) 0x10,
+ (byte) 0x7C, (byte) 0x28, (byte) 0x27, (byte) 0x8C,
+ (byte) 0x13, (byte) 0x95, (byte) 0x9C, (byte) 0xC7,
+ (byte) 0x24, (byte) 0x46, (byte) 0x3B, (byte) 0x70,
+ (byte) 0xCA, (byte) 0xE3, (byte) 0x85, (byte) 0xCB,
+ (byte) 0x11, (byte) 0xD0, (byte) 0x93, (byte) 0xB8,
+ (byte) 0xA6, (byte) 0x83, (byte) 0x20, (byte) 0xFF,
+ (byte) 0x9F, (byte) 0x77, (byte) 0xC3, (byte) 0xCC,
+ (byte) 0x03, (byte) 0x6F, (byte) 0x08, (byte) 0xBF,
+ (byte) 0x40, (byte) 0xE7, (byte) 0x2B, (byte) 0xE2,
+ (byte) 0x79, (byte) 0x0C, (byte) 0xAA, (byte) 0x82,
+ (byte) 0x41, (byte) 0x3A, (byte) 0xEA, (byte) 0xB9,
+ (byte) 0xE4, (byte) 0x9A, (byte) 0xA4, (byte) 0x97,
+ (byte) 0x7E, (byte) 0xDA, (byte) 0x7A, (byte) 0x17,
+ (byte) 0x66, (byte) 0x94, (byte) 0xA1, (byte) 0x1D,
+ (byte) 0x3D, (byte) 0xF0, (byte) 0xDE, (byte) 0xB3,
+ (byte) 0x0B, (byte) 0x72, (byte) 0xA7, (byte) 0x1C,
+ (byte) 0xEF, (byte) 0xD1, (byte) 0x53, (byte) 0x3E,
+ (byte) 0x8F, (byte) 0x33, (byte) 0x26, (byte) 0x5F,
+ (byte) 0xEC, (byte) 0x76, (byte) 0x2A, (byte) 0x49,
+ (byte) 0x81, (byte) 0x88, (byte) 0xEE, (byte) 0x21,
+ (byte) 0xC4, (byte) 0x1A, (byte) 0xEB, (byte) 0xD9,
+ (byte) 0xC5, (byte) 0x39, (byte) 0x99, (byte) 0xCD,
+ (byte) 0xAD, (byte) 0x31, (byte) 0x8B, (byte) 0x01,
+ (byte) 0x18, (byte) 0x23, (byte) 0xDD, (byte) 0x1F,
+ (byte) 0x4E, (byte) 0x2D, (byte) 0xF9, (byte) 0x48,
+ (byte) 0x4F, (byte) 0xF2, (byte) 0x65, (byte) 0x8E,
+ (byte) 0x78, (byte) 0x5C, (byte) 0x58, (byte) 0x19,
+ (byte) 0x8D, (byte) 0xE5, (byte) 0x98, (byte) 0x57,
+ (byte) 0x67, (byte) 0x7F, (byte) 0x05, (byte) 0x64,
+ (byte) 0xAF, (byte) 0x63, (byte) 0xB6, (byte) 0xFE,
+ (byte) 0xF5, (byte) 0xB7, (byte) 0x3C, (byte) 0xA5,
+ (byte) 0xCE, (byte) 0xE9, (byte) 0x68, (byte) 0x44,
+ (byte) 0xE0, (byte) 0x4D, (byte) 0x43, (byte) 0x69,
+ (byte) 0x29, (byte) 0x2E, (byte) 0xAC, (byte) 0x15,
+ (byte) 0x59, (byte) 0xA8, (byte) 0x0A, (byte) 0x9E,
+ (byte) 0x6E, (byte) 0x47, (byte) 0xDF, (byte) 0x34,
+ (byte) 0x35, (byte) 0x6A, (byte) 0xCF, (byte) 0xDC,
+ (byte) 0x22, (byte) 0xC9, (byte) 0xC0, (byte) 0x9B,
+ (byte) 0x89, (byte) 0xD4, (byte) 0xED, (byte) 0xAB,
+ (byte) 0x12, (byte) 0xA2, (byte) 0x0D, (byte) 0x52,
+ (byte) 0xBB, (byte) 0x02, (byte) 0x2F, (byte) 0xA9,
+ (byte) 0xD7, (byte) 0x61, (byte) 0x1E, (byte) 0xB4,
+ (byte) 0x50, (byte) 0x04, (byte) 0xF6, (byte) 0xC2,
+ (byte) 0x16, (byte) 0x25, (byte) 0x86, (byte) 0x56,
+ (byte) 0x55, (byte) 0x09, (byte) 0xBE, (byte) 0x91 }
+ };
+
+ /**
+ * Define the fixed p0/p1 permutations used in keyed S-box lookup.
+ * By changing the following constant definitions, the S-boxes will
+ * automatically get changed in the Twofish engine.
+ */
+ private static final int P_00 = 1;
+ private static final int P_01 = 0;
+ private static final int P_02 = 0;
+ private static final int P_03 = P_01 ^ 1;
+ private static final int P_04 = 1;
+
+ private static final int P_10 = 0;
+ private static final int P_11 = 0;
+ private static final int P_12 = 1;
+ private static final int P_13 = P_11 ^ 1;
+ private static final int P_14 = 0;
+
+ private static final int P_20 = 1;
+ private static final int P_21 = 1;
+ private static final int P_22 = 0;
+ private static final int P_23 = P_21 ^ 1;
+ private static final int P_24 = 0;
+
+ private static final int P_30 = 0;
+ private static final int P_31 = 1;
+ private static final int P_32 = 1;
+ private static final int P_33 = P_31 ^ 1;
+ private static final int P_34 = 1;
+
+ /* Primitive polynomial for GF(256) */
+ private static final int GF256_FDBK = 0x169;
+ private static final int GF256_FDBK_2 = GF256_FDBK / 2;
+ private static final int GF256_FDBK_4 = GF256_FDBK / 4;
+
+ private static final int RS_GF_FDBK = 0x14D; // field generator
+
+ //====================================
+ // Useful constants
+ //====================================
+
+ private static final int ROUNDS = 16;
+ private static final int MAX_ROUNDS = 16; // bytes = 128 bits
+ private static final int BLOCK_SIZE = 16; // bytes = 128 bits
+ private static final int MAX_KEY_BITS = 256;
+
+ private static final int INPUT_WHITEN=0;
+ private static final int OUTPUT_WHITEN=INPUT_WHITEN+BLOCK_SIZE/4; // 4
+ private static final int ROUND_SUBKEYS=OUTPUT_WHITEN+BLOCK_SIZE/4;// 8
+
+ private static final int TOTAL_SUBKEYS=ROUND_SUBKEYS+2*MAX_ROUNDS;// 40
+
+ private static final int SK_STEP = 0x02020202;
+ private static final int SK_BUMP = 0x01010101;
+ private static final int SK_ROTL = 9;
+
+ private boolean encrypting = false;
+
+ private int[] gMDS0 = new int[MAX_KEY_BITS];
+ private int[] gMDS1 = new int[MAX_KEY_BITS];
+ private int[] gMDS2 = new int[MAX_KEY_BITS];
+ private int[] gMDS3 = new int[MAX_KEY_BITS];
+
+ /**
+ * gSubKeys[] and gSBox[] are eventually used in the
+ * encryption and decryption methods.
+ */
+ private int[] gSubKeys;
+ private int[] gSBox;
+
+ private int k64Cnt = 0;
+
+ private byte[] workingKey = null;
+
+ public TwofishEngine()
+ {
+ // calculate the MDS matrix
+ int[] m1 = new int[2];
+ int[] mX = new int[2];
+ int[] mY = new int[2];
+ int j;
+
+ for (int i=0; i< MAX_KEY_BITS ; i++)
+ {
+ j = P[0][i] & 0xff;
+ m1[0] = j;
+ mX[0] = Mx_X(j) & 0xff;
+ mY[0] = Mx_Y(j) & 0xff;
+
+ j = P[1][i] & 0xff;
+ m1[1] = j;
+ mX[1] = Mx_X(j) & 0xff;
+ mY[1] = Mx_Y(j) & 0xff;
+
+ gMDS0[i] = m1[P_00] | mX[P_00] << 8 |
+ mY[P_00] << 16 | mY[P_00] << 24;
+
+ gMDS1[i] = mY[P_10] | mY[P_10] << 8 |
+ mX[P_10] << 16 | m1[P_10] << 24;
+
+ gMDS2[i] = mX[P_20] | mY[P_20] << 8 |
+ m1[P_20] << 16 | mY[P_20] << 24;
+
+ gMDS3[i] = mX[P_30] | m1[P_30] << 8 |
+ mY[P_30] << 16 | mX[P_30] << 24;
+ }
+ }
+
+ /**
+ * initialise a Twofish cipher.
+ *
+ * @param encrypting whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ this.encrypting = encrypting;
+ this.workingKey = ((KeyParameter)params).getKey();
+ this.k64Cnt = (this.workingKey.length / 8); // pre-padded ?
+ setKey(this.workingKey);
+
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to Twofish init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Twofish";
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new IllegalStateException("Twofish not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (encrypting)
+ {
+ encryptBlock(in, inOff, out, outOff);
+ }
+ else
+ {
+ decryptBlock(in, inOff, out, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ if (this.workingKey != null)
+ {
+ setKey(this.workingKey);
+ }
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ //==================================
+ // Private Implementation
+ //==================================
+
+ private void setKey(byte[] key)
+ {
+ int[] k32e = new int[MAX_KEY_BITS/64]; // 4
+ int[] k32o = new int[MAX_KEY_BITS/64]; // 4
+
+ int[] sBoxKeys = new int[MAX_KEY_BITS/64]; // 4
+ gSubKeys = new int[TOTAL_SUBKEYS];
+
+ if (k64Cnt < 1)
+ {
+ throw new IllegalArgumentException("Key size less than 64 bits");
+ }
+
+ if (k64Cnt > 4)
+ {
+ throw new IllegalArgumentException("Key size larger than 256 bits");
+ }
+
+ /*
+ * k64Cnt is the number of 8 byte blocks (64 chunks)
+ * that are in the input key. The input key is a
+ * maximum of 32 bytes (256 bits), so the range
+ * for k64Cnt is 1..4
+ */
+ for (int i=0; i<k64Cnt ; i++)
+ {
+ int p = i* 8;
+
+ k32e[i] = BytesTo32Bits(key, p);
+ k32o[i] = BytesTo32Bits(key, p+4);
+
+ sBoxKeys[k64Cnt-1-i] = RS_MDS_Encode(k32e[i], k32o[i]);
+ }
+
+ int q,A,B;
+ for (int i=0; i < TOTAL_SUBKEYS / 2 ; i++)
+ {
+ q = i*SK_STEP;
+ A = F32(q, k32e);
+ B = F32(q+SK_BUMP, k32o);
+ B = B << 8 | B >>> 24;
+ A += B;
+ gSubKeys[i*2] = A;
+ A += B;
+ gSubKeys[i*2 + 1] = A << SK_ROTL | A >>> (32-SK_ROTL);
+ }
+
+ /*
+ * fully expand the table for speed
+ */
+ int k0 = sBoxKeys[0];
+ int k1 = sBoxKeys[1];
+ int k2 = sBoxKeys[2];
+ int k3 = sBoxKeys[3];
+ int b0, b1, b2, b3;
+ gSBox = new int[4*MAX_KEY_BITS];
+ for (int i=0; i<MAX_KEY_BITS; i++)
+ {
+ b0 = b1 = b2 = b3 = i;
+ switch (k64Cnt & 3)
+ {
+ case 1:
+ gSBox[i*2] = gMDS0[(P[P_01][b0] & 0xff) ^ b0(k0)];
+ gSBox[i*2+1] = gMDS1[(P[P_11][b1] & 0xff) ^ b1(k0)];
+ gSBox[i*2+0x200] = gMDS2[(P[P_21][b2] & 0xff) ^ b2(k0)];
+ gSBox[i*2+0x201] = gMDS3[(P[P_31][b3] & 0xff) ^ b3(k0)];
+ break;
+ case 0: // 256 bits of key
+ b0 = (P[P_04][b0] & 0xff) ^ b0(k3);
+ b1 = (P[P_14][b1] & 0xff) ^ b1(k3);
+ b2 = (P[P_24][b2] & 0xff) ^ b2(k3);
+ b3 = (P[P_34][b3] & 0xff) ^ b3(k3);
+ // fall through, having pre-processed b[0]..b[3] with k32[3]
+ case 3: // 192 bits of key
+ b0 = (P[P_03][b0] & 0xff) ^ b0(k2);
+ b1 = (P[P_13][b1] & 0xff) ^ b1(k2);
+ b2 = (P[P_23][b2] & 0xff) ^ b2(k2);
+ b3 = (P[P_33][b3] & 0xff) ^ b3(k2);
+ // fall through, having pre-processed b[0]..b[3] with k32[2]
+ case 2: // 128 bits of key
+ gSBox[i*2] = gMDS0[(P[P_01]
+ [(P[P_02][b0] & 0xff) ^ b0(k1)] & 0xff) ^ b0(k0)];
+ gSBox[i*2+1] = gMDS1[(P[P_11]
+ [(P[P_12][b1] & 0xff) ^ b1(k1)] & 0xff) ^ b1(k0)];
+ gSBox[i*2+0x200] = gMDS2[(P[P_21]
+ [(P[P_22][b2] & 0xff) ^ b2(k1)] & 0xff) ^ b2(k0)];
+ gSBox[i*2+0x201] = gMDS3[(P[P_31]
+ [(P[P_32][b3] & 0xff) ^ b3(k1)] & 0xff) ^ b3(k0)];
+ break;
+ }
+ }
+
+ /*
+ * the function exits having setup the gSBox with the
+ * input key material.
+ */
+ }
+
+ /**
+ * Encrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ * The input will be an exact multiple of our blocksize.
+ *
+ * encryptBlock uses the pre-calculated gSBox[] and subKey[]
+ * arrays.
+ */
+ private void encryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+ int x0 = BytesTo32Bits(src, srcIndex) ^ gSubKeys[INPUT_WHITEN];
+ int x1 = BytesTo32Bits(src, srcIndex + 4) ^ gSubKeys[INPUT_WHITEN + 1];
+ int x2 = BytesTo32Bits(src, srcIndex + 8) ^ gSubKeys[INPUT_WHITEN + 2];
+ int x3 = BytesTo32Bits(src, srcIndex + 12) ^ gSubKeys[INPUT_WHITEN + 3];
+
+ int k = ROUND_SUBKEYS;
+ int t0, t1;
+ for (int r = 0; r < ROUNDS; r +=2)
+ {
+ t0 = Fe32_0(x0);
+ t1 = Fe32_3(x1);
+ x2 ^= t0 + t1 + gSubKeys[k++];
+ x2 = x2 >>>1 | x2 << 31;
+ x3 = (x3 << 1 | x3 >>> 31) ^ (t0 + 2*t1 + gSubKeys[k++]);
+
+ t0 = Fe32_0(x2);
+ t1 = Fe32_3(x3);
+ x0 ^= t0 + t1 + gSubKeys[k++];
+ x0 = x0 >>>1 | x0 << 31;
+ x1 = (x1 << 1 | x1 >>> 31) ^ (t0 + 2*t1 + gSubKeys[k++]);
+ }
+
+ Bits32ToBytes(x2 ^ gSubKeys[OUTPUT_WHITEN], dst, dstIndex);
+ Bits32ToBytes(x3 ^ gSubKeys[OUTPUT_WHITEN + 1], dst, dstIndex + 4);
+ Bits32ToBytes(x0 ^ gSubKeys[OUTPUT_WHITEN + 2], dst, dstIndex + 8);
+ Bits32ToBytes(x1 ^ gSubKeys[OUTPUT_WHITEN + 3], dst, dstIndex + 12);
+ }
+
+ /**
+ * Decrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ * The input will be an exact multiple of our blocksize.
+ */
+ private void decryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+ int x2 = BytesTo32Bits(src, srcIndex) ^ gSubKeys[OUTPUT_WHITEN];
+ int x3 = BytesTo32Bits(src, srcIndex+4) ^ gSubKeys[OUTPUT_WHITEN + 1];
+ int x0 = BytesTo32Bits(src, srcIndex+8) ^ gSubKeys[OUTPUT_WHITEN + 2];
+ int x1 = BytesTo32Bits(src, srcIndex+12) ^ gSubKeys[OUTPUT_WHITEN + 3];
+
+ int k = ROUND_SUBKEYS + 2 * ROUNDS -1 ;
+ int t0, t1;
+ for (int r = 0; r< ROUNDS ; r +=2)
+ {
+ t0 = Fe32_0(x2);
+ t1 = Fe32_3(x3);
+ x1 ^= t0 + 2*t1 + gSubKeys[k--];
+ x0 = (x0 << 1 | x0 >>> 31) ^ (t0 + t1 + gSubKeys[k--]);
+ x1 = x1 >>>1 | x1 << 31;
+
+ t0 = Fe32_0(x0);
+ t1 = Fe32_3(x1);
+ x3 ^= t0 + 2*t1 + gSubKeys[k--];
+ x2 = (x2 << 1 | x2 >>> 31) ^ (t0 + t1 + gSubKeys[k--]);
+ x3 = x3 >>>1 | x3 << 31;
+ }
+
+ Bits32ToBytes(x0 ^ gSubKeys[INPUT_WHITEN], dst, dstIndex);
+ Bits32ToBytes(x1 ^ gSubKeys[INPUT_WHITEN + 1], dst, dstIndex + 4);
+ Bits32ToBytes(x2 ^ gSubKeys[INPUT_WHITEN + 2], dst, dstIndex + 8);
+ Bits32ToBytes(x3 ^ gSubKeys[INPUT_WHITEN + 3], dst, dstIndex + 12);
+ }
+
+ /*
+ * TODO: This can be optimised and made cleaner by combining
+ * the functionality in this function and applying it appropriately
+ * to the creation of the subkeys during key setup.
+ */
+ private int F32(int x, int[] k32)
+ {
+ int b0 = b0(x);
+ int b1 = b1(x);
+ int b2 = b2(x);
+ int b3 = b3(x);
+ int k0 = k32[0];
+ int k1 = k32[1];
+ int k2 = k32[2];
+ int k3 = k32[3];
+
+ int result = 0;
+ switch (k64Cnt & 3)
+ {
+ case 1:
+ result = gMDS0[(P[P_01][b0] & 0xff) ^ b0(k0)] ^
+ gMDS1[(P[P_11][b1] & 0xff) ^ b1(k0)] ^
+ gMDS2[(P[P_21][b2] & 0xff) ^ b2(k0)] ^
+ gMDS3[(P[P_31][b3] & 0xff) ^ b3(k0)];
+ break;
+ case 0: /* 256 bits of key */
+ b0 = (P[P_04][b0] & 0xff) ^ b0(k3);
+ b1 = (P[P_14][b1] & 0xff) ^ b1(k3);
+ b2 = (P[P_24][b2] & 0xff) ^ b2(k3);
+ b3 = (P[P_34][b3] & 0xff) ^ b3(k3);
+ case 3:
+ b0 = (P[P_03][b0] & 0xff) ^ b0(k2);
+ b1 = (P[P_13][b1] & 0xff) ^ b1(k2);
+ b2 = (P[P_23][b2] & 0xff) ^ b2(k2);
+ b3 = (P[P_33][b3] & 0xff) ^ b3(k2);
+ case 2:
+ result =
+ gMDS0[(P[P_01][(P[P_02][b0]&0xff)^b0(k1)]&0xff)^b0(k0)] ^
+ gMDS1[(P[P_11][(P[P_12][b1]&0xff)^b1(k1)]&0xff)^b1(k0)] ^
+ gMDS2[(P[P_21][(P[P_22][b2]&0xff)^b2(k1)]&0xff)^b2(k0)] ^
+ gMDS3[(P[P_31][(P[P_32][b3]&0xff)^b3(k1)]&0xff)^b3(k0)];
+ break;
+ }
+ return result;
+ }
+
+ /**
+ * Use (12, 8) Reed-Solomon code over GF(256) to produce
+ * a key S-box 32-bit entity from 2 key material 32-bit
+ * entities.
+ *
+ * @param k0 first 32-bit entity
+ * @param k1 second 32-bit entity
+ * @return Remainder polynomial generated using RS code
+ */
+ private int RS_MDS_Encode(int k0, int k1)
+ {
+ int r = k1;
+ for (int i = 0 ; i < 4 ; i++) // shift 1 byte at a time
+ {
+ r = RS_rem(r);
+ }
+ r ^= k0;
+ for (int i=0 ; i < 4 ; i++)
+ {
+ r = RS_rem(r);
+ }
+
+ return r;
+ }
+
+ /**
+ * Reed-Solomon code parameters: (12,8) reversible code:<p>
+ * <pre>
+ * g(x) = x^4 + (a+1/a)x^3 + ax^2 + (a+1/a)x + 1
+ * </pre>
+ * where a = primitive root of field generator 0x14D
+ */
+ private int RS_rem(int x)
+ {
+ int b = (x >>> 24) & 0xff;
+ int g2 = ((b << 1) ^
+ ((b & 0x80) != 0 ? RS_GF_FDBK : 0)) & 0xff;
+ int g3 = ((b >>> 1) ^
+ ((b & 0x01) != 0 ? (RS_GF_FDBK >>> 1) : 0)) ^ g2 ;
+ return ((x << 8) ^ (g3 << 24) ^ (g2 << 16) ^ (g3 << 8) ^ b);
+ }
+
+ private int LFSR1(int x)
+ {
+ return (x >> 1) ^
+ (((x & 0x01) != 0) ? GF256_FDBK_2 : 0);
+ }
+
+ private int LFSR2(int x)
+ {
+ return (x >> 2) ^
+ (((x & 0x02) != 0) ? GF256_FDBK_2 : 0) ^
+ (((x & 0x01) != 0) ? GF256_FDBK_4 : 0);
+ }
+
+ private int Mx_X(int x)
+ {
+ return x ^ LFSR2(x);
+ } // 5B
+
+ private int Mx_Y(int x)
+ {
+ return x ^ LFSR1(x) ^ LFSR2(x);
+ } // EF
+
+ private int b0(int x)
+ {
+ return x & 0xff;
+ }
+
+ private int b1(int x)
+ {
+ return (x >>> 8) & 0xff;
+ }
+
+ private int b2(int x)
+ {
+ return (x >>> 16) & 0xff;
+ }
+
+ private int b3(int x)
+ {
+ return (x >>> 24) & 0xff;
+ }
+
+ private int Fe32_0(int x)
+ {
+ return gSBox[ 0x000 + 2*(x & 0xff) ] ^
+ gSBox[ 0x001 + 2*((x >>> 8) & 0xff) ] ^
+ gSBox[ 0x200 + 2*((x >>> 16) & 0xff) ] ^
+ gSBox[ 0x201 + 2*((x >>> 24) & 0xff) ];
+ }
+
+ private int Fe32_3(int x)
+ {
+ return gSBox[ 0x000 + 2*((x >>> 24) & 0xff) ] ^
+ gSBox[ 0x001 + 2*(x & 0xff) ] ^
+ gSBox[ 0x200 + 2*((x >>> 8) & 0xff) ] ^
+ gSBox[ 0x201 + 2*((x >>> 16) & 0xff) ];
+ }
+
+ private int BytesTo32Bits(byte[] b, int p)
+ {
+ return ((b[p] & 0xff)) |
+ ((b[p+1] & 0xff) << 8) |
+ ((b[p+2] & 0xff) << 16) |
+ ((b[p+3] & 0xff) << 24);
+ }
+
+ private void Bits32ToBytes(int in, byte[] b, int offset)
+ {
+ b[offset] = (byte)in;
+ b[offset + 1] = (byte)(in >> 8);
+ b[offset + 2] = (byte)(in >> 16);
+ b[offset + 3] = (byte)(in >> 24);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/VMPCEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/VMPCEngine.java
new file mode 100644
index 00000000..0703fd68
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/VMPCEngine.java
@@ -0,0 +1,139 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+
+public class VMPCEngine implements StreamCipher
+{
+ /*
+ * variables to hold the state of the VMPC engine during encryption and
+ * decryption
+ */
+ protected byte n = 0;
+ protected byte[] P = null;
+ protected byte s = 0;
+
+ protected byte[] workingIV;
+ protected byte[] workingKey;
+
+ public String getAlgorithmName()
+ {
+ return "VMPC";
+ }
+
+ /**
+ * initialise a VMPC cipher.
+ *
+ * @param forEncryption
+ * whether or not we are for encryption.
+ * @param params
+ * the parameters required to set up the cipher.
+ * @exception IllegalArgumentException
+ * if the params argument is inappropriate.
+ */
+ public void init(boolean forEncryption, CipherParameters params)
+ {
+ if (!(params instanceof ParametersWithIV))
+ {
+ throw new IllegalArgumentException(
+ "VMPC init parameters must include an IV");
+ }
+
+ ParametersWithIV ivParams = (ParametersWithIV) params;
+ KeyParameter key = (KeyParameter) ivParams.getParameters();
+
+ if (!(ivParams.getParameters() instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException(
+ "VMPC init parameters must include a key");
+ }
+
+ this.workingIV = ivParams.getIV();
+
+ if (workingIV == null || workingIV.length < 1 || workingIV.length > 768)
+ {
+ throw new IllegalArgumentException("VMPC requires 1 to 768 bytes of IV");
+ }
+
+ this.workingKey = key.getKey();
+
+ initKey(this.workingKey, this.workingIV);
+ }
+
+ protected void initKey(byte[] keyBytes, byte[] ivBytes)
+ {
+ s = 0;
+ P = new byte[256];
+ for (int i = 0; i < 256; i++)
+ {
+ P[i] = (byte) i;
+ }
+
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.length]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + ivBytes[m % ivBytes.length]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+ n = 0;
+ }
+
+ public void processBytes(byte[] in, int inOff, int len, byte[] out,
+ int outOff)
+ {
+ if ((inOff + len) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + len) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ for (int i = 0; i < len; i++)
+ {
+ s = P[(s + P[n & 0xff]) & 0xff];
+ byte z = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff];
+ // encryption
+ byte temp = P[n & 0xff];
+ P[n & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ n = (byte) ((n + 1) & 0xff);
+
+ // xor
+ out[i + outOff] = (byte) (in[i + inOff] ^ z);
+ }
+ }
+
+ public void reset()
+ {
+ initKey(this.workingKey, this.workingIV);
+ }
+
+ public byte returnByte(byte in)
+ {
+ s = P[(s + P[n & 0xff]) & 0xff];
+ byte z = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff];
+ // encryption
+ byte temp = P[n & 0xff];
+ P[n & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ n = (byte) ((n + 1) & 0xff);
+
+ // xor
+ return (byte) (in ^ z);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/VMPCKSA3Engine.java b/core/src/main/java/org/bouncycastle/crypto/engines/VMPCKSA3Engine.java
new file mode 100644
index 00000000..9e40272b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/VMPCKSA3Engine.java
@@ -0,0 +1,45 @@
+package org.bouncycastle.crypto.engines;
+
+public class VMPCKSA3Engine extends VMPCEngine
+{
+ public String getAlgorithmName()
+ {
+ return "VMPC-KSA3";
+ }
+
+ protected void initKey(byte[] keyBytes, byte[] ivBytes)
+ {
+ s = 0;
+ P = new byte[256];
+ for (int i = 0; i < 256; i++)
+ {
+ P[i] = (byte) i;
+ }
+
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.length]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + ivBytes[m % ivBytes.length]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.length]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+
+ n = 0;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XTEAEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XTEAEngine.java
new file mode 100644
index 00000000..f037da4d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/XTEAEngine.java
@@ -0,0 +1,183 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * An XTEA engine.
+ */
+public class XTEAEngine
+ implements BlockCipher
+{
+ private static final int rounds = 32,
+ block_size = 8,
+// key_size = 16,
+ delta = 0x9E3779B9;
+
+ /*
+ * the expanded key array of 4 subkeys
+ */
+ private int[] _S = new int[4],
+ _sum0 = new int[32],
+ _sum1 = new int[32];
+ private boolean _initialised,
+ _forEncryption;
+
+ /**
+ * Create an instance of the TEA encryption algorithm
+ * and set some defaults
+ */
+ public XTEAEngine()
+ {
+ _initialised = false;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "XTEA";
+ }
+
+ public int getBlockSize()
+ {
+ return block_size;
+ }
+
+ /**
+ * initialise
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("invalid parameter passed to TEA init - " + params.getClass().getName());
+ }
+
+ _forEncryption = forEncryption;
+ _initialised = true;
+
+ KeyParameter p = (KeyParameter)params;
+
+ setKey(p.getKey());
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (!_initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName()+" not initialised");
+ }
+
+ if ((inOff + block_size) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + block_size) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ return (_forEncryption) ? encryptBlock(in, inOff, out, outOff)
+ : decryptBlock(in, inOff, out, outOff);
+ }
+
+ public void reset()
+ {
+ }
+
+ /**
+ * Re-key the cipher.
+ * <p>
+ * @param key the key to be used
+ */
+ private void setKey(
+ byte[] key)
+ {
+ int i, j;
+ for (i = j = 0; i < 4; i++,j+=4)
+ {
+ _S[i] = bytesToInt(key, j);
+ }
+
+ for (i = j = 0; i < rounds; i++)
+ {
+ _sum0[i] = (j + _S[j & 3]);
+ j += delta;
+ _sum1[i] = (j + _S[j >>> 11 & 3]);
+ }
+ }
+
+ private int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ // Pack bytes into integers
+ int v0 = bytesToInt(in, inOff);
+ int v1 = bytesToInt(in, inOff + 4);
+
+ for (int i = 0; i < rounds; i++)
+ {
+ v0 += ((v1 << 4 ^ v1 >>> 5) + v1) ^ _sum0[i];
+ v1 += ((v0 << 4 ^ v0 >>> 5) + v0) ^ _sum1[i];
+ }
+
+ unpackInt(v0, out, outOff);
+ unpackInt(v1, out, outOff + 4);
+
+ return block_size;
+ }
+
+ private int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ // Pack bytes into integers
+ int v0 = bytesToInt(in, inOff);
+ int v1 = bytesToInt(in, inOff + 4);
+
+ for (int i = rounds-1; i >= 0; i--)
+ {
+ v1 -= ((v0 << 4 ^ v0 >>> 5) + v0) ^ _sum1[i];
+ v0 -= ((v1 << 4 ^ v1 >>> 5) + v1) ^ _sum0[i];
+ }
+
+ unpackInt(v0, out, outOff);
+ unpackInt(v1, out, outOff + 4);
+
+ return block_size;
+ }
+
+ private int bytesToInt(byte[] in, int inOff)
+ {
+ return ((in[inOff++]) << 24) |
+ ((in[inOff++] & 255) << 16) |
+ ((in[inOff++] & 255) << 8) |
+ ((in[inOff] & 255));
+ }
+
+ private void unpackInt(int v, byte[] out, int outOff)
+ {
+ out[outOff++] = (byte)(v >>> 24);
+ out[outOff++] = (byte)(v >>> 16);
+ out[outOff++] = (byte)(v >>> 8);
+ out[outOff ] = (byte)v;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/examples/DESExample.java b/core/src/main/java/org/bouncycastle/crypto/examples/DESExample.java
new file mode 100644
index 00000000..16989971
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/examples/DESExample.java
@@ -0,0 +1,419 @@
+package org.bouncycastle.crypto.examples;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.engines.DESedeEngine;
+import org.bouncycastle.crypto.generators.DESedeKeyGenerator;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
+import org.bouncycastle.crypto.params.DESedeParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+
+/**
+ * DESExample is a simple DES based encryptor/decryptor.
+ * <p>
+ * The program is command line driven, with the input
+ * and output files specified on the command line.
+ * <pre>
+ * java org.bouncycastle.crypto.examples.DESExample infile outfile [keyfile]
+ * </pre>
+ * A new key is generated for each encryption, if key is not specified,
+ * then the example will assume encryption is required, and as output
+ * create deskey.dat in the current directory. This key is a hex
+ * encoded byte-stream that is used for the decryption. The output
+ * file is Hex encoded, 60 characters wide text file.
+ * <p>
+ * When encrypting;
+ * <ul>
+ * <li>the infile is expected to be a byte stream (text or binary)
+ * <li>there is no keyfile specified on the input line
+ * </ul>
+ * <p>
+ * When decrypting;
+ * <li>the infile is expected to be the 60 character wide base64
+ * encoded file
+ * <li>the keyfile is expected to be a base64 encoded file
+ * <p>
+ * This example shows how to use the light-weight API, DES and
+ * the filesystem for message encryption and decryption.
+ *
+ */
+public class DESExample extends Object
+{
+ // Encrypting or decrypting ?
+ private boolean encrypt = true;
+
+ // To hold the initialised DESede cipher
+ private PaddedBufferedBlockCipher cipher = null;
+
+ // The input stream of bytes to be processed for encryption
+ private BufferedInputStream in = null;
+
+ // The output stream of bytes to be procssed
+ private BufferedOutputStream out = null;
+
+ // The key
+ private byte[] key = null;
+
+ /*
+ * start the application
+ */
+ public static void main(String[] args)
+ {
+ boolean encrypt = true;
+ String infile = null;
+ String outfile = null;
+ String keyfile = null;
+
+ if (args.length < 2)
+ {
+ DESExample de = new DESExample();
+ System.err.println("Usage: java "+de.getClass().getName()+
+ " infile outfile [keyfile]");
+ System.exit(1);
+ }
+
+ keyfile = "deskey.dat";
+ infile = args[0];
+ outfile = args[1];
+
+ if (args.length > 2)
+ {
+ encrypt = false;
+ keyfile = args[2];
+ }
+
+ DESExample de = new DESExample(infile, outfile, keyfile, encrypt);
+ de.process();
+ }
+
+ // Default constructor, used for the usage message
+ public DESExample()
+ {
+ }
+
+ /*
+ * Constructor, that takes the arguments appropriate for
+ * processing the command line directives.
+ */
+ public DESExample(
+ String infile,
+ String outfile,
+ String keyfile,
+ boolean encrypt)
+ {
+ /*
+ * First, determine that infile & keyfile exist as appropriate.
+ *
+ * This will also create the BufferedInputStream as required
+ * for reading the input file. All input files are treated
+ * as if they are binary, even if they contain text, it's the
+ * bytes that are encrypted.
+ */
+ this.encrypt = encrypt;
+ try
+ {
+ in = new BufferedInputStream(new FileInputStream(infile));
+ }
+ catch (FileNotFoundException fnf)
+ {
+ System.err.println("Input file not found ["+infile+"]");
+ System.exit(1);
+ }
+
+ try
+ {
+ out = new BufferedOutputStream(new FileOutputStream(outfile));
+ }
+ catch (IOException fnf)
+ {
+ System.err.println("Output file not created ["+outfile+"]");
+ System.exit(1);
+ }
+
+ if (encrypt)
+ {
+ try
+ {
+ /*
+ * The process of creating a new key requires a
+ * number of steps.
+ *
+ * First, create the parameters for the key generator
+ * which are a secure random number generator, and
+ * the length of the key (in bits).
+ */
+ SecureRandom sr = null;
+ try
+ {
+ sr = new SecureRandom();
+ /*
+ * This following call to setSeed() makes the
+ * initialisation of the SecureRandom object
+ * _very_ fast, but not secure AT ALL.
+ *
+ * Remove the line, recreate the class file and
+ * then run DESExample again to see the difference.
+ *
+ * The initialisation of a SecureRandom object
+ * can take 5 or more seconds depending on the
+ * CPU that the program is running on. That can
+ * be annoying during unit testing.
+ * -- jon
+ */
+ sr.setSeed("www.bouncycastle.org".getBytes());
+ }
+ catch (Exception nsa)
+ {
+ System.err.println("Hmmm, no SHA1PRNG, you need the "+
+ "Sun implementation");
+ System.exit(1);
+ }
+ KeyGenerationParameters kgp = new KeyGenerationParameters(
+ sr,
+ DESedeParameters.DES_EDE_KEY_LENGTH*8);
+
+ /*
+ * Second, initialise the key generator with the parameters
+ */
+ DESedeKeyGenerator kg = new DESedeKeyGenerator();
+ kg.init(kgp);
+
+ /*
+ * Third, and finally, generate the key
+ */
+ key = kg.generateKey();
+
+ /*
+ * We can now output the key to the file, but first
+ * hex encode the key so that we can have a look
+ * at it with a text editor if we so desire
+ */
+ BufferedOutputStream keystream =
+ new BufferedOutputStream(new FileOutputStream(keyfile));
+ byte[] keyhex = Hex.encode(key);
+ keystream.write(keyhex, 0, keyhex.length);
+ keystream.flush();
+ keystream.close();
+ }
+ catch (IOException createKey)
+ {
+ System.err.println("Could not decryption create key file "+
+ "["+keyfile+"]");
+ System.exit(1);
+ }
+ }
+ else
+ {
+ try
+ {
+ // read the key, and decode from hex encoding
+ BufferedInputStream keystream =
+ new BufferedInputStream(new FileInputStream(keyfile));
+ int len = keystream.available();
+ byte[] keyhex = new byte[len];
+ keystream.read(keyhex, 0, len);
+ key = Hex.decode(keyhex);
+ }
+ catch (IOException ioe)
+ {
+ System.err.println("Decryption key file not found, "+
+ "or not valid ["+keyfile+"]");
+ System.exit(1);
+ }
+ }
+ }
+
+ private void process()
+ {
+ /*
+ * Setup the DESede cipher engine, create a PaddedBufferedBlockCipher
+ * in CBC mode.
+ */
+ cipher = new PaddedBufferedBlockCipher(
+ new CBCBlockCipher(new DESedeEngine()));
+
+ /*
+ * The input and output streams are currently set up
+ * appropriately, and the key bytes are ready to be
+ * used.
+ *
+ */
+
+ if (encrypt)
+ {
+ performEncrypt(key);
+ }
+ else
+ {
+ performDecrypt(key);
+ }
+
+ // after processing clean up the files
+ try
+ {
+ in.close();
+ out.flush();
+ out.close();
+ }
+ catch (IOException closing)
+ {
+
+ }
+ }
+
+ /*
+ * This method performs all the encryption and writes
+ * the cipher text to the buffered output stream created
+ * previously.
+ */
+ private void performEncrypt(byte[] key)
+ {
+ // initialise the cipher with the key bytes, for encryption
+ cipher.init(true, new KeyParameter(key));
+
+ /*
+ * Create some temporary byte arrays for use in
+ * encryption, make them a reasonable size so that
+ * we don't spend forever reading small chunks from
+ * a file.
+ *
+ * There is no particular reason for using getBlockSize()
+ * to determine the size of the input chunk. It just
+ * was a convenient number for the example.
+ */
+ // int inBlockSize = cipher.getBlockSize() * 5;
+ int inBlockSize = 47;
+ int outBlockSize = cipher.getOutputSize(inBlockSize);
+
+ byte[] inblock = new byte[inBlockSize];
+ byte[] outblock = new byte[outBlockSize];
+
+ /*
+ * now, read the file, and output the chunks
+ */
+ try
+ {
+ int inL;
+ int outL;
+ byte[] rv = null;
+ while ((inL=in.read(inblock, 0, inBlockSize)) > 0)
+ {
+ outL = cipher.processBytes(inblock, 0, inL, outblock, 0);
+ /*
+ * Before we write anything out, we need to make sure
+ * that we've got something to write out.
+ */
+ if (outL > 0)
+ {
+ rv = Hex.encode(outblock, 0, outL);
+ out.write(rv, 0, rv.length);
+ out.write('\n');
+ }
+ }
+
+ try
+ {
+ /*
+ * Now, process the bytes that are still buffered
+ * within the cipher.
+ */
+ outL = cipher.doFinal(outblock, 0);
+ if (outL > 0)
+ {
+ rv = Hex.encode(outblock, 0, outL);
+ out.write(rv, 0, rv.length);
+ out.write('\n');
+ }
+ }
+ catch (CryptoException ce)
+ {
+
+ }
+ }
+ catch (IOException ioeread)
+ {
+ ioeread.printStackTrace();
+ }
+ }
+
+ /*
+ * This method performs all the decryption and writes
+ * the plain text to the buffered output stream created
+ * previously.
+ */
+ private void performDecrypt(byte[] key)
+ {
+ // initialise the cipher for decryption
+ cipher.init(false, new KeyParameter(key));
+
+ /*
+ * As the decryption is from our preformatted file,
+ * and we know that it's a hex encoded format, then
+ * we wrap the InputStream with a BufferedReader
+ * so that we can read it easily.
+ */
+ BufferedReader br = new BufferedReader(new InputStreamReader(in));
+
+ /*
+ * now, read the file, and output the chunks
+ */
+ try
+ {
+ int outL;
+ byte[] inblock = null;
+ byte[] outblock = null;
+ String rv = null;
+ while ((rv = br.readLine()) != null)
+ {
+ inblock = Hex.decode(rv);
+ outblock = new byte[cipher.getOutputSize(inblock.length)];
+
+ outL = cipher.processBytes(inblock, 0, inblock.length,
+ outblock, 0);
+ /*
+ * Before we write anything out, we need to make sure
+ * that we've got something to write out.
+ */
+ if (outL > 0)
+ {
+ out.write(outblock, 0, outL);
+ }
+ }
+
+ try
+ {
+ /*
+ * Now, process the bytes that are still buffered
+ * within the cipher.
+ */
+ outL = cipher.doFinal(outblock, 0);
+ if (outL > 0)
+ {
+ out.write(outblock, 0, outL);
+ }
+ }
+ catch (CryptoException ce)
+ {
+
+ }
+ }
+ catch (IOException ioeread)
+ {
+ ioeread.printStackTrace();
+ }
+ }
+
+}
+
diff --git a/core/src/main/java/org/bouncycastle/crypto/examples/JPAKEExample.java b/core/src/main/java/org/bouncycastle/crypto/examples/JPAKEExample.java
new file mode 100644
index 00000000..f0065f4c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/examples/JPAKEExample.java
@@ -0,0 +1,214 @@
+package org.bouncycastle.crypto.examples;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.agreement.jpake.JPAKEPrimeOrderGroup;
+import org.bouncycastle.crypto.agreement.jpake.JPAKEPrimeOrderGroups;
+import org.bouncycastle.crypto.agreement.jpake.JPAKEParticipant;
+import org.bouncycastle.crypto.agreement.jpake.JPAKERound1Payload;
+import org.bouncycastle.crypto.agreement.jpake.JPAKERound2Payload;
+import org.bouncycastle.crypto.agreement.jpake.JPAKERound3Payload;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+
+/**
+ * An example of a J-PAKE exchange.
+ * <p>
+ *
+ * In this example, both Alice and Bob are on the same computer (in the same JVM, in fact).
+ * In reality, Alice and Bob would be in different locations,
+ * and would be sending their generated payloads to each other.
+ */
+public class JPAKEExample
+{
+
+ public static void main(String args[]) throws CryptoException
+ {
+ /*
+ * Initialization
+ *
+ * Pick an appropriate prime order group to use throughout the exchange.
+ * Note that both participants must use the same group.
+ */
+ JPAKEPrimeOrderGroup group = JPAKEPrimeOrderGroups.NIST_3072;
+
+ BigInteger p = group.getP();
+ BigInteger q = group.getQ();
+ BigInteger g = group.getG();
+
+ String alicePassword = "password";
+ String bobPassword = "password";
+
+ System.out.println("********* Initialization **********");
+ System.out.println("Public parameters for the cyclic group:");
+ System.out.println("p (" + p.bitLength() + " bits): " + p.toString(16));
+ System.out.println("q (" + q.bitLength() + " bits): " + q.toString(16));
+ System.out.println("g (" + p.bitLength() + " bits): " + g.toString(16));
+ System.out.println("p mod q = " + p.mod(q).toString(16));
+ System.out.println("g^{q} mod p = " + g.modPow(q, p).toString(16));
+ System.out.println("");
+
+ System.out.println("(Secret passwords used by Alice and Bob: " +
+ "\"" + alicePassword + "\" and \"" + bobPassword + "\")\n");
+
+ /*
+ * Both participants must use the same hashing algorithm.
+ */
+ Digest digest = new SHA256Digest();
+ SecureRandom random = new SecureRandom();
+
+ JPAKEParticipant alice = new JPAKEParticipant("alice", alicePassword.toCharArray(), group, digest, random);
+ JPAKEParticipant bob = new JPAKEParticipant("bob", bobPassword.toCharArray(), group, digest, random);
+
+ /*
+ * Round 1
+ *
+ * Alice and Bob each generate a round 1 payload, and send it to each other.
+ */
+
+ JPAKERound1Payload aliceRound1Payload = alice.createRound1PayloadToSend();
+ JPAKERound1Payload bobRound1Payload = bob.createRound1PayloadToSend();
+
+ System.out.println("************ Round 1 **************");
+ System.out.println("Alice sends to Bob: ");
+ System.out.println("g^{x1}=" + aliceRound1Payload.getGx1().toString(16));
+ System.out.println("g^{x2}=" + aliceRound1Payload.getGx2().toString(16));
+ System.out.println("KP{x1}={" + aliceRound1Payload.getKnowledgeProofForX1()[0].toString(16) + "};{" + aliceRound1Payload.getKnowledgeProofForX1()[1].toString(16) + "}");
+ System.out.println("KP{x2}={" + aliceRound1Payload.getKnowledgeProofForX2()[0].toString(16) + "};{" + aliceRound1Payload.getKnowledgeProofForX2()[1].toString(16) + "}");
+ System.out.println("");
+
+ System.out.println("Bob sends to Alice: ");
+ System.out.println("g^{x3}=" + bobRound1Payload.getGx1().toString(16));
+ System.out.println("g^{x4}=" + bobRound1Payload.getGx2().toString(16));
+ System.out.println("KP{x3}={" + bobRound1Payload.getKnowledgeProofForX1()[0].toString(16) + "};{" + bobRound1Payload.getKnowledgeProofForX1()[1].toString(16) + "}");
+ System.out.println("KP{x4}={" + bobRound1Payload.getKnowledgeProofForX2()[0].toString(16) + "};{" + bobRound1Payload.getKnowledgeProofForX2()[1].toString(16) + "}");
+ System.out.println("");
+
+ /*
+ * Each participant must then validate the received payload for round 1
+ */
+
+ alice.validateRound1PayloadReceived(bobRound1Payload);
+ System.out.println("Alice checks g^{x4}!=1: OK");
+ System.out.println("Alice checks KP{x3}: OK");
+ System.out.println("Alice checks KP{x4}: OK");
+ System.out.println("");
+
+ bob.validateRound1PayloadReceived(aliceRound1Payload);
+ System.out.println("Bob checks g^{x2}!=1: OK");
+ System.out.println("Bob checks KP{x1},: OK");
+ System.out.println("Bob checks KP{x2},: OK");
+ System.out.println("");
+
+ /*
+ * Round 2
+ *
+ * Alice and Bob each generate a round 2 payload, and send it to each other.
+ */
+
+ JPAKERound2Payload aliceRound2Payload = alice.createRound2PayloadToSend();
+ JPAKERound2Payload bobRound2Payload = bob.createRound2PayloadToSend();
+
+ System.out.println("************ Round 2 **************");
+ System.out.println("Alice sends to Bob: ");
+ System.out.println("A=" + aliceRound2Payload.getA().toString(16));
+ System.out.println("KP{x2*s}={" + aliceRound2Payload.getKnowledgeProofForX2s()[0].toString(16) + "},{" + aliceRound2Payload.getKnowledgeProofForX2s()[1].toString(16) + "}");
+ System.out.println("");
+
+ System.out.println("Bob sends to Alice");
+ System.out.println("B=" + bobRound2Payload.getA().toString(16));
+ System.out.println("KP{x4*s}={" + bobRound2Payload.getKnowledgeProofForX2s()[0].toString(16) + "},{" + bobRound2Payload.getKnowledgeProofForX2s()[1].toString(16) + "}");
+ System.out.println("");
+
+ /*
+ * Each participant must then validate the received payload for round 2
+ */
+
+ alice.validateRound2PayloadReceived(bobRound2Payload);
+ System.out.println("Alice checks KP{x4*s}: OK\n");
+
+ bob.validateRound2PayloadReceived(aliceRound2Payload);
+ System.out.println("Bob checks KP{x2*s}: OK\n");
+
+ /*
+ * After round 2, each participant computes the keying material.
+ */
+
+ BigInteger aliceKeyingMaterial = alice.calculateKeyingMaterial();
+ BigInteger bobKeyingMaterial = bob.calculateKeyingMaterial();
+
+ System.out.println("********* After round 2 ***********");
+ System.out.println("Alice computes key material \t K=" + aliceKeyingMaterial.toString(16));
+ System.out.println("Bob computes key material \t K=" + bobKeyingMaterial.toString(16));
+ System.out.println();
+
+
+ /*
+ * You must derive a session key from the keying material applicable
+ * to whatever encryption algorithm you want to use.
+ */
+
+ BigInteger aliceKey = deriveSessionKey(aliceKeyingMaterial);
+ BigInteger bobKey = deriveSessionKey(bobKeyingMaterial);
+
+ /*
+ * At this point, you can stop and use the session keys if you want.
+ * This is implicit key confirmation.
+ *
+ * If you want to explicitly confirm that the key material matches,
+ * you can continue on and perform round 3.
+ */
+
+ /*
+ * Round 3
+ *
+ * Alice and Bob each generate a round 3 payload, and send it to each other.
+ */
+
+ JPAKERound3Payload aliceRound3Payload = alice.createRound3PayloadToSend(aliceKeyingMaterial);
+ JPAKERound3Payload bobRound3Payload = bob.createRound3PayloadToSend(bobKeyingMaterial);
+
+ System.out.println("************ Round 3 **************");
+ System.out.println("Alice sends to Bob: ");
+ System.out.println("MacTag=" + aliceRound3Payload.getMacTag().toString(16));
+ System.out.println("");
+ System.out.println("Bob sends to Alice: ");
+ System.out.println("MacTag=" + bobRound3Payload.getMacTag().toString(16));
+ System.out.println("");
+
+ /*
+ * Each participant must then validate the received payload for round 3
+ */
+
+ alice.validateRound3PayloadReceived(bobRound3Payload, aliceKeyingMaterial);
+ System.out.println("Alice checks MacTag: OK\n");
+
+ bob.validateRound3PayloadReceived(aliceRound3Payload, bobKeyingMaterial);
+ System.out.println("Bob checks MacTag: OK\n");
+
+ System.out.println();
+ System.out.println("MacTags validated, therefore the keying material matches.");
+ }
+
+ private static BigInteger deriveSessionKey(BigInteger keyingMaterial)
+ {
+ /*
+ * You should use a secure key derivation function (KDF) to derive the session key.
+ *
+ * For the purposes of this example, I'm just going to use a hash of the keying material.
+ */
+ SHA256Digest digest = new SHA256Digest();
+
+ byte[] keyByteArray = keyingMaterial.toByteArray();
+
+ byte[] output = new byte[digest.getDigestSize()];
+
+ digest.update(keyByteArray, 0, keyByteArray.length);
+
+ digest.doFinal(output, 0);
+
+ return new BigInteger(output);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java
new file mode 100644
index 00000000..2ef8dd2b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java
@@ -0,0 +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;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/DESKeyGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/DESKeyGenerator.java
new file mode 100644
index 00000000..7111118b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/DESKeyGenerator.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.params.DESParameters;
+
+public class DESKeyGenerator
+ extends CipherKeyGenerator
+{
+ /**
+ * initialise the key generator - if strength is set to zero
+ * the key generated will be 64 bits in size, otherwise
+ * strength can be 64 or 56 bits (if you don't count the parity bits).
+ *
+ * @param param the parameters to be used for key generation
+ */
+ public void init(
+ KeyGenerationParameters param)
+ {
+ super.init(param);
+
+ if (strength == 0 || strength == (56 / 8))
+ {
+ strength = DESParameters.DES_KEY_LENGTH;
+ }
+ else if (strength != DESParameters.DES_KEY_LENGTH)
+ {
+ throw new IllegalArgumentException("DES key must be "
+ + (DESParameters.DES_KEY_LENGTH * 8)
+ + " bits long.");
+ }
+ }
+
+ public byte[] generateKey()
+ {
+ byte[] newKey = new byte[DESParameters.DES_KEY_LENGTH];
+
+ do
+ {
+ random.nextBytes(newKey);
+
+ DESParameters.setOddParity(newKey);
+ }
+ while (DESParameters.isWeakKey(newKey, 0));
+
+ return newKey;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java
new file mode 100644
index 00000000..3cab983d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java
@@ -0,0 +1,56 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.params.DESedeParameters;
+
+public class DESedeKeyGenerator
+ extends DESKeyGenerator
+{
+ /**
+ * initialise the key generator - if strength is set to zero
+ * the key generated will be 192 bits in size, otherwise
+ * strength can be 128 or 192 (or 112 or 168 if you don't count
+ * parity bits), depending on whether you wish to do 2-key or 3-key
+ * triple DES.
+ *
+ * @param param the parameters to be used for key generation
+ */
+ public void init(
+ KeyGenerationParameters param)
+ {
+ this.random = param.getRandom();
+ this.strength = (param.getStrength() + 7) / 8;
+
+ if (strength == 0 || strength == (168 / 8))
+ {
+ strength = DESedeParameters.DES_EDE_KEY_LENGTH;
+ }
+ else if (strength == (112 / 8))
+ {
+ strength = 2 * DESedeParameters.DES_KEY_LENGTH;
+ }
+ else if (strength != DESedeParameters.DES_EDE_KEY_LENGTH
+ && strength != (2 * DESedeParameters.DES_KEY_LENGTH))
+ {
+ throw new IllegalArgumentException("DESede key must be "
+ + (DESedeParameters.DES_EDE_KEY_LENGTH * 8) + " or "
+ + (2 * 8 * DESedeParameters.DES_KEY_LENGTH)
+ + " bits long.");
+ }
+ }
+
+ public byte[] generateKey()
+ {
+ byte[] newKey = new byte[strength];
+
+ do
+ {
+ random.nextBytes(newKey);
+
+ DESedeParameters.setOddParity(newKey);
+ }
+ while (DESedeParameters.isWeakKey(newKey, 0, newKey.length));
+
+ return newKey;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java
new file mode 100644
index 00000000..f93428ef
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java
@@ -0,0 +1,42 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.params.DHKeyGenerationParameters;
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
+import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+
+import java.math.BigInteger;
+
+/**
+ * a basic Diffie-Hellman key pair generator.
+ *
+ * This generates keys consistent for use with the basic algorithm for
+ * Diffie-Hellman.
+ */
+public class DHBasicKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+ private DHKeyGenerationParameters param;
+
+ public void init(
+ KeyGenerationParameters param)
+ {
+ this.param = (DHKeyGenerationParameters)param;
+ }
+
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ DHKeyGeneratorHelper helper = DHKeyGeneratorHelper.INSTANCE;
+ DHParameters dhp = param.getParameters();
+
+ BigInteger x = helper.calculatePrivate(dhp, param.getRandom());
+ BigInteger y = helper.calculatePublic(dhp, x);
+
+ return new AsymmetricCipherKeyPair(
+ new DHPublicKeyParameters(y, dhp),
+ new DHPrivateKeyParameters(x, dhp));
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/DHKeyGeneratorHelper.java b/core/src/main/java/org/bouncycastle/crypto/generators/DHKeyGeneratorHelper.java
new file mode 100644
index 00000000..e0d86fc3
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/DHKeyGeneratorHelper.java
@@ -0,0 +1,51 @@
+package org.bouncycastle.crypto.generators;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.util.BigIntegers;
+
+class DHKeyGeneratorHelper
+{
+ static final DHKeyGeneratorHelper INSTANCE = new DHKeyGeneratorHelper();
+
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+ private static final BigInteger TWO = BigInteger.valueOf(2);
+
+ private DHKeyGeneratorHelper()
+ {
+ }
+
+ BigInteger calculatePrivate(DHParameters dhParams, SecureRandom random)
+ {
+ BigInteger p = dhParams.getP();
+ int limit = dhParams.getL();
+
+ if (limit != 0)
+ {
+ return new BigInteger(limit, random).setBit(limit - 1);
+ }
+
+ BigInteger min = TWO;
+ int m = dhParams.getM();
+ if (m != 0)
+ {
+ min = ONE.shiftLeft(m - 1);
+ }
+
+ BigInteger max = p.subtract(TWO);
+ BigInteger q = dhParams.getQ();
+ if (q != null)
+ {
+ max = q.subtract(TWO);
+ }
+
+ return BigIntegers.createRandomInRange(min, max, random);
+ }
+
+ BigInteger calculatePublic(DHParameters dhParams, BigInteger x)
+ {
+ return dhParams.getG().modPow(x, dhParams.getP());
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/DHKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/DHKeyPairGenerator.java
new file mode 100644
index 00000000..d07ca80d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/DHKeyPairGenerator.java
@@ -0,0 +1,42 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.params.DHKeyGenerationParameters;
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
+import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+
+import java.math.BigInteger;
+
+/**
+ * a Diffie-Hellman key pair generator.
+ *
+ * This generates keys consistent for use in the MTI/A0 key agreement protocol
+ * as described in "Handbook of Applied Cryptography", Pages 516-519.
+ */
+public class DHKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+ private DHKeyGenerationParameters param;
+
+ public void init(
+ KeyGenerationParameters param)
+ {
+ this.param = (DHKeyGenerationParameters)param;
+ }
+
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ DHKeyGeneratorHelper helper = DHKeyGeneratorHelper.INSTANCE;
+ DHParameters dhp = param.getParameters();
+
+ BigInteger x = helper.calculatePrivate(dhp, param.getRandom());
+ BigInteger y = helper.calculatePublic(dhp, x);
+
+ return new AsymmetricCipherKeyPair(
+ new DHPublicKeyParameters(y, dhp),
+ new DHPrivateKeyParameters(x, dhp));
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/DHParametersGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/DHParametersGenerator.java
new file mode 100644
index 00000000..f5d42642
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/DHParametersGenerator.java
@@ -0,0 +1,52 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.params.DHParameters;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+public class DHParametersGenerator
+{
+ private int size;
+ private int certainty;
+ private SecureRandom random;
+
+ private static final BigInteger TWO = BigInteger.valueOf(2);
+
+ /**
+ * Initialise the parameters generator.
+ *
+ * @param size bit length for the prime p
+ * @param certainty level of certainty for the prime number tests
+ * @param random a source of randomness
+ */
+ public void init(
+ int size,
+ int certainty,
+ SecureRandom random)
+ {
+ this.size = size;
+ this.certainty = certainty;
+ this.random = random;
+ }
+
+ /**
+ * which generates the p and g values from the given parameters,
+ * returning the DHParameters object.
+ * <p>
+ * Note: can take a while...
+ */
+ public DHParameters generateParameters()
+ {
+ //
+ // find a safe prime p where p = 2*q + 1, where p and q are prime.
+ //
+ BigInteger[] safePrimes = DHParametersHelper.generateSafePrimes(size, certainty, random);
+
+ BigInteger p = safePrimes[0];
+ BigInteger q = safePrimes[1];
+ BigInteger g = DHParametersHelper.selectGenerator(p, q, random);
+
+ return new DHParameters(p, g, q, TWO, null);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/DHParametersHelper.java b/core/src/main/java/org/bouncycastle/crypto/generators/DHParametersHelper.java
new file mode 100644
index 00000000..118bc9cc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/DHParametersHelper.java
@@ -0,0 +1,73 @@
+package org.bouncycastle.crypto.generators;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.util.BigIntegers;
+
+class DHParametersHelper
+{
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+ private static final BigInteger TWO = BigInteger.valueOf(2);
+
+ /*
+ * Finds a pair of prime BigInteger's {p, q: p = 2q + 1}
+ *
+ * (see: Handbook of Applied Cryptography 4.86)
+ */
+ static BigInteger[] generateSafePrimes(int size, int certainty, SecureRandom random)
+ {
+ BigInteger p, q;
+ int qLength = size - 1;
+
+ for (;;)
+ {
+ q = new BigInteger(qLength, 2, random);
+
+ // p <- 2q + 1
+ p = q.shiftLeft(1).add(ONE);
+
+ if (p.isProbablePrime(certainty) && (certainty <= 2 || q.isProbablePrime(certainty)))
+ {
+ break;
+ }
+ }
+
+ return new BigInteger[] { p, q };
+ }
+
+ /*
+ * Select a high order element of the multiplicative group Zp*
+ *
+ * p and q must be s.t. p = 2*q + 1, where p and q are prime (see generateSafePrimes)
+ */
+ static BigInteger selectGenerator(BigInteger p, BigInteger q, SecureRandom random)
+ {
+ BigInteger pMinusTwo = p.subtract(TWO);
+ BigInteger g;
+
+ /*
+ * (see: Handbook of Applied Cryptography 4.80)
+ */
+// do
+// {
+// g = BigIntegers.createRandomInRange(TWO, pMinusTwo, random);
+// }
+// while (g.modPow(TWO, p).equals(ONE) || g.modPow(q, p).equals(ONE));
+
+
+ /*
+ * RFC 2631 2.2.1.2 (and see: Handbook of Applied Cryptography 4.81)
+ */
+ do
+ {
+ BigInteger h = BigIntegers.createRandomInRange(TWO, pMinusTwo, random);
+
+ g = h.modPow(TWO, p);
+ }
+ while (g.equals(ONE));
+
+
+ return g;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java
new file mode 100644
index 00000000..93f49cfa
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java
@@ -0,0 +1,61 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.params.DSAKeyGenerationParameters;
+import org.bouncycastle.crypto.params.DSAParameters;
+import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
+import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
+import org.bouncycastle.util.BigIntegers;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/**
+ * a DSA key pair generator.
+ *
+ * This generates DSA keys in line with the method described
+ * in <i>FIPS 186-3 B.1 FFC Key Pair Generation</i>.
+ */
+public class DSAKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ private DSAKeyGenerationParameters param;
+
+ public void init(
+ KeyGenerationParameters param)
+ {
+ this.param = (DSAKeyGenerationParameters)param;
+ }
+
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ DSAParameters dsaParams = param.getParameters();
+
+ BigInteger x = generatePrivateKey(dsaParams.getQ(), param.getRandom());
+ BigInteger y = calculatePublicKey(dsaParams.getP(), dsaParams.getG(), x);
+
+ return new AsymmetricCipherKeyPair(
+ new DSAPublicKeyParameters(y, dsaParams),
+ new DSAPrivateKeyParameters(x, dsaParams));
+ }
+
+ private static BigInteger generatePrivateKey(BigInteger q, SecureRandom random)
+ {
+ // TODO Prefer this method? (change test cases that used fixed random)
+ // B.1.1 Key Pair Generation Using Extra Random Bits
+// BigInteger c = new BigInteger(q.bitLength() + 64, random);
+// return c.mod(q.subtract(ONE)).add(ONE);
+
+ // B.1.2 Key Pair Generation by Testing Candidates
+ return BigIntegers.createRandomInRange(ONE, q.subtract(ONE), random);
+ }
+
+ private static BigInteger calculatePublicKey(BigInteger p, BigInteger g, BigInteger x)
+ {
+ return g.modPow(x, p);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java
new file mode 100644
index 00000000..749b0cc5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java
@@ -0,0 +1,387 @@
+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.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;
+
+/**
+ * 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;
+
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+ 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.
+ *
+ * @param size size of the key (range 2^512 -> 2^1024 - 64 bit increments)
+ * @param certainty measure of robustness of prime (for FIPS 186-2 compliance this should be at least 80).
+ * @param random random byte source.
+ */
+ public void init(
+ int size,
+ int certainty,
+ SecureRandom random)
+ {
+ this.use186_3 = false;
+ this.L = size;
+ this.N = getDefaultN(size);
+ this.certainty = certainty;
+ this.random = 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 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");
+ }
+
+ if (digest.getDigestSize() * 8 < N)
+ {
+ throw new IllegalStateException("Digest output size too small for value of N");
+ }
+ }
+
+ /**
+ * which generates the p and g values from the given parameters,
+ * returning the DSAParameters object.
+ * <p>
+ * Note: can take a while...
+ */
+ public DSAParameters generateParameters()
+ {
+ return (use186_3)
+ ? generateParameters_FIPS186_3()
+ : generateParameters_FIPS186_2();
+ }
+
+ private DSAParameters generateParameters_FIPS186_2()
+ {
+ byte[] seed = new byte[20];
+ byte[] part1 = new byte[20];
+ byte[] part2 = new byte[20];
+ byte[] u = new byte[20];
+ 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(digest, seed, part1);
+ System.arraycopy(seed, 0, part2, 0, seed.length);
+ inc(part2);
+ hash(digest, part2, part2);
+
+ for (int i = 0; i != u.length; i++)
+ {
+ u[i] = (byte)(part1[i] ^ part2[i]);
+ }
+
+ u[0] |= (byte)0x80;
+ u[19] |= (byte)0x01;
+
+ BigInteger q = new BigInteger(1, u);
+
+ if (!q.isProbablePrime(certainty))
+ {
+ continue;
+ }
+
+ byte[] offset = Arrays.clone(seed);
+ inc(offset);
+
+ for (int counter = 0; counter < 4096; ++counter)
+ {
+ for (int k = 0; k < n; k++)
+ {
+ inc(offset);
+ hash(digest, offset, part1);
+ System.arraycopy(part1, 0, w, w.length - (k + 1) * part1.length, part1.length);
+ }
+
+ inc(offset);
+ 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;
+
+ BigInteger x = new BigInteger(1, w);
+
+ BigInteger c = x.mod(q.shiftLeft(1));
+
+ BigInteger p = x.subtract(c.subtract(ONE));
+
+ if (p.bitLength() != L)
+ {
+ continue;
+ }
+
+ if (p.isProbablePrime(certainty))
+ {
+ BigInteger g = calculateGenerator_FIPS186_2(p, q, random);
+
+ return new DSAParameters(p, q, g, new DSAValidationParameters(seed, counter));
+ }
+ }
+ }
+ }
+
+ private static BigInteger calculateGenerator_FIPS186_2(BigInteger p, BigInteger q, SecureRandom r)
+ {
+ BigInteger e = p.subtract(ONE).divide(q);
+ BigInteger pSub2 = p.subtract(TWO);
+
+ for (;;)
+ {
+ BigInteger h = BigIntegers.createRandomInRange(TWO, pSub2, r);
+ BigInteger g = h.modPow(e, p);
+ if (g.bitLength() > 1)
+ {
+ return g;
+ }
+ }
+ }
+
+ /**
+ * generate suitable parameters for DSA, in line with
+ * <i>FIPS 186-3 A.1 Generation of the FFC Primes p and q</i>.
+ */
+ private DSAParameters generateParameters_FIPS186_3()
+ {
+// 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 = 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
+// the pair is not in the list, then return INVALID.
+ // Note: checked at initialisation
+
+// 2. If (seedlen < N), then return INVALID.
+ // FIXME This should be configurable (must be >= N)
+ int seedlen = N;
+ byte[] seed = new byte[seedlen / 8];
+
+// 3. n = ceiling(L ⁄ outlen) – 1.
+ int n = (L - 1) / outlen;
+
+// 4. b = L – 1 – (n ∗ outlen).
+ int b = (L - 1) % outlen;
+
+ byte[] output = new byte[d.getDigestSize()];
+ for (;;)
+ {
+// 5. Get an arbitrary sequence of seedlen bits as the domain_parameter_seed.
+ random.nextBytes(seed);
+
+// 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).
+ BigInteger q = ONE.shiftLeft(N - 1).add(U).add(ONE).subtract(U.mod(TWO));
+
+// 8. Test whether or not q is prime as specified in Appendix C.3.
+ // TODO Review C.3 for primality checking
+ if (!q.isProbablePrime(certainty))
+ {
+// 9. If q is not a prime, then go to step 5.
+ continue;
+ }
+
+// 10. offset = 1.
+ // Note: 'offset' value managed incrementally
+ byte[] offset = Arrays.clone(seed);
+
+// 11. For counter = 0 to (4L – 1) do
+ int counterLimit = 4 * L;
+ for (int counter = 0; counter < counterLimit; ++counter)
+ {
+// 11.1 For j = 0 to n do
+// Vj = Hash ((domain_parameter_seed + offset + j) mod 2^seedlen).
+// 11.2 W = V0 + (V1 ∗ 2^outlen) + ... + (V^(n–1) ∗ 2^((n–1) ∗ outlen)) + ((Vn mod 2^b) ∗ 2^(n ∗ outlen)).
+ // TODO Assemble w as a byte array
+ BigInteger W = ZERO;
+ for (int j = 0, exp = 0; j <= n; ++j, exp += outlen)
+ {
+ inc(offset);
+ hash(d, offset, output);
+
+ BigInteger Vj = new BigInteger(1, output);
+ if (j == n)
+ {
+ Vj = Vj.mod(ONE.shiftLeft(b));
+ }
+
+ W = W.add(Vj.shiftLeft(exp));
+ }
+
+// 11.3 X = W + 2^(L–1). Comment: 0 ≤ W < 2L–1; hence, 2L–1 ≤ X < 2L.
+ BigInteger X = W.add(ONE.shiftLeft(L - 1));
+
+// 11.4 c = X mod 2q.
+ BigInteger c = X.mod(q.shiftLeft(1));
+
+// 11.5 p = X - (c - 1). Comment: p ≡ 1 (mod 2q).
+ BigInteger p = X.subtract(c.subtract(ONE));
+
+// 11.6 If (p < 2^(L - 1)), then go to step 11.9
+ if (p.bitLength() != L)
+ {
+ continue;
+ }
+
+// 11.7 Test whether or not p is prime as specified in Appendix C.3.
+ // TODO Review C.3 for primality checking
+ if (p.isProbablePrime(certainty))
+ {
+// 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.
+ 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));
+ }
+
+// 11.9 offset = offset + n + 1. Comment: Increment offset; then, as part of
+// the loop in step 11, increment counter; if
+// counter < 4L, repeat steps 11.1 through 11.8.
+ // Note: 'offset' value already incremented in inner loop
+ }
+// 12. Go to step 5.
+ }
+ }
+
+ private static BigInteger calculateGenerator_FIPS186_3_Unverifiable(BigInteger p, BigInteger q,
+ SecureRandom r)
+ {
+ 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 void hash(Digest d, byte[] input, byte[] output)
+ {
+ d.update(input, 0, input.length);
+ d.doFinal(output, 0);
+ }
+
+ private static int getDefaultN(int L)
+ {
+ return L > 1024 ? 256 : 160;
+ }
+
+ private static void inc(byte[] buf)
+ {
+ for (int i = buf.length - 1; i >= 0; --i)
+ {
+ byte b = (byte)((buf[i] + 1) & 0xff);
+ buf[i] = b;
+
+ if (b != 0)
+ {
+ break;
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/DSTU4145KeyPairGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/DSTU4145KeyPairGenerator.java
new file mode 100644
index 00000000..3f931b2c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/DSTU4145KeyPairGenerator.java
@@ -0,0 +1,21 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+
+public class DSTU4145KeyPairGenerator
+ extends ECKeyPairGenerator
+{
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ AsymmetricCipherKeyPair pair = super.generateKeyPair();
+
+ ECPublicKeyParameters pub = (ECPublicKeyParameters)pair.getPublic();
+ ECPrivateKeyParameters priv = (ECPrivateKeyParameters)pair.getPrivate();
+
+ pub = new ECPublicKeyParameters(pub.getQ().negate(), pub.getParameters());
+
+ return new AsymmetricCipherKeyPair(pub, priv);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
new file mode 100644
index 00000000..d77bd747
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
@@ -0,0 +1,53 @@
+package org.bouncycastle.crypto.generators;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECPoint;
+
+public class ECKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator, ECConstants
+{
+ ECDomainParameters params;
+ SecureRandom random;
+
+ public void init(
+ KeyGenerationParameters param)
+ {
+ ECKeyGenerationParameters ecP = (ECKeyGenerationParameters)param;
+
+ this.random = ecP.getRandom();
+ this.params = ecP.getDomainParameters();
+ }
+
+ /**
+ * Given the domain parameters this routine generates an EC key
+ * pair in accordance with X9.62 section 5.2.1 pages 26, 27.
+ */
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ BigInteger n = params.getN();
+ int nBitLength = n.bitLength();
+ BigInteger d;
+
+ do
+ {
+ d = new BigInteger(nBitLength, random);
+ }
+ while (d.equals(ZERO) || (d.compareTo(n) >= 0));
+
+ ECPoint Q = params.getG().multiply(d);
+
+ return new AsymmetricCipherKeyPair(
+ new ECPublicKeyParameters(Q, params),
+ new ECPrivateKeyParameters(d, params));
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/ElGamalKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/ElGamalKeyPairGenerator.java
new file mode 100644
index 00000000..f23b6976
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/ElGamalKeyPairGenerator.java
@@ -0,0 +1,44 @@
+package org.bouncycastle.crypto.generators;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.ElGamalKeyGenerationParameters;
+import org.bouncycastle.crypto.params.ElGamalParameters;
+import org.bouncycastle.crypto.params.ElGamalPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ElGamalPublicKeyParameters;
+
+/**
+ * a ElGamal key pair generator.
+ * <p>
+ * This generates keys consistent for use with ElGamal as described in
+ * page 164 of "Handbook of Applied Cryptography".
+ */
+public class ElGamalKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+ private ElGamalKeyGenerationParameters param;
+
+ public void init(
+ KeyGenerationParameters param)
+ {
+ this.param = (ElGamalKeyGenerationParameters)param;
+ }
+
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ DHKeyGeneratorHelper helper = DHKeyGeneratorHelper.INSTANCE;
+ ElGamalParameters egp = param.getParameters();
+ DHParameters dhp = new DHParameters(egp.getP(), egp.getG(), null, egp.getL());
+
+ BigInteger x = helper.calculatePrivate(dhp, param.getRandom());
+ BigInteger y = helper.calculatePublic(dhp, x);
+
+ return new AsymmetricCipherKeyPair(
+ new ElGamalPublicKeyParameters(y, egp),
+ new ElGamalPrivateKeyParameters(x, egp));
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/ElGamalParametersGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/ElGamalParametersGenerator.java
new file mode 100644
index 00000000..21e8c2a0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/ElGamalParametersGenerator.java
@@ -0,0 +1,43 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.params.ElGamalParameters;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+public class ElGamalParametersGenerator
+{
+ private int size;
+ private int certainty;
+ private SecureRandom random;
+
+ public void init(
+ int size,
+ int certainty,
+ SecureRandom random)
+ {
+ this.size = size;
+ this.certainty = certainty;
+ this.random = random;
+ }
+
+ /**
+ * which generates the p and g values from the given parameters,
+ * returning the ElGamalParameters object.
+ * <p>
+ * Note: can take a while...
+ */
+ public ElGamalParameters generateParameters()
+ {
+ //
+ // find a safe prime p where p = 2*q + 1, where p and q are prime.
+ //
+ BigInteger[] safePrimes = DHParametersHelper.generateSafePrimes(size, certainty, random);
+
+ BigInteger p = safePrimes[0];
+ BigInteger q = safePrimes[1];
+ BigInteger g = DHParametersHelper.selectGenerator(p, q, random);
+
+ return new ElGamalParameters(p, g);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/EphemeralKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/EphemeralKeyPairGenerator.java
new file mode 100644
index 00000000..1004f23e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/EphemeralKeyPairGenerator.java
@@ -0,0 +1,26 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.EphemeralKeyPair;
+import org.bouncycastle.crypto.KeyEncoder;
+
+public class EphemeralKeyPairGenerator
+{
+ private AsymmetricCipherKeyPairGenerator gen;
+ private KeyEncoder keyEncoder;
+
+ public EphemeralKeyPairGenerator(AsymmetricCipherKeyPairGenerator gen, KeyEncoder keyEncoder)
+ {
+ this.gen = gen;
+ this.keyEncoder = keyEncoder;
+ }
+
+ public EphemeralKeyPair generate()
+ {
+ AsymmetricCipherKeyPair eph = gen.generateKeyPair();
+
+ // Encode the ephemeral public key
+ return new EphemeralKeyPair(eph, keyEncoder);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/GOST3410KeyPairGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/GOST3410KeyPairGenerator.java
new file mode 100644
index 00000000..3e13c212
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/GOST3410KeyPairGenerator.java
@@ -0,0 +1,57 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.params.GOST3410KeyGenerationParameters;
+import org.bouncycastle.crypto.params.GOST3410Parameters;
+import org.bouncycastle.crypto.params.GOST3410PrivateKeyParameters;
+import org.bouncycastle.crypto.params.GOST3410PublicKeyParameters;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/**
+ * a GOST3410 key pair generator.
+ * This generates GOST3410 keys in line with the method described
+ * in GOST R 34.10-94.
+ */
+public class GOST3410KeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+ {
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+
+ private GOST3410KeyGenerationParameters param;
+
+ public void init(
+ KeyGenerationParameters param)
+ {
+ this.param = (GOST3410KeyGenerationParameters)param;
+ }
+
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ BigInteger p, q, a, x, y;
+ GOST3410Parameters GOST3410Params = param.getParameters();
+ SecureRandom random = param.getRandom();
+
+ q = GOST3410Params.getQ();
+ p = GOST3410Params.getP();
+ a = GOST3410Params.getA();
+
+ do
+ {
+ x = new BigInteger(256, random);
+ }
+ while (x.equals(ZERO) || x.compareTo(q) >= 0);
+
+ //
+ // calculate the public key.
+ //
+ y = a.modPow(x, p);
+
+ return new AsymmetricCipherKeyPair(
+ new GOST3410PublicKeyParameters(y, GOST3410Params),
+ new GOST3410PrivateKeyParameters(x, GOST3410Params));
+ }
+ }
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/GOST3410ParametersGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/GOST3410ParametersGenerator.java
new file mode 100644
index 00000000..1c7cecf8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/GOST3410ParametersGenerator.java
@@ -0,0 +1,541 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.params.GOST3410Parameters;
+import org.bouncycastle.crypto.params.GOST3410ValidationParameters;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/**
+ * generate suitable parameters for GOST3410.
+ */
+public class GOST3410ParametersGenerator
+{
+ private int size;
+ private int typeproc;
+ private SecureRandom init_random;
+
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+ private static final BigInteger TWO = BigInteger.valueOf(2);
+
+ /**
+ * initialise the key generator.
+ *
+ * @param size size of the key
+ * @param typeproc type procedure A,B = 1; A',B' - else
+ * @param random random byte source.
+ */
+ public void init(
+ int size,
+ int typeproc,
+ SecureRandom random)
+ {
+ this.size = size;
+ this.typeproc = typeproc;
+ this.init_random = random;
+ }
+
+ //Procedure A
+ private int procedure_A(int x0, int c, BigInteger[] pq, int size)
+ {
+ //Verify and perform condition: 0<x<2^16; 0<c<2^16; c - odd.
+ while(x0<0 || x0>65536)
+ {
+ x0 = init_random.nextInt()/32768;
+ }
+
+ while((c<0 || c>65536) || (c/2==0))
+ {
+ c = init_random.nextInt()/32768 + 1;
+ }
+
+ BigInteger C = new BigInteger(Integer.toString(c));
+ BigInteger constA16 = new BigInteger("19381");
+
+ //step1
+ BigInteger[] y = new BigInteger[1]; // begin length = 1
+ y[0] = new BigInteger(Integer.toString(x0));
+
+ //step 2
+ int[] t = new int[1]; // t - orders; begin length = 1
+ t[0] = size;
+ int s = 0;
+ for (int i=0; t[i]>=17; i++)
+ {
+ // extension array t
+ int tmp_t[] = new int[t.length + 1]; ///////////////
+ System.arraycopy(t,0,tmp_t,0,t.length); // extension
+ t = new int[tmp_t.length]; // array t
+ System.arraycopy(tmp_t, 0, t, 0, tmp_t.length); ///////////////
+
+ t[i+1] = t[i]/2;
+ s = i+1;
+ }
+
+ //step3
+ BigInteger p[] = new BigInteger[s+1];
+ p[s] = new BigInteger("8003",16); //set min prime number length 16 bit
+
+ int m = s-1; //step4
+
+ for (int i=0; i<s; i++)
+ {
+ int rm = t[m]/16; //step5
+
+ step6: for(;;)
+ {
+ //step 6
+ BigInteger tmp_y[] = new BigInteger[y.length]; ////////////////
+ System.arraycopy(y,0,tmp_y,0,y.length); // extension
+ y = new BigInteger[rm+1]; // array y
+ System.arraycopy(tmp_y,0,y,0,tmp_y.length); ////////////////
+
+ for (int j=0; j<rm; j++)
+ {
+ y[j+1] = (y[j].multiply(constA16).add(C)).mod(TWO.pow(16));
+ }
+
+ //step 7
+ BigInteger Ym = new BigInteger("0");
+ for (int j=0; j<rm; j++)
+ {
+ Ym = Ym.add(y[j].multiply(TWO.pow(16*j)));
+ }
+
+ y[0] = y[rm]; //step 8
+
+ //step 9
+ BigInteger N = TWO.pow(t[m]-1).divide(p[m+1]).
+ add((TWO.pow(t[m]-1).multiply(Ym)).
+ divide(p[m+1].multiply(TWO.pow(16*rm))));
+
+ if (N.mod(TWO).compareTo(ONE)==0)
+ {
+ N = N.add(ONE);
+ }
+
+ int k = 0; //step 10
+
+ step11: for(;;)
+ {
+ //step 11
+ p[m] = p[m+1].multiply(N.add(BigInteger.valueOf(k))).add(ONE);
+
+ if (p[m].compareTo(TWO.pow(t[m]))==1)
+ {
+ continue step6; //step 12
+ }
+
+ //step13
+ if ((TWO.modPow(p[m+1].multiply(N.add(BigInteger.valueOf(k))),p[m]).compareTo(ONE)==0) &&
+ (TWO.modPow(N.add(BigInteger.valueOf(k)),p[m]).compareTo(ONE)!=0))
+ {
+ m -= 1;
+ break;
+ }
+ else
+ {
+ k += 2;
+ continue step11;
+ }
+ }
+
+ if (m>=0)
+ {
+ break; //step 14
+ }
+ else
+ {
+ pq[0] = p[0];
+ pq[1] = p[1];
+ return y[0].intValue(); //return for procedure B step 2
+ }
+ }
+ }
+ return y[0].intValue();
+ }
+
+ //Procedure A'
+ private long procedure_Aa(long x0, long c, BigInteger[] pq, int size)
+ {
+ //Verify and perform condition: 0<x<2^32; 0<c<2^32; c - odd.
+ while(x0<0 || x0>4294967296L)
+ {
+ x0 = init_random.nextInt()*2;
+ }
+
+ while((c<0 || c>4294967296L) || (c/2==0))
+ {
+ c = init_random.nextInt()*2+1;
+ }
+
+ BigInteger C = new BigInteger(Long.toString(c));
+ BigInteger constA32 = new BigInteger("97781173");
+
+ //step1
+ BigInteger[] y = new BigInteger[1]; // begin length = 1
+ y[0] = new BigInteger(Long.toString(x0));
+
+ //step 2
+ int[] t = new int[1]; // t - orders; begin length = 1
+ t[0] = size;
+ int s = 0;
+ for (int i=0; t[i]>=33; i++)
+ {
+ // extension array t
+ int tmp_t[] = new int[t.length + 1]; ///////////////
+ System.arraycopy(t,0,tmp_t,0,t.length); // extension
+ t = new int[tmp_t.length]; // array t
+ System.arraycopy(tmp_t, 0, t, 0, tmp_t.length); ///////////////
+
+ t[i+1] = t[i]/2;
+ s = i+1;
+ }
+
+ //step3
+ BigInteger p[] = new BigInteger[s+1];
+ p[s] = new BigInteger("8000000B",16); //set min prime number length 32 bit
+
+ int m = s-1; //step4
+
+ for (int i=0; i<s; i++)
+ {
+ int rm = t[m]/32; //step5
+
+ step6: for(;;)
+ {
+ //step 6
+ BigInteger tmp_y[] = new BigInteger[y.length]; ////////////////
+ System.arraycopy(y,0,tmp_y,0,y.length); // extension
+ y = new BigInteger[rm+1]; // array y
+ System.arraycopy(tmp_y,0,y,0,tmp_y.length); ////////////////
+
+ for (int j=0; j<rm; j++)
+ {
+ y[j+1] = (y[j].multiply(constA32).add(C)).mod(TWO.pow(32));
+ }
+
+ //step 7
+ BigInteger Ym = new BigInteger("0");
+ for (int j=0; j<rm; j++)
+ {
+ Ym = Ym.add(y[j].multiply(TWO.pow(32*j)));
+ }
+
+ y[0] = y[rm]; //step 8
+
+ //step 9
+ BigInteger N = TWO.pow(t[m]-1).divide(p[m+1]).
+ add((TWO.pow(t[m]-1).multiply(Ym)).
+ divide(p[m+1].multiply(TWO.pow(32*rm))));
+
+ if (N.mod(TWO).compareTo(ONE)==0)
+ {
+ N = N.add(ONE);
+ }
+
+ int k = 0; //step 10
+
+ step11: for(;;)
+ {
+ //step 11
+ p[m] = p[m+1].multiply(N.add(BigInteger.valueOf(k))).add(ONE);
+
+ if (p[m].compareTo(TWO.pow(t[m]))==1)
+ {
+ continue step6; //step 12
+ }
+
+ //step13
+ if ((TWO.modPow(p[m+1].multiply(N.add(BigInteger.valueOf(k))),p[m]).compareTo(ONE)==0) &&
+ (TWO.modPow(N.add(BigInteger.valueOf(k)),p[m]).compareTo(ONE)!=0))
+ {
+ m -= 1;
+ break;
+ }
+ else
+ {
+ k += 2;
+ continue step11;
+ }
+ }
+
+ if (m>=0)
+ {
+ break; //step 14
+ }
+ else
+ {
+ pq[0] = p[0];
+ pq[1] = p[1];
+ return y[0].longValue(); //return for procedure B' step 2
+ }
+ }
+ }
+ return y[0].longValue();
+ }
+
+ //Procedure B
+ private void procedure_B(int x0, int c, BigInteger[] pq)
+ {
+ //Verify and perform condition: 0<x<2^16; 0<c<2^16; c - odd.
+ while(x0<0 || x0>65536)
+ {
+ x0 = init_random.nextInt()/32768;
+ }
+
+ while((c<0 || c>65536) || (c/2==0))
+ {
+ c = init_random.nextInt()/32768 + 1;
+ }
+
+ BigInteger [] qp = new BigInteger[2];
+ BigInteger q = null, Q = null, p = null;
+ BigInteger C = new BigInteger(Integer.toString(c));
+ BigInteger constA16 = new BigInteger("19381");
+
+ //step1
+ x0 = procedure_A(x0, c, qp, 256);
+ q = qp[0];
+
+ //step2
+ x0 = procedure_A(x0, c, qp, 512);
+ Q = qp[0];
+
+ BigInteger[] y = new BigInteger[65];
+ y[0] = new BigInteger(Integer.toString(x0));
+
+ int tp = 1024;
+
+ step3: for(;;)
+ {
+ //step 3
+ for (int j=0; j<64; j++)
+ {
+ y[j+1] = (y[j].multiply(constA16).add(C)).mod(TWO.pow(16));
+ }
+
+ //step 4
+ BigInteger Y = new BigInteger("0");
+
+ for (int j=0; j<64; j++)
+ {
+ Y = Y.add(y[j].multiply(TWO.pow(16*j)));
+ }
+
+ y[0] = y[64]; //step 5
+
+ //step 6
+ BigInteger N = TWO.pow(tp-1).divide(q.multiply(Q)).
+ add((TWO.pow(tp-1).multiply(Y)).
+ divide(q.multiply(Q).multiply(TWO.pow(1024))));
+
+ if (N.mod(TWO).compareTo(ONE)==0)
+ {
+ N = N.add(ONE);
+ }
+
+ int k = 0; //step 7
+
+ step8: for(;;)
+ {
+ //step 11
+ p = q.multiply(Q).multiply(N.add(BigInteger.valueOf(k))).add(ONE);
+
+ if (p.compareTo(TWO.pow(tp))==1)
+ {
+ continue step3; //step 9
+ }
+
+ //step10
+ if ((TWO.modPow(q.multiply(Q).multiply(N.add(BigInteger.valueOf(k))),p).compareTo(ONE)==0) &&
+ (TWO.modPow(q.multiply(N.add(BigInteger.valueOf(k))),p).compareTo(ONE)!=0))
+ {
+ pq[0] = p;
+ pq[1] = q;
+ return;
+ }
+ else
+ {
+ k += 2;
+ continue step8;
+ }
+ }
+ }
+ }
+
+ //Procedure B'
+ private void procedure_Bb(long x0, long c, BigInteger[] pq)
+ {
+ //Verify and perform condition: 0<x<2^32; 0<c<2^32; c - odd.
+ while(x0<0 || x0>4294967296L)
+ {
+ x0 = init_random.nextInt()*2;
+ }
+
+ while((c<0 || c>4294967296L) || (c/2==0))
+ {
+ c = init_random.nextInt()*2+1;
+ }
+
+ BigInteger [] qp = new BigInteger[2];
+ BigInteger q = null, Q = null, p = null;
+ BigInteger C = new BigInteger(Long.toString(c));
+ BigInteger constA32 = new BigInteger("97781173");
+
+ //step1
+ x0 = procedure_Aa(x0, c, qp, 256);
+ q = qp[0];
+
+ //step2
+ x0 = procedure_Aa(x0, c, qp, 512);
+ Q = qp[0];
+
+ BigInteger[] y = new BigInteger[33];
+ y[0] = new BigInteger(Long.toString(x0));
+
+ int tp = 1024;
+
+ step3: for(;;)
+ {
+ //step 3
+ for (int j=0; j<32; j++)
+ {
+ y[j+1] = (y[j].multiply(constA32).add(C)).mod(TWO.pow(32));
+ }
+
+ //step 4
+ BigInteger Y = new BigInteger("0");
+ for (int j=0; j<32; j++)
+ {
+ Y = Y.add(y[j].multiply(TWO.pow(32*j)));
+ }
+
+ y[0] = y[32]; //step 5
+
+ //step 6
+ BigInteger N = TWO.pow(tp-1).divide(q.multiply(Q)).
+ add((TWO.pow(tp-1).multiply(Y)).
+ divide(q.multiply(Q).multiply(TWO.pow(1024))));
+
+ if (N.mod(TWO).compareTo(ONE)==0)
+ {
+ N = N.add(ONE);
+ }
+
+ int k = 0; //step 7
+
+ step8: for(;;)
+ {
+ //step 11
+ p = q.multiply(Q).multiply(N.add(BigInteger.valueOf(k))).add(ONE);
+
+ if (p.compareTo(TWO.pow(tp))==1)
+ {
+ continue step3; //step 9
+ }
+
+ //step10
+ if ((TWO.modPow(q.multiply(Q).multiply(N.add(BigInteger.valueOf(k))),p).compareTo(ONE)==0) &&
+ (TWO.modPow(q.multiply(N.add(BigInteger.valueOf(k))),p).compareTo(ONE)!=0))
+ {
+ pq[0] = p;
+ pq[1] = q;
+ return;
+ }
+ else
+ {
+ k += 2;
+ continue step8;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Procedure C
+ * procedure generates the a value from the given p,q,
+ * returning the a value.
+ */
+ private BigInteger procedure_C(BigInteger p, BigInteger q)
+ {
+ BigInteger pSub1 = p.subtract(ONE);
+ BigInteger pSub1DivQ = pSub1.divide(q);
+ int length = p.bitLength();
+
+ for(;;)
+ {
+ BigInteger d = new BigInteger(length, init_random);
+
+ // 1 < d < p-1
+ if (d.compareTo(ONE) > 0 && d.compareTo(pSub1) < 0)
+ {
+ BigInteger a = d.modPow(pSub1DivQ, p);
+
+ if (a.compareTo(ONE) != 0)
+ {
+ return a;
+ }
+ }
+ }
+ }
+
+ /**
+ * which generates the p , q and a values from the given parameters,
+ * returning the GOST3410Parameters object.
+ */
+ public GOST3410Parameters generateParameters()
+ {
+ BigInteger [] pq = new BigInteger[2];
+ BigInteger q = null, p = null, a = null;
+
+ int x0, c;
+ long x0L, cL;
+
+ if (typeproc==1)
+ {
+ x0 = init_random.nextInt();
+ c = init_random.nextInt();
+
+ switch(size)
+ {
+ case 512:
+ procedure_A(x0, c, pq, 512);
+ break;
+ case 1024:
+ procedure_B(x0, c, pq);
+ break;
+ default:
+ throw new IllegalArgumentException("Ooops! key size 512 or 1024 bit.");
+ }
+ p = pq[0]; q = pq[1];
+ a = procedure_C(p, q);
+ //System.out.println("p:"+p.toString(16)+"\n"+"q:"+q.toString(16)+"\n"+"a:"+a.toString(16));
+ //System.out.println("p:"+p+"\n"+"q:"+q+"\n"+"a:"+a);
+ return new GOST3410Parameters(p, q, a, new GOST3410ValidationParameters(x0, c));
+ }
+ else
+ {
+ x0L = init_random.nextLong();
+ cL = init_random.nextLong();
+
+ switch(size)
+ {
+ case 512:
+ procedure_Aa(x0L, cL, pq, 512);
+ break;
+ case 1024:
+ procedure_Bb(x0L, cL, pq);
+ break;
+ default:
+ throw new IllegalStateException("Ooops! key size 512 or 1024 bit.");
+ }
+ p = pq[0]; q = pq[1];
+ a = procedure_C(p, q);
+ //System.out.println("p:"+p.toString(16)+"\n"+"q:"+q.toString(16)+"\n"+"a:"+a.toString(16));
+ //System.out.println("p:"+p+"\n"+"q:"+q+"\n"+"a:"+a);
+ return new GOST3410Parameters(p, q, a, new GOST3410ValidationParameters(x0L, cL));
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/HKDFBytesGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/HKDFBytesGenerator.java
new file mode 100644
index 00000000..8e93e6b0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/HKDFBytesGenerator.java
@@ -0,0 +1,161 @@
+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.macs.HMac;
+import org.bouncycastle.crypto.params.HKDFParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * HMAC-based Extract-and-Expand Key Derivation Function (HKDF) implemented
+ * according to IETF RFC 5869, May 2010 as specified by H. Krawczyk, IBM
+ * Research & P. Eronen, Nokia. It uses a HMac internally to compute de OKM
+ * (output keying material) and is likely to have better security properties
+ * than KDF's based on just a hash function.
+ */
+public class HKDFBytesGenerator
+ implements DerivationFunction
+{
+
+ private HMac hMacHash;
+ private int hashLen;
+
+ private byte[] info;
+ private byte[] currentT;
+
+ private int generatedBytes;
+
+ /**
+ * Creates a HKDFBytesGenerator based on the given hash function.
+ *
+ * @param hash the digest to be used as the source of generatedBytes bytes
+ */
+ public HKDFBytesGenerator(Digest hash)
+ {
+ this.hMacHash = new HMac(hash);
+ this.hashLen = hash.getDigestSize();
+ }
+
+ public void init(DerivationParameters param)
+ {
+ if (!(param instanceof HKDFParameters))
+ {
+ throw new IllegalArgumentException(
+ "HKDF parameters required for HKDFBytesGenerator");
+ }
+
+ HKDFParameters params = (HKDFParameters)param;
+ if (params.skipExtract())
+ {
+ // use IKM directly as PRK
+ hMacHash.init(new KeyParameter(params.getIKM()));
+ }
+ else
+ {
+ hMacHash.init(extract(params.getSalt(), params.getIKM()));
+ }
+
+ info = params.getInfo();
+
+ generatedBytes = 0;
+ currentT = new byte[hashLen];
+ }
+
+ /**
+ * Performs the extract part of the key derivation function.
+ *
+ * @param salt the salt to use
+ * @param ikm the input keying material
+ * @return the PRK as KeyParameter
+ */
+ private KeyParameter extract(byte[] salt, byte[] ikm)
+ {
+ hMacHash.init(new KeyParameter(ikm));
+ if (salt == null)
+ {
+ // TODO check if hashLen is indeed same as HMAC size
+ hMacHash.init(new KeyParameter(new byte[hashLen]));
+ }
+ else
+ {
+ hMacHash.init(new KeyParameter(salt));
+ }
+
+ hMacHash.update(ikm, 0, ikm.length);
+
+ byte[] prk = new byte[hashLen];
+ hMacHash.doFinal(prk, 0);
+ return new KeyParameter(prk);
+ }
+
+ /**
+ * Performs the expand part of the key derivation function, using currentT
+ * as input and output buffer.
+ *
+ * @throws DataLengthException if the total number of bytes generated is larger than the one
+ * specified by RFC 5869 (255 * HashLen)
+ */
+ private void expandNext()
+ throws DataLengthException
+ {
+ int n = generatedBytes / hashLen + 1;
+ if (n >= 256)
+ {
+ throw new DataLengthException(
+ "HKDF cannot generate more than 255 blocks of HashLen size");
+ }
+ // special case for T(0): T(0) is empty, so no update
+ if (generatedBytes != 0)
+ {
+ hMacHash.update(currentT, 0, hashLen);
+ }
+ hMacHash.update(info, 0, info.length);
+ hMacHash.update((byte)n);
+ hMacHash.doFinal(currentT, 0);
+ }
+
+ public Digest getDigest()
+ {
+ return hMacHash.getUnderlyingDigest();
+ }
+
+ public int generateBytes(byte[] out, int outOff, int len)
+ throws DataLengthException, IllegalArgumentException
+ {
+
+ if (generatedBytes + len > 255 * hashLen)
+ {
+ throw new DataLengthException(
+ "HKDF may only be used for 255 * HashLen bytes of output");
+ }
+
+ if (generatedBytes % hashLen == 0)
+ {
+ expandNext();
+ }
+
+ // copy what is left in the currentT (1..hash
+ int toGenerate = len;
+ int posInT = generatedBytes % hashLen;
+ int leftInT = hashLen - generatedBytes % hashLen;
+ int toCopy = Math.min(leftInT, toGenerate);
+ System.arraycopy(currentT, posInT, out, outOff, toCopy);
+ generatedBytes += toCopy;
+ toGenerate -= toCopy;
+ outOff += toCopy;
+
+ while (toGenerate > 0)
+ {
+ expandNext();
+ toCopy = Math.min(hashLen, toGenerate);
+ System.arraycopy(currentT, 0, out, outOff, toCopy);
+ generatedBytes += toCopy;
+ toGenerate -= toCopy;
+ outOff += toCopy;
+ }
+
+ return len;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/KDF1BytesGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/KDF1BytesGenerator.java
new file mode 100644
index 00000000..7789b7b5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/KDF1BytesGenerator.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.Digest;
+
+/**
+ * KDF1 generator for derived keys and ivs as defined by IEEE P1363a/ISO 18033
+ * <br>
+ * This implementation is based on ISO 18033/IEEE P1363a.
+ */
+public class KDF1BytesGenerator
+ extends BaseKDFBytesGenerator
+{
+ /**
+ * Construct a KDF1 byte generator.
+ * <p>
+ * @param digest the digest to be used as the source of derived keys.
+ */
+ public KDF1BytesGenerator(
+ Digest digest)
+ {
+ super(0, digest);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/KDF2BytesGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/KDF2BytesGenerator.java
new file mode 100644
index 00000000..ac0c64a4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/KDF2BytesGenerator.java
@@ -0,0 +1,24 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.Digest;
+
+/**
+ * KDF2 generator for derived keys and ivs as defined by IEEE P1363a/ISO 18033
+ * <br>
+ * This implementation is based on IEEE P1363/ISO 18033.
+ */
+public class KDF2BytesGenerator
+ extends BaseKDFBytesGenerator
+{
+ /**
+ * Construct a KDF2 bytes generator. Generates key material
+ * according to IEEE P1363 or ISO 18033 depending on the initialisation.
+ * <p>
+ * @param digest the digest to be used as the source of derived keys.
+ */
+ public KDF2BytesGenerator(
+ Digest digest)
+ {
+ super(1, digest);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/MGF1BytesGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/MGF1BytesGenerator.java
new file mode 100644
index 00000000..e93c0d73
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/MGF1BytesGenerator.java
@@ -0,0 +1,114 @@
+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.MGFParameters;
+
+/**
+ * Generator for MGF1 as defined in PKCS 1v2
+ */
+public class MGF1BytesGenerator
+ implements DerivationFunction
+{
+ private Digest digest;
+ private byte[] seed;
+ private int hLen;
+
+ /**
+ * @param digest the digest to be used as the source of generated bytes
+ */
+ public MGF1BytesGenerator(
+ Digest digest)
+ {
+ this.digest = digest;
+ this.hLen = digest.getDigestSize();
+ }
+
+ public void init(
+ DerivationParameters param)
+ {
+ if (!(param instanceof MGFParameters))
+ {
+ throw new IllegalArgumentException("MGF parameters required for MGF1Generator");
+ }
+
+ MGFParameters p = (MGFParameters)param;
+
+ seed = p.getSeed();
+ }
+
+ /**
+ * return the underlying digest.
+ */
+ public Digest getDigest()
+ {
+ return digest;
+ }
+
+ /**
+ * int to octet string.
+ */
+ private void ItoOSP(
+ int i,
+ byte[] sp)
+ {
+ sp[0] = (byte)(i >>> 24);
+ sp[1] = (byte)(i >>> 16);
+ sp[2] = (byte)(i >>> 8);
+ sp[3] = (byte)(i >>> 0);
+ }
+
+ /**
+ * fill len bytes of the output buffer with bytes generated from
+ * the derivation function.
+ *
+ * @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");
+ }
+
+ byte[] hashBuf = new byte[hLen];
+ byte[] C = new byte[4];
+ int counter = 0;
+
+ digest.reset();
+
+ if (len > hLen)
+ {
+ do
+ {
+ ItoOSP(counter, C);
+
+ digest.update(seed, 0, seed.length);
+ digest.update(C, 0, C.length);
+ digest.doFinal(hashBuf, 0);
+
+ System.arraycopy(hashBuf, 0, out, outOff + counter * hLen, hLen);
+ }
+ while (++counter < (len / hLen));
+ }
+
+ if ((counter * hLen) < len)
+ {
+ ItoOSP(counter, C);
+
+ digest.update(seed, 0, seed.length);
+ digest.update(C, 0, C.length);
+ digest.doFinal(hashBuf, 0);
+
+ System.arraycopy(hashBuf, 0, out, outOff + counter * hLen, len - (counter * hLen));
+ }
+
+ return len;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/NaccacheSternKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/NaccacheSternKeyPairGenerator.java
new file mode 100644
index 00000000..ceb39407
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/NaccacheSternKeyPairGenerator.java
@@ -0,0 +1,365 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.params.NaccacheSternKeyGenerationParameters;
+import org.bouncycastle.crypto.params.NaccacheSternKeyParameters;
+import org.bouncycastle.crypto.params.NaccacheSternPrivateKeyParameters;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.Vector;
+
+/**
+ * Key generation parameters for NaccacheStern cipher. For details on this cipher, please see
+ *
+ * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf
+ */
+public class NaccacheSternKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+
+ private static int[] smallPrimes =
+ {
+ 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67,
+ 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149,
+ 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233,
+ 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331,
+ 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431,
+ 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523,
+ 541, 547, 557
+ };
+
+ private NaccacheSternKeyGenerationParameters param;
+
+ private static final BigInteger ONE = BigInteger.valueOf(1); // JDK 1.1 compatibility
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator#init(org.bouncycastle.crypto.KeyGenerationParameters)
+ */
+ public void init(KeyGenerationParameters param)
+ {
+ this.param = (NaccacheSternKeyGenerationParameters)param;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator#generateKeyPair()
+ */
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ int strength = param.getStrength();
+ SecureRandom rand = param.getRandom();
+ int certainty = param.getCertainty();
+ boolean debug = param.isDebug();
+
+ if (debug)
+ {
+ System.out.println("Fetching first " + param.getCntSmallPrimes() + " primes.");
+ }
+
+ Vector smallPrimes = findFirstPrimes(param.getCntSmallPrimes());
+ smallPrimes = permuteList(smallPrimes, rand);
+
+ BigInteger u = ONE;
+ BigInteger v = ONE;
+
+ for (int i = 0; i < smallPrimes.size() / 2; i++)
+ {
+ u = u.multiply((BigInteger)smallPrimes.elementAt(i));
+ }
+ for (int i = smallPrimes.size() / 2; i < smallPrimes.size(); i++)
+ {
+ v = v.multiply((BigInteger)smallPrimes.elementAt(i));
+ }
+
+ BigInteger sigma = u.multiply(v);
+
+ // n = (2 a u p_ + 1 ) ( 2 b v q_ + 1)
+ // -> |n| = strength
+ // |2| = 1 in bits
+ // -> |a| * |b| = |n| - |u| - |v| - |p_| - |q_| - |2| -|2|
+ // remainingStrength = strength - sigma.bitLength() - p_.bitLength() -
+ // q_.bitLength() - 1 -1
+ int remainingStrength = strength - sigma.bitLength() - 48;
+ BigInteger a = generatePrime(remainingStrength / 2 + 1, certainty, rand);
+ BigInteger b = generatePrime(remainingStrength / 2 + 1, certainty, rand);
+
+ BigInteger p_;
+ BigInteger q_;
+ BigInteger p;
+ BigInteger q;
+ long tries = 0;
+ if (debug)
+ {
+ System.out.println("generating p and q");
+ }
+
+ BigInteger _2au = a.multiply(u).shiftLeft(1);
+ BigInteger _2bv = b.multiply(v).shiftLeft(1);
+
+ for (;;)
+ {
+ tries++;
+
+ p_ = generatePrime(24, certainty, rand);
+
+ p = p_.multiply(_2au).add(ONE);
+
+ if (!p.isProbablePrime(certainty))
+ {
+ continue;
+ }
+
+ for (;;)
+ {
+ q_ = generatePrime(24, certainty, rand);
+
+ if (p_.equals(q_))
+ {
+ continue;
+ }
+
+ q = q_.multiply(_2bv).add(ONE);
+
+ if (q.isProbablePrime(certainty))
+ {
+ break;
+ }
+ }
+
+ if (!sigma.gcd(p_.multiply(q_)).equals(ONE))
+ {
+ // System.out.println("sigma.gcd(p_.mult(q_)) != 1!\n p_: " + p_
+ // +"\n q_: "+ q_ );
+ continue;
+ }
+
+ if (p.multiply(q).bitLength() < strength)
+ {
+ if (debug)
+ {
+ System.out.println("key size too small. Should be " + strength + " but is actually "
+ + p.multiply(q).bitLength());
+ }
+ continue;
+ }
+ break;
+ }
+
+ if (debug)
+ {
+ System.out.println("needed " + tries + " tries to generate p and q.");
+ }
+
+ BigInteger n = p.multiply(q);
+ BigInteger phi_n = p.subtract(ONE).multiply(q.subtract(ONE));
+ BigInteger g;
+ tries = 0;
+ if (debug)
+ {
+ System.out.println("generating g");
+ }
+ for (;;)
+ {
+
+ Vector gParts = new Vector();
+ for (int ind = 0; ind != smallPrimes.size(); ind++)
+ {
+ BigInteger i = (BigInteger)smallPrimes.elementAt(ind);
+ BigInteger e = phi_n.divide(i);
+
+ for (;;)
+ {
+ tries++;
+ g = new BigInteger(strength, certainty, rand);
+ if (g.modPow(e, n).equals(ONE))
+ {
+ continue;
+ }
+ gParts.addElement(g);
+ break;
+ }
+ }
+ g = ONE;
+ for (int i = 0; i < smallPrimes.size(); i++)
+ {
+ g = g.multiply(((BigInteger)gParts.elementAt(i)).modPow(sigma.divide((BigInteger)smallPrimes.elementAt(i)), n)).mod(n);
+ }
+
+ // make sure that g is not divisible by p_i or q_i
+ boolean divisible = false;
+ for (int i = 0; i < smallPrimes.size(); i++)
+ {
+ if (g.modPow(phi_n.divide((BigInteger)smallPrimes.elementAt(i)), n).equals(ONE))
+ {
+ if (debug)
+ {
+ System.out.println("g has order phi(n)/" + smallPrimes.elementAt(i) + "\n g: " + g);
+ }
+ divisible = true;
+ break;
+ }
+ }
+
+ if (divisible)
+ {
+ continue;
+ }
+
+ // make sure that g has order > phi_n/4
+
+ if (g.modPow(phi_n.divide(BigInteger.valueOf(4)), n).equals(ONE))
+ {
+ if (debug)
+ {
+ System.out.println("g has order phi(n)/4\n g:" + g);
+ }
+ continue;
+ }
+
+ if (g.modPow(phi_n.divide(p_), n).equals(ONE))
+ {
+ if (debug)
+ {
+ System.out.println("g has order phi(n)/p'\n g: " + g);
+ }
+ continue;
+ }
+ if (g.modPow(phi_n.divide(q_), n).equals(ONE))
+ {
+ if (debug)
+ {
+ System.out.println("g has order phi(n)/q'\n g: " + g);
+ }
+ continue;
+ }
+ if (g.modPow(phi_n.divide(a), n).equals(ONE))
+ {
+ if (debug)
+ {
+ System.out.println("g has order phi(n)/a\n g: " + g);
+ }
+ continue;
+ }
+ if (g.modPow(phi_n.divide(b), n).equals(ONE))
+ {
+ if (debug)
+ {
+ System.out.println("g has order phi(n)/b\n g: " + g);
+ }
+ continue;
+ }
+ break;
+ }
+ if (debug)
+ {
+ System.out.println("needed " + tries + " tries to generate g");
+ System.out.println();
+ System.out.println("found new NaccacheStern cipher variables:");
+ System.out.println("smallPrimes: " + smallPrimes);
+ System.out.println("sigma:...... " + sigma + " (" + sigma.bitLength() + " bits)");
+ System.out.println("a:.......... " + a);
+ System.out.println("b:.......... " + b);
+ System.out.println("p':......... " + p_);
+ System.out.println("q':......... " + q_);
+ System.out.println("p:.......... " + p);
+ System.out.println("q:.......... " + q);
+ System.out.println("n:.......... " + n);
+ System.out.println("phi(n):..... " + phi_n);
+ System.out.println("g:.......... " + g);
+ System.out.println();
+ }
+
+ return new AsymmetricCipherKeyPair(new NaccacheSternKeyParameters(false, g, n, sigma.bitLength()),
+ new NaccacheSternPrivateKeyParameters(g, n, sigma.bitLength(), smallPrimes, phi_n));
+ }
+
+ private static BigInteger generatePrime(
+ int bitLength,
+ int certainty,
+ SecureRandom rand)
+ {
+ BigInteger p_ = new BigInteger(bitLength, certainty, rand);
+ while (p_.bitLength() != bitLength)
+ {
+ p_ = new BigInteger(bitLength, certainty, rand);
+ }
+ return p_;
+ }
+
+ /**
+ * Generates a permuted ArrayList from the original one. The original List
+ * is not modified
+ *
+ * @param arr
+ * the ArrayList to be permuted
+ * @param rand
+ * the source of Randomness for permutation
+ * @return a new ArrayList with the permuted elements.
+ */
+ private static Vector permuteList(
+ Vector arr,
+ SecureRandom rand)
+ {
+ Vector retval = new Vector();
+ Vector tmp = new Vector();
+ for (int i = 0; i < arr.size(); i++)
+ {
+ tmp.addElement(arr.elementAt(i));
+ }
+ retval.addElement(tmp.elementAt(0));
+ tmp.removeElementAt(0);
+ while (tmp.size() != 0)
+ {
+ retval.insertElementAt(tmp.elementAt(0), getInt(rand, retval.size() + 1));
+ tmp.removeElementAt(0);
+ }
+ return retval;
+ }
+
+ private static int getInt(
+ SecureRandom rand,
+ int n)
+ {
+ if ((n & -n) == n)
+ {
+ return (int)((n * (long)(rand.nextInt() & 0x7fffffff)) >> 31);
+ }
+
+ int bits, val;
+ do
+ {
+ bits = rand.nextInt() & 0x7fffffff;
+ val = bits % n;
+ }
+ while (bits - val + (n-1) < 0);
+
+ return val;
+ }
+
+ /**
+ * Finds the first 'count' primes starting with 3
+ *
+ * @param count
+ * the number of primes to find
+ * @return a vector containing the found primes as Integer
+ */
+ private static Vector findFirstPrimes(
+ int count)
+ {
+ Vector primes = new Vector(count);
+
+ for (int i = 0; i != count; i++)
+ {
+ primes.addElement(BigInteger.valueOf(smallPrimes[i]));
+ }
+
+ return primes;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java
new file mode 100644
index 00000000..8a4d28ab
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java
@@ -0,0 +1,131 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.PBEParametersGenerator;
+import org.bouncycastle.crypto.digests.MD5Digest;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+
+/**
+ * Generator for PBE derived keys and ivs as usd by OpenSSL.
+ * <p>
+ * The scheme is a simple extension of PKCS 5 V2.0 Scheme 1 using MD5 with an
+ * iteration count of 1.
+ * <p>
+ */
+public class OpenSSLPBEParametersGenerator
+ extends PBEParametersGenerator
+{
+ private Digest digest = new MD5Digest();
+
+ /**
+ * Construct a OpenSSL Parameters generator.
+ */
+ public OpenSSLPBEParametersGenerator()
+ {
+ }
+
+ /**
+ * Initialise - note the iteration count for this algorithm is fixed at 1.
+ *
+ * @param password password to use.
+ * @param salt salt to use.
+ */
+ public void init(
+ byte[] password,
+ byte[] salt)
+ {
+ super.init(password, salt, 1);
+ }
+
+ /**
+ * the derived key function, the ith hash of the password and the salt.
+ */
+ private byte[] generateDerivedKey(
+ int bytesNeeded)
+ {
+ byte[] buf = new byte[digest.getDigestSize()];
+ byte[] key = new byte[bytesNeeded];
+ int offset = 0;
+
+ for (;;)
+ {
+ digest.update(password, 0, password.length);
+ digest.update(salt, 0, salt.length);
+
+ digest.doFinal(buf, 0);
+
+ int len = (bytesNeeded > buf.length) ? buf.length : bytesNeeded;
+ System.arraycopy(buf, 0, key, offset, len);
+ offset += len;
+
+ // check if we need any more
+ bytesNeeded -= len;
+ if (bytesNeeded == 0)
+ {
+ break;
+ }
+
+ // do another round
+ digest.reset();
+ digest.update(buf, 0, buf.length);
+ }
+
+ return key;
+ }
+
+ /**
+ * Generate a key parameter derived from the password, salt, and iteration
+ * count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ * @exception IllegalArgumentException if the key length larger than the base hash size.
+ */
+ public CipherParameters generateDerivedParameters(
+ int keySize)
+ {
+ keySize = keySize / 8;
+
+ byte[] dKey = generateDerivedKey(keySize);
+
+ return new KeyParameter(dKey, 0, keySize);
+ }
+
+ /**
+ * Generate a key with initialisation vector parameter derived from
+ * the password, salt, and iteration count we are currently initialised
+ * with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @param ivSize the size of the iv we want (in bits)
+ * @return a ParametersWithIV object.
+ * @exception IllegalArgumentException if keySize + ivSize is larger than the base hash size.
+ */
+ public CipherParameters generateDerivedParameters(
+ int keySize,
+ int ivSize)
+ {
+ keySize = keySize / 8;
+ ivSize = ivSize / 8;
+
+ byte[] dKey = generateDerivedKey(keySize + ivSize);
+
+ return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), dKey, keySize, ivSize);
+ }
+
+ /**
+ * Generate a key parameter for use with a MAC derived from the password,
+ * salt, and iteration count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ * @exception IllegalArgumentException if the key length larger than the base hash size.
+ */
+ public CipherParameters generateDerivedMacParameters(
+ int keySize)
+ {
+ return generateDerivedParameters(keySize);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/PKCS12ParametersGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/PKCS12ParametersGenerator.java
new file mode 100644
index 00000000..d9b82c32
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/PKCS12ParametersGenerator.java
@@ -0,0 +1,220 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.ExtendedDigest;
+import org.bouncycastle.crypto.PBEParametersGenerator;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+
+/**
+ * Generator for PBE derived keys and ivs as defined by PKCS 12 V1.0.
+ * <p>
+ * The document this implementation is based on can be found at
+ * <a href=http://www.rsasecurity.com/rsalabs/pkcs/pkcs-12/index.html>
+ * RSA's PKCS12 Page</a>
+ */
+public class PKCS12ParametersGenerator
+ extends PBEParametersGenerator
+{
+ public static final int KEY_MATERIAL = 1;
+ public static final int IV_MATERIAL = 2;
+ public static final int MAC_MATERIAL = 3;
+
+ private Digest digest;
+
+ private int u;
+ private int v;
+
+ /**
+ * Construct a PKCS 12 Parameters generator. This constructor will
+ * accept any digest which also implements ExtendedDigest.
+ *
+ * @param digest the digest to be used as the source of derived keys.
+ * @exception IllegalArgumentException if an unknown digest is passed in.
+ */
+ public PKCS12ParametersGenerator(
+ Digest digest)
+ {
+ this.digest = digest;
+ if (digest instanceof ExtendedDigest)
+ {
+ u = digest.getDigestSize();
+ v = ((ExtendedDigest)digest).getByteLength();
+ }
+ else
+ {
+ throw new IllegalArgumentException("Digest " + digest.getAlgorithmName() + " unsupported");
+ }
+ }
+
+ /**
+ * add a + b + 1, returning the result in a. The a value is treated
+ * as a BigInteger of length (b.length * 8) bits. The result is
+ * modulo 2^b.length in case of overflow.
+ */
+ private void adjust(
+ byte[] a,
+ int aOff,
+ byte[] b)
+ {
+ int x = (b[b.length - 1] & 0xff) + (a[aOff + b.length - 1] & 0xff) + 1;
+
+ a[aOff + b.length - 1] = (byte)x;
+ x >>>= 8;
+
+ for (int i = b.length - 2; i >= 0; i--)
+ {
+ x += (b[i] & 0xff) + (a[aOff + i] & 0xff);
+ a[aOff + i] = (byte)x;
+ x >>>= 8;
+ }
+ }
+
+ /**
+ * generation of a derived key ala PKCS12 V1.0.
+ */
+ private byte[] generateDerivedKey(
+ int idByte,
+ int n)
+ {
+ byte[] D = new byte[v];
+ byte[] dKey = new byte[n];
+
+ for (int i = 0; i != D.length; i++)
+ {
+ D[i] = (byte)idByte;
+ }
+
+ byte[] S;
+
+ if ((salt != null) && (salt.length != 0))
+ {
+ S = new byte[v * ((salt.length + v - 1) / v)];
+
+ for (int i = 0; i != S.length; i++)
+ {
+ S[i] = salt[i % salt.length];
+ }
+ }
+ else
+ {
+ S = new byte[0];
+ }
+
+ byte[] P;
+
+ if ((password != null) && (password.length != 0))
+ {
+ P = new byte[v * ((password.length + v - 1) / v)];
+
+ for (int i = 0; i != P.length; i++)
+ {
+ P[i] = password[i % password.length];
+ }
+ }
+ else
+ {
+ P = new byte[0];
+ }
+
+ byte[] I = new byte[S.length + P.length];
+
+ System.arraycopy(S, 0, I, 0, S.length);
+ System.arraycopy(P, 0, I, S.length, P.length);
+
+ byte[] B = new byte[v];
+ int c = (n + u - 1) / u;
+ byte[] A = new byte[u];
+
+ for (int i = 1; i <= c; i++)
+ {
+ digest.update(D, 0, D.length);
+ digest.update(I, 0, I.length);
+ digest.doFinal(A, 0);
+ for (int j = 1; j < iterationCount; j++)
+ {
+ digest.update(A, 0, A.length);
+ digest.doFinal(A, 0);
+ }
+
+ for (int j = 0; j != B.length; j++)
+ {
+ B[j] = A[j % A.length];
+ }
+
+ for (int j = 0; j != I.length / v; j++)
+ {
+ adjust(I, j * v, B);
+ }
+
+ if (i == c)
+ {
+ System.arraycopy(A, 0, dKey, (i - 1) * u, dKey.length - ((i - 1) * u));
+ }
+ else
+ {
+ System.arraycopy(A, 0, dKey, (i - 1) * u, A.length);
+ }
+ }
+
+ return dKey;
+ }
+
+ /**
+ * Generate a key parameter derived from the password, salt, and iteration
+ * count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ */
+ public CipherParameters generateDerivedParameters(
+ int keySize)
+ {
+ keySize = keySize / 8;
+
+ byte[] dKey = generateDerivedKey(KEY_MATERIAL, keySize);
+
+ return new KeyParameter(dKey, 0, keySize);
+ }
+
+ /**
+ * Generate a key with initialisation vector parameter derived from
+ * the password, salt, and iteration count we are currently initialised
+ * with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @param ivSize the size of the iv we want (in bits)
+ * @return a ParametersWithIV object.
+ */
+ public CipherParameters generateDerivedParameters(
+ int keySize,
+ int ivSize)
+ {
+ keySize = keySize / 8;
+ ivSize = ivSize / 8;
+
+ byte[] dKey = generateDerivedKey(KEY_MATERIAL, keySize);
+
+ byte[] iv = generateDerivedKey(IV_MATERIAL, ivSize);
+
+ return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), iv, 0, ivSize);
+ }
+
+ /**
+ * Generate a key parameter for use with a MAC derived from the password,
+ * salt, and iteration count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ */
+ public CipherParameters generateDerivedMacParameters(
+ int keySize)
+ {
+ keySize = keySize / 8;
+
+ byte[] dKey = generateDerivedKey(MAC_MATERIAL, keySize);
+
+ return new KeyParameter(dKey, 0, keySize);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/PKCS5S1ParametersGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/PKCS5S1ParametersGenerator.java
new file mode 100644
index 00000000..1c62eccc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/PKCS5S1ParametersGenerator.java
@@ -0,0 +1,119 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.PBEParametersGenerator;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+
+/**
+ * Generator for PBE derived keys and ivs as defined by PKCS 5 V2.0 Scheme 1.
+ * Note this generator is limited to the size of the hash produced by the
+ * digest used to drive it.
+ * <p>
+ * The document this implementation is based on can be found at
+ * <a href=http://www.rsasecurity.com/rsalabs/pkcs/pkcs-5/index.html>
+ * RSA's PKCS5 Page</a>
+ */
+public class PKCS5S1ParametersGenerator
+ extends PBEParametersGenerator
+{
+ private Digest digest;
+
+ /**
+ * Construct a PKCS 5 Scheme 1 Parameters generator.
+ *
+ * @param digest the digest to be used as the source of derived keys.
+ */
+ public PKCS5S1ParametersGenerator(
+ Digest digest)
+ {
+ this.digest = digest;
+ }
+
+ /**
+ * the derived key function, the ith hash of the password and the salt.
+ */
+ private byte[] generateDerivedKey()
+ {
+ byte[] digestBytes = new byte[digest.getDigestSize()];
+
+ digest.update(password, 0, password.length);
+ digest.update(salt, 0, salt.length);
+
+ digest.doFinal(digestBytes, 0);
+ for (int i = 1; i < iterationCount; i++)
+ {
+ digest.update(digestBytes, 0, digestBytes.length);
+ digest.doFinal(digestBytes, 0);
+ }
+
+ return digestBytes;
+ }
+
+ /**
+ * Generate a key parameter derived from the password, salt, and iteration
+ * count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ * @exception IllegalArgumentException if the key length larger than the base hash size.
+ */
+ public CipherParameters generateDerivedParameters(
+ int keySize)
+ {
+ keySize = keySize / 8;
+
+ if (keySize > digest.getDigestSize())
+ {
+ throw new IllegalArgumentException(
+ "Can't generate a derived key " + keySize + " bytes long.");
+ }
+
+ byte[] dKey = generateDerivedKey();
+
+ return new KeyParameter(dKey, 0, keySize);
+ }
+
+ /**
+ * Generate a key with initialisation vector parameter derived from
+ * the password, salt, and iteration count we are currently initialised
+ * with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @param ivSize the size of the iv we want (in bits)
+ * @return a ParametersWithIV object.
+ * @exception IllegalArgumentException if keySize + ivSize is larger than the base hash size.
+ */
+ public CipherParameters generateDerivedParameters(
+ int keySize,
+ int ivSize)
+ {
+ keySize = keySize / 8;
+ ivSize = ivSize / 8;
+
+ if ((keySize + ivSize) > digest.getDigestSize())
+ {
+ throw new IllegalArgumentException(
+ "Can't generate a derived key " + (keySize + ivSize) + " bytes long.");
+ }
+
+ byte[] dKey = generateDerivedKey();
+
+ return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), dKey, keySize, ivSize);
+ }
+
+ /**
+ * Generate a key parameter for use with a MAC derived from the password,
+ * salt, and iteration count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ * @exception IllegalArgumentException if the key length larger than the base hash size.
+ */
+ public CipherParameters generateDerivedMacParameters(
+ int keySize)
+ {
+ return generateDerivedParameters(keySize);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java
new file mode 100644
index 00000000..640ead46
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java
@@ -0,0 +1,153 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.PBEParametersGenerator;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+
+/**
+ * Generator for PBE derived keys and ivs as defined by PKCS 5 V2.0 Scheme 2.
+ * This generator uses a SHA-1 HMac as the calculation function.
+ * <p>
+ * The document this implementation is based on can be found at
+ * <a href=http://www.rsasecurity.com/rsalabs/pkcs/pkcs-5/index.html>
+ * RSA's PKCS5 Page</a>
+ */
+public class PKCS5S2ParametersGenerator
+ extends PBEParametersGenerator
+{
+ private Mac hMac;
+ private byte[] state;
+
+ /**
+ * construct a PKCS5 Scheme 2 Parameters generator.
+ */
+ public PKCS5S2ParametersGenerator()
+ {
+ this(new SHA1Digest());
+ }
+
+ public PKCS5S2ParametersGenerator(Digest digest)
+ {
+ hMac = new HMac(digest);
+ state = new byte[hMac.getMacSize()];
+ }
+
+ private void F(
+ byte[] S,
+ int c,
+ byte[] iBuf,
+ byte[] out,
+ int outOff)
+ {
+ if (c == 0)
+ {
+ throw new IllegalArgumentException("iteration count must be at least 1.");
+ }
+
+ if (S != null)
+ {
+ hMac.update(S, 0, S.length);
+ }
+
+ hMac.update(iBuf, 0, iBuf.length);
+ hMac.doFinal(state, 0);
+
+ System.arraycopy(state, 0, out, outOff, state.length);
+
+ for (int count = 1; count < c; count++)
+ {
+ hMac.update(state, 0, state.length);
+ hMac.doFinal(state, 0);
+
+ for (int j = 0; j != state.length; j++)
+ {
+ out[outOff + j] ^= state[j];
+ }
+ }
+ }
+
+ private byte[] generateDerivedKey(
+ int dkLen)
+ {
+ int hLen = hMac.getMacSize();
+ int l = (dkLen + hLen - 1) / hLen;
+ byte[] iBuf = new byte[4];
+ byte[] outBytes = new byte[l * hLen];
+ int outPos = 0;
+
+ CipherParameters param = new KeyParameter(password);
+
+ hMac.init(param);
+
+ for (int i = 1; i <= l; i++)
+ {
+ // Increment the value in 'iBuf'
+ int pos = 3;
+ while (++iBuf[pos] == 0)
+ {
+ --pos;
+ }
+
+ F(salt, iterationCount, iBuf, outBytes, outPos);
+ outPos += hLen;
+ }
+
+ return outBytes;
+ }
+
+ /**
+ * Generate a key parameter derived from the password, salt, and iteration
+ * count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ */
+ public CipherParameters generateDerivedParameters(
+ int keySize)
+ {
+ keySize = keySize / 8;
+
+ byte[] dKey = generateDerivedKey(keySize);
+
+ return new KeyParameter(dKey, 0, keySize);
+ }
+
+ /**
+ * Generate a key with initialisation vector parameter derived from
+ * the password, salt, and iteration count we are currently initialised
+ * with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @param ivSize the size of the iv we want (in bits)
+ * @return a ParametersWithIV object.
+ */
+ public CipherParameters generateDerivedParameters(
+ int keySize,
+ int ivSize)
+ {
+ keySize = keySize / 8;
+ ivSize = ivSize / 8;
+
+ byte[] dKey = generateDerivedKey(keySize + ivSize);
+
+ return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), dKey, keySize, ivSize);
+ }
+
+ /**
+ * Generate a key parameter for use with a MAC derived from the password,
+ * salt, and iteration count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ */
+ public CipherParameters generateDerivedMacParameters(
+ int keySize)
+ {
+ return generateDerivedParameters(keySize);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/RSABlindingFactorGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/RSABlindingFactorGenerator.java
new file mode 100644
index 00000000..add67143
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/RSABlindingFactorGenerator.java
@@ -0,0 +1,77 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/**
+ * Generate a random factor suitable for use with RSA blind signatures
+ * as outlined in Chaum's blinding and unblinding as outlined in
+ * "Handbook of Applied Cryptography", page 475.
+ */
+public class RSABlindingFactorGenerator
+{
+ private static BigInteger ZERO = BigInteger.valueOf(0);
+ private static BigInteger ONE = BigInteger.valueOf(1);
+
+ private RSAKeyParameters key;
+ private SecureRandom random;
+
+ /**
+ * Initialise the factor generator
+ *
+ * @param param the necessary RSA key parameters.
+ */
+ public void init(
+ CipherParameters param)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ key = (RSAKeyParameters)rParam.getParameters();
+ random = rParam.getRandom();
+ }
+ else
+ {
+ key = (RSAKeyParameters)param;
+ random = new SecureRandom();
+ }
+
+ if (key instanceof RSAPrivateCrtKeyParameters)
+ {
+ throw new IllegalArgumentException("generator requires RSA public key");
+ }
+ }
+
+ /**
+ * Generate a suitable blind factor for the public key the generator was initialised with.
+ *
+ * @return a random blind factor
+ */
+ public BigInteger generateBlindingFactor()
+ {
+ if (key == null)
+ {
+ throw new IllegalStateException("generator not initialised");
+ }
+
+ BigInteger m = key.getModulus();
+ int length = m.bitLength() - 1; // must be less than m.bitLength()
+ BigInteger factor;
+ BigInteger gcd;
+
+ do
+ {
+ factor = new BigInteger(length, random);
+ gcd = factor.gcd(m);
+ }
+ while (factor.equals(ZERO) || factor.equals(ONE) || !gcd.equals(ONE));
+
+ return factor;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
new file mode 100644
index 00000000..f58069d5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
@@ -0,0 +1,147 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+
+import java.math.BigInteger;
+
+/**
+ * an RSA key pair generator.
+ */
+public class RSAKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ private RSAKeyGenerationParameters param;
+
+ public void init(
+ KeyGenerationParameters param)
+ {
+ this.param = (RSAKeyGenerationParameters)param;
+ }
+
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ BigInteger p, q, n, d, e, pSub1, qSub1, phi;
+
+ //
+ // p and q values should have a length of half the strength in bits
+ //
+ int strength = param.getStrength();
+ int pbitlength = (strength + 1) / 2;
+ int qbitlength = strength - pbitlength;
+ int mindiffbits = strength / 3;
+
+ e = param.getPublicExponent();
+
+ // TODO Consider generating safe primes for p, q (see DHParametersHelper.generateSafePrimes)
+ // (then p-1 and q-1 will not consist of only small factors - see "Pollard's algorithm")
+
+ //
+ // generate p, prime and (p-1) relatively prime to e
+ //
+ for (;;)
+ {
+ p = new BigInteger(pbitlength, 1, param.getRandom());
+
+ if (p.mod(e).equals(ONE))
+ {
+ continue;
+ }
+
+ if (!p.isProbablePrime(param.getCertainty()))
+ {
+ continue;
+ }
+
+ if (e.gcd(p.subtract(ONE)).equals(ONE))
+ {
+ break;
+ }
+ }
+
+ //
+ // generate a modulus of the required length
+ //
+ for (;;)
+ {
+ // generate q, prime and (q-1) relatively prime to e,
+ // and not equal to p
+ //
+ for (;;)
+ {
+ q = new BigInteger(qbitlength, 1, param.getRandom());
+
+ if (q.subtract(p).abs().bitLength() < mindiffbits)
+ {
+ continue;
+ }
+
+ if (q.mod(e).equals(ONE))
+ {
+ continue;
+ }
+
+ if (!q.isProbablePrime(param.getCertainty()))
+ {
+ continue;
+ }
+
+ if (e.gcd(q.subtract(ONE)).equals(ONE))
+ {
+ break;
+ }
+ }
+
+ //
+ // calculate the modulus
+ //
+ n = p.multiply(q);
+
+ if (n.bitLength() == param.getStrength())
+ {
+ break;
+ }
+
+ //
+ // if we get here our primes aren't big enough, make the largest
+ // of the two p and try again
+ //
+ p = p.max(q);
+ }
+
+ if (p.compareTo(q) < 0)
+ {
+ phi = p;
+ p = q;
+ q = phi;
+ }
+
+ pSub1 = p.subtract(ONE);
+ qSub1 = q.subtract(ONE);
+ phi = pSub1.multiply(qSub1);
+
+ //
+ // calculate the private exponent
+ //
+ d = e.modInverse(phi);
+
+ //
+ // calculate the CRT factors
+ //
+ BigInteger dP, dQ, qInv;
+
+ dP = d.remainder(pSub1);
+ dQ = d.remainder(qSub1);
+ qInv = q.modInverse(p);
+
+ return new AsymmetricCipherKeyPair(
+ new RSAKeyParameters(false, n, e),
+ new RSAPrivateCrtKeyParameters(n, e, d, p, q, dP, dQ, qInv));
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/SCrypt.java b/core/src/main/java/org/bouncycastle/crypto/generators/SCrypt.java
new file mode 100644
index 00000000..da22fa48
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/generators/SCrypt.java
@@ -0,0 +1,147 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.PBEParametersGenerator;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.engines.Salsa20Engine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.util.Pack;
+import org.bouncycastle.util.Arrays;
+
+public class SCrypt
+{
+ // TODO Validate arguments
+ public static byte[] generate(byte[] P, byte[] S, int N, int r, int p, int dkLen)
+ {
+ return MFcrypt(P, S, N, r, p, dkLen);
+ }
+
+ private static byte[] MFcrypt(byte[] P, byte[] S, int N, int r, int p, int dkLen)
+ {
+ int MFLenBytes = r * 128;
+ byte[] bytes = SingleIterationPBKDF2(P, S, p * MFLenBytes);
+
+ int[] B = null;
+
+ try
+ {
+ int BLen = bytes.length >>> 2;
+ B = new int[BLen];
+
+ Pack.littleEndianToInt(bytes, 0, B);
+
+ int MFLenWords = MFLenBytes >>> 2;
+ for (int BOff = 0; BOff < BLen; BOff += MFLenWords)
+ {
+ // TODO These can be done in parallel threads
+ SMix(B, BOff, N, r);
+ }
+
+ Pack.intToLittleEndian(B, bytes, 0);
+
+ return SingleIterationPBKDF2(P, bytes, dkLen);
+ }
+ finally
+ {
+ Clear(bytes);
+ Clear(B);
+ }
+ }
+
+ private static byte[] SingleIterationPBKDF2(byte[] P, byte[] S, int dkLen)
+ {
+ PBEParametersGenerator pGen = new PKCS5S2ParametersGenerator(new SHA256Digest());
+ pGen.init(P, S, 1);
+ KeyParameter key = (KeyParameter) pGen.generateDerivedMacParameters(dkLen * 8);
+ return key.getKey();
+ }
+
+ private static void SMix(int[] B, int BOff, int N, int r)
+ {
+ int BCount = r * 32;
+
+ int[] blockX1 = new int[16];
+ int[] blockX2 = new int[16];
+ int[] blockY = new int[BCount];
+
+ int[] X = new int[BCount];
+ int[][] V = new int[N][];
+
+ try
+ {
+ System.arraycopy(B, BOff, X, 0, BCount);
+
+ for (int i = 0; i < N; ++i)
+ {
+ V[i] = Arrays.clone(X);
+ BlockMix(X, blockX1, blockX2, blockY, r);
+ }
+
+ int mask = N - 1;
+ for (int i = 0; i < N; ++i)
+ {
+ int j = X[BCount - 16] & mask;
+ Xor(X, V[j], 0, X);
+ BlockMix(X, blockX1, blockX2, blockY, r);
+ }
+
+ System.arraycopy(X, 0, B, BOff, BCount);
+ }
+ finally
+ {
+ ClearAll(V);
+ ClearAll(new int[][]{ X, blockX1, blockX2, blockY });
+ }
+ }
+
+ private static void BlockMix(int[] B, int[] X1, int[] X2, int[] Y, int r)
+ {
+ System.arraycopy(B, B.length - 16, X1, 0, 16);
+
+ int BOff = 0, YOff = 0, halfLen = B.length >>> 1;
+
+ for (int i = 2 * r; i > 0; --i)
+ {
+ Xor(X1, B, BOff, X2);
+
+ Salsa20Engine.salsaCore(8, X2, X1);
+ System.arraycopy(X1, 0, Y, YOff, 16);
+
+ YOff = halfLen + BOff - YOff;
+ BOff += 16;
+ }
+
+ System.arraycopy(Y, 0, B, 0, Y.length);
+ }
+
+ private static void Xor(int[] a, int[] b, int bOff, int[] output)
+ {
+ for (int i = output.length - 1; i >= 0; --i)
+ {
+ output[i] = a[i] ^ b[bOff + i];
+ }
+ }
+
+ private static void Clear(byte[] array)
+ {
+ if (array != null)
+ {
+ Arrays.fill(array, (byte)0);
+ }
+ }
+
+ private static void Clear(int[] array)
+ {
+ if (array != null)
+ {
+ Arrays.fill(array, 0);
+ }
+ }
+
+ private static void ClearAll(int[][] arrays)
+ {
+ for (int i = 0; i < arrays.length; ++i)
+ {
+ Clear(arrays[i]);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java b/core/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java
new file mode 100644
index 00000000..bb09a76b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java
@@ -0,0 +1,244 @@
+package org.bouncycastle.crypto.io;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.StreamCipher;
+
+/**
+ * A CipherInputStream is composed of an InputStream and a BufferedBlockCipher so
+ * that read() methods return data that are read in from the
+ * underlying InputStream but have been additionally processed by the
+ * Cipher. The Cipher must be fully initialized before being used by
+ * a CipherInputStream.
+ * <p>
+ * For example, if the Cipher is initialized for decryption, the
+ * CipherInputStream will attempt to read in data and decrypt them,
+ * before returning the decrypted data.
+ */
+public class CipherInputStream
+ extends FilterInputStream
+{
+ private BufferedBlockCipher bufferedBlockCipher;
+ private StreamCipher streamCipher;
+
+ private byte[] buf;
+ private byte[] inBuf;
+
+ private int bufOff;
+ private int maxBuf;
+ private boolean finalized;
+
+ private static final int INPUT_BUF_SIZE = 2048;
+
+ /**
+ * Constructs a CipherInputStream from an InputStream and a
+ * BufferedBlockCipher.
+ */
+ public CipherInputStream(
+ InputStream is,
+ BufferedBlockCipher cipher)
+ {
+ super(is);
+
+ this.bufferedBlockCipher = cipher;
+
+ buf = new byte[cipher.getOutputSize(INPUT_BUF_SIZE)];
+ inBuf = new byte[INPUT_BUF_SIZE];
+ }
+
+ public CipherInputStream(
+ InputStream is,
+ StreamCipher cipher)
+ {
+ super(is);
+
+ this.streamCipher = cipher;
+
+ buf = new byte[INPUT_BUF_SIZE];
+ inBuf = new byte[INPUT_BUF_SIZE];
+ }
+
+ /**
+ * grab the next chunk of input from the underlying input stream
+ */
+ private int nextChunk()
+ throws IOException
+ {
+ int available = super.available();
+
+ // must always try to read 1 byte!
+ // some buggy InputStreams return < 0!
+ if (available <= 0)
+ {
+ available = 1;
+ }
+
+ if (available > inBuf.length)
+ {
+ available = super.read(inBuf, 0, inBuf.length);
+ }
+ else
+ {
+ available = super.read(inBuf, 0, available);
+ }
+
+ if (available < 0)
+ {
+ if (finalized)
+ {
+ return -1;
+ }
+
+ try
+ {
+ if (bufferedBlockCipher != null)
+ {
+ maxBuf = bufferedBlockCipher.doFinal(buf, 0);
+ }
+ else
+ {
+ maxBuf = 0; // a stream cipher
+ }
+ }
+ catch (Exception e)
+ {
+ throw new IOException("error processing stream: " + e.toString());
+ }
+
+ bufOff = 0;
+
+ finalized = true;
+
+ if (bufOff == maxBuf)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ bufOff = 0;
+
+ try
+ {
+ if (bufferedBlockCipher != null)
+ {
+ maxBuf = bufferedBlockCipher.processBytes(inBuf, 0, available, buf, 0);
+ }
+ else
+ {
+ streamCipher.processBytes(inBuf, 0, available, buf, 0);
+ maxBuf = available;
+ }
+ }
+ catch (Exception e)
+ {
+ throw new IOException("error processing stream: " + e.toString());
+ }
+
+ if (maxBuf == 0) // not enough bytes read for first block...
+ {
+ return nextChunk();
+ }
+ }
+
+ return maxBuf;
+ }
+
+ public int read()
+ throws IOException
+ {
+ if (bufOff == maxBuf)
+ {
+ if (nextChunk() < 0)
+ {
+ return -1;
+ }
+ }
+
+ return buf[bufOff++] & 0xff;
+ }
+
+ public int read(
+ byte[] b)
+ throws IOException
+ {
+ return read(b, 0, b.length);
+ }
+
+ public int read(
+ byte[] b,
+ int off,
+ int len)
+ throws IOException
+ {
+ if (bufOff == maxBuf)
+ {
+ if (nextChunk() < 0)
+ {
+ return -1;
+ }
+ }
+
+ int available = maxBuf - bufOff;
+
+ if (len > available)
+ {
+ System.arraycopy(buf, bufOff, b, off, available);
+ bufOff = maxBuf;
+
+ return available;
+ }
+ else
+ {
+ System.arraycopy(buf, bufOff, b, off, len);
+ bufOff += len;
+
+ return len;
+ }
+ }
+
+ public long skip(
+ long n)
+ throws IOException
+ {
+ if (n <= 0)
+ {
+ return 0;
+ }
+
+ int available = maxBuf - bufOff;
+
+ if (n > available)
+ {
+ bufOff = maxBuf;
+
+ return available;
+ }
+ else
+ {
+ bufOff += (int)n;
+
+ return (int)n;
+ }
+ }
+
+ public int available()
+ throws IOException
+ {
+ return maxBuf - bufOff;
+ }
+
+ public void close()
+ throws IOException
+ {
+ super.close();
+ }
+
+ public boolean markSupported()
+ {
+ return false;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java b/core/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java
new file mode 100644
index 00000000..17a7b6d8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java
@@ -0,0 +1,188 @@
+package org.bouncycastle.crypto.io;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.StreamCipher;
+
+public class CipherOutputStream
+ extends FilterOutputStream
+{
+ private BufferedBlockCipher bufferedBlockCipher;
+ private StreamCipher streamCipher;
+
+ private byte[] oneByte = new byte[1];
+ private byte[] buf;
+
+ /**
+ * Constructs a CipherOutputStream from an OutputStream and a
+ * BufferedBlockCipher.
+ */
+ public CipherOutputStream(
+ OutputStream os,
+ BufferedBlockCipher cipher)
+ {
+ super(os);
+ this.bufferedBlockCipher = cipher;
+ this.buf = new byte[cipher.getBlockSize()];
+ }
+
+ /**
+ * Constructs a CipherOutputStream from an OutputStream and a
+ * BufferedBlockCipher.
+ */
+ public CipherOutputStream(
+ OutputStream os,
+ StreamCipher cipher)
+ {
+ super(os);
+ this.streamCipher = cipher;
+ }
+
+ /**
+ * Writes the specified byte to this output stream.
+ *
+ * @param b the <code>byte</code>.
+ * @exception java.io.IOException if an I/O error occurs.
+ */
+ public void write(
+ int b)
+ throws IOException
+ {
+ oneByte[0] = (byte)b;
+
+ if (bufferedBlockCipher != null)
+ {
+ int len = bufferedBlockCipher.processBytes(oneByte, 0, 1, buf, 0);
+
+ if (len != 0)
+ {
+ out.write(buf, 0, len);
+ }
+ }
+ else
+ {
+ out.write(streamCipher.returnByte((byte)b));
+ }
+ }
+
+ /**
+ * Writes <code>b.length</code> bytes from the specified byte array
+ * to this output stream.
+ * <p>
+ * The <code>write</code> method of
+ * <code>CipherOutputStream</code> calls the <code>write</code>
+ * method of three arguments with the three arguments
+ * <code>b</code>, <code>0</code>, and <code>b.length</code>.
+ *
+ * @param b the data.
+ * @exception java.io.IOException if an I/O error occurs.
+ * @see #write(byte[], int, int)
+ */
+ public void write(
+ byte[] b)
+ throws IOException
+ {
+ write(b, 0, b.length);
+ }
+
+ /**
+ * Writes <code>len</code> bytes from the specified byte array
+ * starting at offset <code>off</code> to this output stream.
+ *
+ * @param b the data.
+ * @param off the start offset in the data.
+ * @param len the number of bytes to write.
+ * @exception java.io.IOException if an I/O error occurs.
+ */
+ public void write(
+ byte[] b,
+ int off,
+ int len)
+ throws IOException
+ {
+ if (bufferedBlockCipher != null)
+ {
+ byte[] buf = new byte[bufferedBlockCipher.getOutputSize(len)];
+
+ int outLen = bufferedBlockCipher.processBytes(b, off, len, buf, 0);
+
+ if (outLen != 0)
+ {
+ out.write(buf, 0, outLen);
+ }
+ }
+ else
+ {
+ byte[] buf = new byte[len];
+
+ streamCipher.processBytes(b, off, len, buf, 0);
+
+ out.write(buf, 0, len);
+ }
+ }
+
+ /**
+ * Flushes this output stream by forcing any buffered output bytes
+ * that have already been processed by the encapsulated cipher object
+ * to be written out.
+ *
+ * <p>
+ * Any bytes buffered by the encapsulated cipher
+ * and waiting to be processed by it will not be written out. For example,
+ * if the encapsulated cipher is a block cipher, and the total number of
+ * bytes written using one of the <code>write</code> methods is less than
+ * the cipher's block size, no bytes will be written out.
+ *
+ * @exception java.io.IOException if an I/O error occurs.
+ */
+ public void flush()
+ throws IOException
+ {
+ super.flush();
+ }
+
+ /**
+ * Closes this output stream and releases any system resources
+ * associated with this stream.
+ * <p>
+ * This method invokes the <code>doFinal</code> method of the encapsulated
+ * cipher object, which causes any bytes buffered by the encapsulated
+ * cipher to be processed. The result is written out by calling the
+ * <code>flush</code> method of this output stream.
+ * <p>
+ * This method resets the encapsulated cipher object to its initial state
+ * and calls the <code>close</code> method of the underlying output
+ * stream.
+ *
+ * @exception java.io.IOException if an I/O error occurs.
+ */
+ public void close()
+ throws IOException
+ {
+ try
+ {
+ if (bufferedBlockCipher != null)
+ {
+ byte[] buf = new byte[bufferedBlockCipher.getOutputSize(0)];
+
+ int outLen = bufferedBlockCipher.doFinal(buf, 0);
+
+ if (outLen != 0)
+ {
+ out.write(buf, 0, outLen);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ throw new IOException("Error closing stream: " + e.toString());
+ }
+
+ flush();
+
+ super.close();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/io/DigestInputStream.java b/core/src/main/java/org/bouncycastle/crypto/io/DigestInputStream.java
new file mode 100644
index 00000000..ef0b03e2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/io/DigestInputStream.java
@@ -0,0 +1,52 @@
+package org.bouncycastle.crypto.io;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.crypto.Digest;
+
+public class DigestInputStream
+ extends FilterInputStream
+{
+ protected Digest digest;
+
+ public DigestInputStream(
+ InputStream stream,
+ Digest digest)
+ {
+ super(stream);
+ this.digest = digest;
+ }
+
+ public int read()
+ throws IOException
+ {
+ int b = in.read();
+
+ if (b >= 0)
+ {
+ digest.update((byte)b);
+ }
+ return b;
+ }
+
+ public int read(
+ byte[] b,
+ int off,
+ int len)
+ throws IOException
+ {
+ int n = in.read(b, off, len);
+ if (n > 0)
+ {
+ digest.update(b, off, n);
+ }
+ return n;
+ }
+
+ public Digest getDigest()
+ {
+ return digest;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/io/DigestOutputStream.java b/core/src/main/java/org/bouncycastle/crypto/io/DigestOutputStream.java
new file mode 100644
index 00000000..23c7e539
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/io/DigestOutputStream.java
@@ -0,0 +1,42 @@
+package org.bouncycastle.crypto.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.bouncycastle.crypto.Digest;
+
+public class DigestOutputStream
+ extends OutputStream
+{
+ protected Digest digest;
+
+ public DigestOutputStream(
+ Digest Digest)
+ {
+ this.digest = Digest;
+ }
+
+ public void write(int b)
+ throws IOException
+ {
+ digest.update((byte)b);
+ }
+
+ public void write(
+ byte[] b,
+ int off,
+ int len)
+ throws IOException
+ {
+ digest.update(b, off, len);
+ }
+
+ public byte[] getDigest()
+ {
+ byte[] res = new byte[digest.getDigestSize()];
+
+ digest.doFinal(res, 0);
+
+ return res;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/io/MacInputStream.java b/core/src/main/java/org/bouncycastle/crypto/io/MacInputStream.java
new file mode 100644
index 00000000..b78548c6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/io/MacInputStream.java
@@ -0,0 +1,52 @@
+package org.bouncycastle.crypto.io;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.crypto.Mac;
+
+public class MacInputStream
+ extends FilterInputStream
+{
+ protected Mac mac;
+
+ public MacInputStream(
+ InputStream stream,
+ Mac mac)
+ {
+ super(stream);
+ this.mac = mac;
+ }
+
+ public int read()
+ throws IOException
+ {
+ int b = in.read();
+
+ if (b >= 0)
+ {
+ mac.update((byte)b);
+ }
+ return b;
+ }
+
+ public int read(
+ byte[] b,
+ int off,
+ int len)
+ throws IOException
+ {
+ int n = in.read(b, off, len);
+ if (n >= 0)
+ {
+ mac.update(b, off, n);
+ }
+ return n;
+ }
+
+ public Mac getMac()
+ {
+ return mac;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/io/MacOutputStream.java b/core/src/main/java/org/bouncycastle/crypto/io/MacOutputStream.java
new file mode 100644
index 00000000..0f0e7dbe
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/io/MacOutputStream.java
@@ -0,0 +1,42 @@
+package org.bouncycastle.crypto.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.bouncycastle.crypto.Mac;
+
+public class MacOutputStream
+ extends OutputStream
+{
+ protected Mac mac;
+
+ public MacOutputStream(
+ Mac mac)
+ {
+ this.mac = mac;
+ }
+
+ public void write(int b)
+ throws IOException
+ {
+ mac.update((byte)b);
+ }
+
+ public void write(
+ byte[] b,
+ int off,
+ int len)
+ throws IOException
+ {
+ mac.update(b, off, len);
+ }
+
+ public byte[] getMac()
+ {
+ byte[] res = new byte[mac.getMacSize()];
+
+ mac.doFinal(res, 0);
+
+ return res;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/io/SignerInputStream.java b/core/src/main/java/org/bouncycastle/crypto/io/SignerInputStream.java
new file mode 100644
index 00000000..9583e4cf
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/io/SignerInputStream.java
@@ -0,0 +1,52 @@
+package org.bouncycastle.crypto.io;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.crypto.Signer;
+
+public class SignerInputStream
+ extends FilterInputStream
+{
+ protected Signer signer;
+
+ public SignerInputStream(
+ InputStream stream,
+ Signer signer)
+ {
+ super(stream);
+ this.signer = signer;
+ }
+
+ public int read()
+ throws IOException
+ {
+ int b = in.read();
+
+ if (b >= 0)
+ {
+ signer.update((byte)b);
+ }
+ return b;
+ }
+
+ public int read(
+ byte[] b,
+ int off,
+ int len)
+ throws IOException
+ {
+ int n = in.read(b, off, len);
+ if (n > 0)
+ {
+ signer.update(b, off, n);
+ }
+ return n;
+ }
+
+ public Signer getSigner()
+ {
+ return signer;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/io/SignerOutputStream.java b/core/src/main/java/org/bouncycastle/crypto/io/SignerOutputStream.java
new file mode 100644
index 00000000..1c21b5dc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/io/SignerOutputStream.java
@@ -0,0 +1,38 @@
+package org.bouncycastle.crypto.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.bouncycastle.crypto.Signer;
+
+public class SignerOutputStream
+ extends OutputStream
+{
+ protected Signer signer;
+
+ public SignerOutputStream(
+ Signer Signer)
+ {
+ this.signer = Signer;
+ }
+
+ public void write(int b)
+ throws IOException
+ {
+ signer.update((byte)b);
+ }
+
+ public void write(
+ byte[] b,
+ int off,
+ int len)
+ throws IOException
+ {
+ signer.update(b, off, len);
+ }
+
+ public Signer getSigner()
+ {
+ return signer;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java b/core/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java
new file mode 100755
index 00000000..f4dfc6ed
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java
@@ -0,0 +1,256 @@
+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 static final BigInteger ONE = BigInteger.valueOf(1);
+
+ 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(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/core/src/main/java/org/bouncycastle/crypto/kems/RSAKeyEncapsulation.java b/core/src/main/java/org/bouncycastle/crypto/kems/RSAKeyEncapsulation.java
new file mode 100755
index 00000000..8c1a1724
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/kems/RSAKeyEncapsulation.java
@@ -0,0 +1,164 @@
+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 static final BigInteger ZERO = BigInteger.valueOf(0);
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ 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(ZERO, n.subtract(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/core/src/main/java/org/bouncycastle/crypto/macs/BlockCipherMac.java b/core/src/main/java/org/bouncycastle/crypto/macs/BlockCipherMac.java
new file mode 100644
index 00000000..6de39a85
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/macs/BlockCipherMac.java
@@ -0,0 +1,174 @@
+package org.bouncycastle.crypto.macs;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+
+public class BlockCipherMac
+ implements Mac
+{
+ private byte[] mac;
+
+ private byte[] buf;
+ private int bufOff;
+ private BlockCipher cipher;
+
+ private int macSize;
+
+ /**
+ * create a standard MAC based on a block cipher. This will produce an
+ * authentication code half the length of the block size of the cipher.
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @deprecated use CBCBlockCipherMac
+ */
+ public BlockCipherMac(
+ BlockCipher cipher)
+ {
+ this(cipher, (cipher.getBlockSize() * 8) / 2);
+ }
+
+ /**
+ * create a standard MAC based on a block cipher with the size of the
+ * MAC been given in bits.
+ * <p>
+ * Note: the size of the MAC must be at least 16 bits (FIPS Publication 113),
+ * and in general should be less than the size of the block cipher as it reduces
+ * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
+ * @deprecated use CBCBlockCipherMac
+ */
+ public BlockCipherMac(
+ BlockCipher cipher,
+ int macSizeInBits)
+ {
+ if ((macSizeInBits % 8) != 0)
+ {
+ throw new IllegalArgumentException("MAC size must be multiple of 8");
+ }
+
+ this.cipher = new CBCBlockCipher(cipher);
+ this.macSize = macSizeInBits / 8;
+
+ mac = new byte[cipher.getBlockSize()];
+
+ buf = new byte[cipher.getBlockSize()];
+ bufOff = 0;
+ }
+
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName();
+ }
+
+ public void init(
+ CipherParameters params)
+ {
+ reset();
+
+ cipher.init(true, params);
+ }
+
+ public int getMacSize()
+ {
+ return macSize;
+ }
+
+ public void update(
+ byte in)
+ {
+ if (bufOff == buf.length)
+ {
+ cipher.processBlock(buf, 0, mac, 0);
+ bufOff = 0;
+ }
+
+ buf[bufOff++] = in;
+ }
+
+ public void update(
+ byte[] in,
+ int inOff,
+ int len)
+ {
+ if (len < 0)
+ {
+ throw new IllegalArgumentException("Can't have a negative input length!");
+ }
+
+ int blockSize = cipher.getBlockSize();
+ int resultLen = 0;
+ int gapLen = blockSize - bufOff;
+
+ if (len > gapLen)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+ resultLen += cipher.processBlock(buf, 0, mac, 0);
+
+ bufOff = 0;
+ len -= gapLen;
+ inOff += gapLen;
+
+ while (len > blockSize)
+ {
+ resultLen += cipher.processBlock(in, inOff, mac, 0);
+
+ len -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ System.arraycopy(in, inOff, buf, bufOff, len);
+
+ bufOff += len;
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ int blockSize = cipher.getBlockSize();
+
+ //
+ // pad with zeroes
+ //
+ while (bufOff < blockSize)
+ {
+ buf[bufOff] = 0;
+ bufOff++;
+ }
+
+ cipher.processBlock(buf, 0, mac, 0);
+
+ System.arraycopy(mac, 0, out, outOff, macSize);
+
+ reset();
+
+ return macSize;
+ }
+
+ /**
+ * Reset the mac generator.
+ */
+ public void reset()
+ {
+ /*
+ * clean the buffer.
+ */
+ for (int i = 0; i < buf.length; i++)
+ {
+ buf[i] = 0;
+ }
+
+ bufOff = 0;
+
+ /*
+ * reset the underlying cipher.
+ */
+ cipher.reset();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java b/core/src/main/java/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java
new file mode 100644
index 00000000..9bf6cb0e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java
@@ -0,0 +1,229 @@
+package org.bouncycastle.crypto.macs;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.paddings.BlockCipherPadding;
+
+/**
+ * standard CBC Block Cipher MAC - if no padding is specified the default of
+ * pad of zeroes is used.
+ */
+public class CBCBlockCipherMac
+ implements Mac
+{
+ private byte[] mac;
+
+ private byte[] buf;
+ private int bufOff;
+ private BlockCipher cipher;
+ private BlockCipherPadding padding;
+
+ private int macSize;
+
+ /**
+ * create a standard MAC based on a CBC block cipher. This will produce an
+ * authentication code half the length of the block size of the cipher.
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ */
+ public CBCBlockCipherMac(
+ BlockCipher cipher)
+ {
+ this(cipher, (cipher.getBlockSize() * 8) / 2, null);
+ }
+
+ /**
+ * create a standard MAC based on a CBC block cipher. This will produce an
+ * authentication code half the length of the block size of the cipher.
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param padding the padding to be used to complete the last block.
+ */
+ public CBCBlockCipherMac(
+ BlockCipher cipher,
+ BlockCipherPadding padding)
+ {
+ this(cipher, (cipher.getBlockSize() * 8) / 2, padding);
+ }
+
+ /**
+ * create a standard MAC based on a block cipher with the size of the
+ * MAC been given in bits. This class uses CBC mode as the basis for the
+ * MAC generation.
+ * <p>
+ * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
+ * or 16 bits if being used as a data authenticator (FIPS Publication 113),
+ * and in general should be less than the size of the block cipher as it reduces
+ * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
+ */
+ public CBCBlockCipherMac(
+ BlockCipher cipher,
+ int macSizeInBits)
+ {
+ this(cipher, macSizeInBits, null);
+ }
+
+ /**
+ * create a standard MAC based on a block cipher with the size of the
+ * MAC been given in bits. This class uses CBC mode as the basis for the
+ * MAC generation.
+ * <p>
+ * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
+ * or 16 bits if being used as a data authenticator (FIPS Publication 113),
+ * and in general should be less than the size of the block cipher as it reduces
+ * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
+ * @param padding the padding to be used to complete the last block.
+ */
+ public CBCBlockCipherMac(
+ BlockCipher cipher,
+ int macSizeInBits,
+ BlockCipherPadding padding)
+ {
+ if ((macSizeInBits % 8) != 0)
+ {
+ throw new IllegalArgumentException("MAC size must be multiple of 8");
+ }
+
+ this.cipher = new CBCBlockCipher(cipher);
+ this.padding = padding;
+ this.macSize = macSizeInBits / 8;
+
+ mac = new byte[cipher.getBlockSize()];
+
+ buf = new byte[cipher.getBlockSize()];
+ bufOff = 0;
+ }
+
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName();
+ }
+
+ public void init(
+ CipherParameters params)
+ {
+ reset();
+
+ cipher.init(true, params);
+ }
+
+ public int getMacSize()
+ {
+ return macSize;
+ }
+
+ public void update(
+ byte in)
+ {
+ if (bufOff == buf.length)
+ {
+ cipher.processBlock(buf, 0, mac, 0);
+ bufOff = 0;
+ }
+
+ buf[bufOff++] = in;
+ }
+
+ public void update(
+ byte[] in,
+ int inOff,
+ int len)
+ {
+ if (len < 0)
+ {
+ throw new IllegalArgumentException("Can't have a negative input length!");
+ }
+
+ int blockSize = cipher.getBlockSize();
+ int gapLen = blockSize - bufOff;
+
+ if (len > gapLen)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+ cipher.processBlock(buf, 0, mac, 0);
+
+ bufOff = 0;
+ len -= gapLen;
+ inOff += gapLen;
+
+ while (len > blockSize)
+ {
+ cipher.processBlock(in, inOff, mac, 0);
+
+ len -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ System.arraycopy(in, inOff, buf, bufOff, len);
+
+ bufOff += len;
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ int blockSize = cipher.getBlockSize();
+
+ if (padding == null)
+ {
+ //
+ // pad with zeroes
+ //
+ while (bufOff < blockSize)
+ {
+ buf[bufOff] = 0;
+ bufOff++;
+ }
+ }
+ else
+ {
+ if (bufOff == blockSize)
+ {
+ cipher.processBlock(buf, 0, mac, 0);
+ bufOff = 0;
+ }
+
+ padding.addPadding(buf, bufOff);
+ }
+
+ cipher.processBlock(buf, 0, mac, 0);
+
+ System.arraycopy(mac, 0, out, outOff, macSize);
+
+ reset();
+
+ return macSize;
+ }
+
+ /**
+ * Reset the mac generator.
+ */
+ public void reset()
+ {
+ /*
+ * clean the buffer.
+ */
+ for (int i = 0; i < buf.length; i++)
+ {
+ buf[i] = 0;
+ }
+
+ bufOff = 0;
+
+ /*
+ * reset the underlying cipher.
+ */
+ cipher.reset();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/macs/CFBBlockCipherMac.java b/core/src/main/java/org/bouncycastle/crypto/macs/CFBBlockCipherMac.java
new file mode 100644
index 00000000..d7ad6126
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/macs/CFBBlockCipherMac.java
@@ -0,0 +1,388 @@
+package org.bouncycastle.crypto.macs;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.paddings.BlockCipherPadding;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+
+/**
+ * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher.
+ */
+class MacCFBBlockCipher
+{
+ private byte[] IV;
+ private byte[] cfbV;
+ private byte[] cfbOutV;
+
+ private int blockSize;
+ private BlockCipher cipher = null;
+
+ /**
+ * Basic constructor.
+ *
+ * @param cipher the block cipher to be used as the basis of the
+ * feedback mode.
+ * @param blockSize the block size in bits (note: a multiple of 8)
+ */
+ public MacCFBBlockCipher(
+ BlockCipher cipher,
+ int bitBlockSize)
+ {
+ this.cipher = cipher;
+ this.blockSize = bitBlockSize / 8;
+
+ this.IV = new byte[cipher.getBlockSize()];
+ this.cfbV = new byte[cipher.getBlockSize()];
+ this.cfbOutV = new byte[cipher.getBlockSize()];
+ }
+
+ /**
+ * Initialise the cipher and, possibly, the initialisation vector (IV).
+ * If an IV isn't passed as part of the parameter, the IV will be all zeros.
+ * An IV which is too short is handled in FIPS compliant fashion.
+ *
+ * @param param the key and other data required by the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ CipherParameters params)
+ throws IllegalArgumentException
+ {
+ if (params instanceof ParametersWithIV)
+ {
+ ParametersWithIV ivParam = (ParametersWithIV)params;
+ byte[] iv = ivParam.getIV();
+
+ if (iv.length < IV.length)
+ {
+ System.arraycopy(iv, 0, IV, IV.length - iv.length, iv.length);
+ }
+ else
+ {
+ System.arraycopy(iv, 0, IV, 0, IV.length);
+ }
+
+ reset();
+
+ cipher.init(true, ivParam.getParameters());
+ }
+ else
+ {
+ reset();
+
+ cipher.init(true, params);
+ }
+ }
+
+ /**
+ * return the algorithm name and mode.
+ *
+ * @return the name of the underlying algorithm followed by "/CFB"
+ * and the block size in bits.
+ */
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName() + "/CFB" + (blockSize * 8);
+ }
+
+ /**
+ * return the block size we are operating at.
+ *
+ * @return the block size we are operating at (in bytes).
+ */
+ public int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ /**
+ * Process one block of input from the array in and write it to
+ * the out array.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ cipher.processBlock(cfbV, 0, cfbOutV, 0);
+
+ //
+ // XOR the cfbV with the plaintext producing the cipher text
+ //
+ for (int i = 0; i < blockSize; i++)
+ {
+ out[outOff + i] = (byte)(cfbOutV[i] ^ in[inOff + i]);
+ }
+
+ //
+ // change over the input block.
+ //
+ System.arraycopy(cfbV, blockSize, cfbV, 0, cfbV.length - blockSize);
+ System.arraycopy(out, outOff, cfbV, cfbV.length - blockSize, blockSize);
+
+ return blockSize;
+ }
+
+ /**
+ * reset the chaining vector back to the IV and reset the underlying
+ * cipher.
+ */
+ public void reset()
+ {
+ System.arraycopy(IV, 0, cfbV, 0, IV.length);
+
+ cipher.reset();
+ }
+
+ void getMacBlock(
+ byte[] mac)
+ {
+ cipher.processBlock(cfbV, 0, mac, 0);
+ }
+}
+
+public class CFBBlockCipherMac
+ implements Mac
+{
+ private byte[] mac;
+
+ private byte[] buf;
+ private int bufOff;
+ private MacCFBBlockCipher cipher;
+ private BlockCipherPadding padding = null;
+
+
+ private int macSize;
+
+ /**
+ * create a standard MAC based on a CFB block cipher. This will produce an
+ * authentication code half the length of the block size of the cipher, with
+ * the CFB mode set to 8 bits.
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ */
+ public CFBBlockCipherMac(
+ BlockCipher cipher)
+ {
+ this(cipher, 8, (cipher.getBlockSize() * 8) / 2, null);
+ }
+
+ /**
+ * create a standard MAC based on a CFB block cipher. This will produce an
+ * authentication code half the length of the block size of the cipher, with
+ * the CFB mode set to 8 bits.
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param padding the padding to be used.
+ */
+ public CFBBlockCipherMac(
+ BlockCipher cipher,
+ BlockCipherPadding padding)
+ {
+ this(cipher, 8, (cipher.getBlockSize() * 8) / 2, padding);
+ }
+
+ /**
+ * create a standard MAC based on a block cipher with the size of the
+ * MAC been given in bits. This class uses CFB mode as the basis for the
+ * MAC generation.
+ * <p>
+ * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
+ * or 16 bits if being used as a data authenticator (FIPS Publication 113),
+ * and in general should be less than the size of the block cipher as it reduces
+ * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param cfbBitSize the size of an output block produced by the CFB mode.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
+ */
+ public CFBBlockCipherMac(
+ BlockCipher cipher,
+ int cfbBitSize,
+ int macSizeInBits)
+ {
+ this(cipher, cfbBitSize, macSizeInBits, null);
+ }
+
+ /**
+ * create a standard MAC based on a block cipher with the size of the
+ * MAC been given in bits. This class uses CFB mode as the basis for the
+ * MAC generation.
+ * <p>
+ * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
+ * or 16 bits if being used as a data authenticator (FIPS Publication 113),
+ * and in general should be less than the size of the block cipher as it reduces
+ * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param cfbBitSize the size of an output block produced by the CFB mode.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
+ * @param padding a padding to be used.
+ */
+ public CFBBlockCipherMac(
+ BlockCipher cipher,
+ int cfbBitSize,
+ int macSizeInBits,
+ BlockCipherPadding padding)
+ {
+ if ((macSizeInBits % 8) != 0)
+ {
+ throw new IllegalArgumentException("MAC size must be multiple of 8");
+ }
+
+ mac = new byte[cipher.getBlockSize()];
+
+ this.cipher = new MacCFBBlockCipher(cipher, cfbBitSize);
+ this.padding = padding;
+ this.macSize = macSizeInBits / 8;
+
+ buf = new byte[this.cipher.getBlockSize()];
+ bufOff = 0;
+ }
+
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName();
+ }
+
+ public void init(
+ CipherParameters params)
+ {
+ reset();
+
+ cipher.init(params);
+ }
+
+ public int getMacSize()
+ {
+ return macSize;
+ }
+
+ public void update(
+ byte in)
+ {
+ if (bufOff == buf.length)
+ {
+ cipher.processBlock(buf, 0, mac, 0);
+ bufOff = 0;
+ }
+
+ buf[bufOff++] = in;
+ }
+
+ public void update(
+ byte[] in,
+ int inOff,
+ int len)
+ {
+ if (len < 0)
+ {
+ throw new IllegalArgumentException("Can't have a negative input length!");
+ }
+
+ int blockSize = cipher.getBlockSize();
+ int resultLen = 0;
+ int gapLen = blockSize - bufOff;
+
+ if (len > gapLen)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+ resultLen += cipher.processBlock(buf, 0, mac, 0);
+
+ bufOff = 0;
+ len -= gapLen;
+ inOff += gapLen;
+
+ while (len > blockSize)
+ {
+ resultLen += cipher.processBlock(in, inOff, mac, 0);
+
+ len -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ System.arraycopy(in, inOff, buf, bufOff, len);
+
+ bufOff += len;
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ int blockSize = cipher.getBlockSize();
+
+ //
+ // pad with zeroes
+ //
+ if (this.padding == null)
+ {
+ while (bufOff < blockSize)
+ {
+ buf[bufOff] = 0;
+ bufOff++;
+ }
+ }
+ else
+ {
+ padding.addPadding(buf, bufOff);
+ }
+
+ cipher.processBlock(buf, 0, mac, 0);
+
+ cipher.getMacBlock(mac);
+
+ System.arraycopy(mac, 0, out, outOff, macSize);
+
+ reset();
+
+ return macSize;
+ }
+
+ /**
+ * Reset the mac generator.
+ */
+ public void reset()
+ {
+ /*
+ * clean the buffer.
+ */
+ for (int i = 0; i < buf.length; i++)
+ {
+ buf[i] = 0;
+ }
+
+ bufOff = 0;
+
+ /*
+ * reset the underlying cipher.
+ */
+ cipher.reset();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/macs/CMac.java b/core/src/main/java/org/bouncycastle/crypto/macs/CMac.java
new file mode 100644
index 00000000..8a3b5bbf
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/macs/CMac.java
@@ -0,0 +1,238 @@
+package org.bouncycastle.crypto.macs;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.paddings.ISO7816d4Padding;
+
+/**
+ * CMAC - as specified at www.nuee.nagoya-u.ac.jp/labs/tiwata/omac/omac.html
+ * <p>
+ * CMAC is analogous to OMAC1 - see also en.wikipedia.org/wiki/CMAC
+ * </p><p>
+ * CMAC is a NIST recomendation - see
+ * csrc.nist.gov/CryptoToolkit/modes/800-38_Series_Publications/SP800-38B.pdf
+ * </p><p>
+ * CMAC/OMAC1 is a blockcipher-based message authentication code designed and
+ * analyzed by Tetsu Iwata and Kaoru Kurosawa.
+ * </p><p>
+ * CMAC/OMAC1 is a simple variant of the CBC MAC (Cipher Block Chaining Message
+ * Authentication Code). OMAC stands for One-Key CBC MAC.
+ * </p><p>
+ * It supports 128- or 64-bits block ciphers, with any key size, and returns
+ * a MAC with dimension less or equal to the block size of the underlying
+ * cipher.
+ * </p>
+ */
+public class CMac implements Mac
+{
+ private static final byte CONSTANT_128 = (byte)0x87;
+ private static final byte CONSTANT_64 = (byte)0x1b;
+
+ private byte[] ZEROES;
+
+ private byte[] mac;
+
+ private byte[] buf;
+ private int bufOff;
+ private BlockCipher cipher;
+
+ private int macSize;
+
+ private byte[] L, Lu, Lu2;
+
+ /**
+ * create a standard MAC based on a CBC block cipher (64 or 128 bit block).
+ * This will produce an authentication code the length of the block size
+ * of the cipher.
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ */
+ public CMac(BlockCipher cipher)
+ {
+ this(cipher, cipher.getBlockSize() * 8);
+ }
+
+ /**
+ * create a standard MAC based on a block cipher with the size of the
+ * MAC been given in bits.
+ * <p/>
+ * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
+ * or 16 bits if being used as a data authenticator (FIPS Publication 113),
+ * and in general should be less than the size of the block cipher as it reduces
+ * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8 and <= 128.
+ */
+ public CMac(BlockCipher cipher, int macSizeInBits)
+ {
+ if ((macSizeInBits % 8) != 0)
+ {
+ throw new IllegalArgumentException("MAC size must be multiple of 8");
+ }
+
+ if (macSizeInBits > (cipher.getBlockSize() * 8))
+ {
+ throw new IllegalArgumentException(
+ "MAC size must be less or equal to "
+ + (cipher.getBlockSize() * 8));
+ }
+
+ if (cipher.getBlockSize() != 8 && cipher.getBlockSize() != 16)
+ {
+ throw new IllegalArgumentException(
+ "Block size must be either 64 or 128 bits");
+ }
+
+ this.cipher = new CBCBlockCipher(cipher);
+ this.macSize = macSizeInBits / 8;
+
+ mac = new byte[cipher.getBlockSize()];
+
+ buf = new byte[cipher.getBlockSize()];
+
+ ZEROES = new byte[cipher.getBlockSize()];
+
+ bufOff = 0;
+ }
+
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName();
+ }
+
+ private static byte[] doubleLu(byte[] in)
+ {
+ int FirstBit = (in[0] & 0xFF) >> 7;
+ byte[] ret = new byte[in.length];
+ for (int i = 0; i < in.length - 1; i++)
+ {
+ ret[i] = (byte)((in[i] << 1) + ((in[i + 1] & 0xFF) >> 7));
+ }
+ ret[in.length - 1] = (byte)(in[in.length - 1] << 1);
+ if (FirstBit == 1)
+ {
+ ret[in.length - 1] ^= in.length == 16 ? CONSTANT_128 : CONSTANT_64;
+ }
+ return ret;
+ }
+
+ public void init(CipherParameters params)
+ {
+ if (params != null)
+ {
+ cipher.init(true, params);
+
+ //initializes the L, Lu, Lu2 numbers
+ L = new byte[ZEROES.length];
+ cipher.processBlock(ZEROES, 0, L, 0);
+ Lu = doubleLu(L);
+ Lu2 = doubleLu(Lu);
+ }
+
+ reset();
+ }
+
+ public int getMacSize()
+ {
+ return macSize;
+ }
+
+ public void update(byte in)
+ {
+ if (bufOff == buf.length)
+ {
+ cipher.processBlock(buf, 0, mac, 0);
+ bufOff = 0;
+ }
+
+ buf[bufOff++] = in;
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ {
+ if (len < 0)
+ {
+ throw new IllegalArgumentException(
+ "Can't have a negative input length!");
+ }
+
+ int blockSize = cipher.getBlockSize();
+ int gapLen = blockSize - bufOff;
+
+ if (len > gapLen)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+ cipher.processBlock(buf, 0, mac, 0);
+
+ bufOff = 0;
+ len -= gapLen;
+ inOff += gapLen;
+
+ while (len > blockSize)
+ {
+ cipher.processBlock(in, inOff, mac, 0);
+
+ len -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ System.arraycopy(in, inOff, buf, bufOff, len);
+
+ bufOff += len;
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ {
+ int blockSize = cipher.getBlockSize();
+
+ byte[] lu;
+ if (bufOff == blockSize)
+ {
+ lu = Lu;
+ }
+ else
+ {
+ new ISO7816d4Padding().addPadding(buf, bufOff);
+ lu = Lu2;
+ }
+
+ for (int i = 0; i < mac.length; i++)
+ {
+ buf[i] ^= lu[i];
+ }
+
+ cipher.processBlock(buf, 0, mac, 0);
+
+ System.arraycopy(mac, 0, out, outOff, macSize);
+
+ reset();
+
+ return macSize;
+ }
+
+ /**
+ * Reset the mac generator.
+ */
+ public void reset()
+ {
+ /*
+ * clean the buffer.
+ */
+ for (int i = 0; i < buf.length; i++)
+ {
+ buf[i] = 0;
+ }
+
+ bufOff = 0;
+
+ /*
+ * reset the underlying cipher.
+ */
+ cipher.reset();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/macs/GMac.java b/core/src/main/java/org/bouncycastle/crypto/macs/GMac.java
new file mode 100644
index 00000000..8aae1e26
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/macs/GMac.java
@@ -0,0 +1,114 @@
+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)
+ {
+ // use of this confused flow analyser in some earlier JDKs
+ this.cipher = cipher;
+ this.macSizeBits = 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.toString());
+ }
+ }
+
+ public void reset()
+ {
+ cipher.reset();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/macs/GOST28147Mac.java b/core/src/main/java/org/bouncycastle/crypto/macs/GOST28147Mac.java
new file mode 100644
index 00000000..b71975b8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/macs/GOST28147Mac.java
@@ -0,0 +1,298 @@
+package org.bouncycastle.crypto.macs;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithSBox;
+
+/**
+ * implementation of GOST 28147-89 MAC
+ */
+public class GOST28147Mac
+ implements Mac
+{
+ private int blockSize = 8;
+ private int macSize = 4;
+ private int bufOff;
+ private byte[] buf;
+ private byte[] mac;
+ private boolean firstStep = true;
+ private int[] workingKey = null;
+
+ //
+ // This is default S-box - E_A.
+ private byte S[] = {
+ 0x9,0x6,0x3,0x2,0x8,0xB,0x1,0x7,0xA,0x4,0xE,0xF,0xC,0x0,0xD,0x5,
+ 0x3,0x7,0xE,0x9,0x8,0xA,0xF,0x0,0x5,0x2,0x6,0xC,0xB,0x4,0xD,0x1,
+ 0xE,0x4,0x6,0x2,0xB,0x3,0xD,0x8,0xC,0xF,0x5,0xA,0x0,0x7,0x1,0x9,
+ 0xE,0x7,0xA,0xC,0xD,0x1,0x3,0x9,0x0,0x2,0xB,0x4,0xF,0x8,0x5,0x6,
+ 0xB,0x5,0x1,0x9,0x8,0xD,0xF,0x0,0xE,0x4,0x2,0x3,0xC,0x7,0xA,0x6,
+ 0x3,0xA,0xD,0xC,0x1,0x2,0x0,0xB,0x7,0x5,0x9,0x4,0x8,0xF,0xE,0x6,
+ 0x1,0xD,0x2,0x9,0x7,0xA,0x6,0x0,0x8,0xC,0x4,0x5,0xF,0x3,0xB,0xE,
+ 0xB,0xA,0xF,0x5,0x0,0xC,0xE,0x8,0x6,0x2,0x3,0x9,0x1,0x7,0xD,0x4
+ };
+
+ public GOST28147Mac()
+ {
+ mac = new byte[blockSize];
+
+ buf = new byte[blockSize];
+ bufOff = 0;
+ }
+
+ private int[] generateWorkingKey(
+ byte[] userKey)
+ {
+ if (userKey.length != 32)
+ {
+ throw new IllegalArgumentException("Key length invalid. Key needs to be 32 byte - 256 bit!!!");
+ }
+
+ int key[] = new int[8];
+ for(int i=0; i!=8; i++)
+ {
+ key[i] = bytesToint(userKey,i*4);
+ }
+
+ return key;
+ }
+
+ public void init(
+ CipherParameters params)
+ throws IllegalArgumentException
+ {
+ reset();
+ buf = new byte[blockSize];
+ if (params instanceof ParametersWithSBox)
+ {
+ ParametersWithSBox param = (ParametersWithSBox)params;
+
+ //
+ // Set the S-Box
+ //
+ System.arraycopy(param.getSBox(), 0, this.S, 0, param.getSBox().length);
+
+ //
+ // set key if there is one
+ //
+ if (param.getParameters() != null)
+ {
+ workingKey = generateWorkingKey(((KeyParameter)param.getParameters()).getKey());
+ }
+ }
+ else if (params instanceof KeyParameter)
+ {
+ workingKey = generateWorkingKey(((KeyParameter)params).getKey());
+ }
+ else
+ {
+ throw new IllegalArgumentException("invalid parameter passed to GOST28147 init - " + params.getClass().getName());
+ }
+ }
+
+ public String getAlgorithmName()
+ {
+ return "GOST28147Mac";
+ }
+
+ public int getMacSize()
+ {
+ return macSize;
+ }
+
+ private int gost28147_mainStep(int n1, int key)
+ {
+ int cm = (key + n1); // CM1
+
+ // S-box replacing
+
+ int om = S[ 0 + ((cm >> (0 * 4)) & 0xF)] << (0 * 4);
+ om += S[ 16 + ((cm >> (1 * 4)) & 0xF)] << (1 * 4);
+ om += S[ 32 + ((cm >> (2 * 4)) & 0xF)] << (2 * 4);
+ om += S[ 48 + ((cm >> (3 * 4)) & 0xF)] << (3 * 4);
+ om += S[ 64 + ((cm >> (4 * 4)) & 0xF)] << (4 * 4);
+ om += S[ 80 + ((cm >> (5 * 4)) & 0xF)] << (5 * 4);
+ om += S[ 96 + ((cm >> (6 * 4)) & 0xF)] << (6 * 4);
+ om += S[112 + ((cm >> (7 * 4)) & 0xF)] << (7 * 4);
+
+ return om << 11 | om >>> (32-11); // 11-leftshift
+ }
+
+ private void gost28147MacFunc(
+ int[] workingKey,
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int N1, N2, tmp; //tmp -> for saving N1
+ N1 = bytesToint(in, inOff);
+ N2 = bytesToint(in, inOff + 4);
+
+ for(int k = 0; k < 2; k++) // 1-16 steps
+ {
+ for(int j = 0; j < 8; j++)
+ {
+ tmp = N1;
+ N1 = N2 ^ gost28147_mainStep(N1, workingKey[j]); // CM2
+ N2 = tmp;
+ }
+ }
+
+ intTobytes(N1, out, outOff);
+ intTobytes(N2, out, outOff + 4);
+ }
+
+ //array of bytes to type int
+ private int bytesToint(
+ byte[] in,
+ int inOff)
+ {
+ return ((in[inOff + 3] << 24) & 0xff000000) + ((in[inOff + 2] << 16) & 0xff0000) +
+ ((in[inOff + 1] << 8) & 0xff00) + (in[inOff] & 0xff);
+ }
+
+ //int to array of bytes
+ private void intTobytes(
+ int num,
+ byte[] out,
+ int outOff)
+ {
+ out[outOff + 3] = (byte)(num >>> 24);
+ out[outOff + 2] = (byte)(num >>> 16);
+ out[outOff + 1] = (byte)(num >>> 8);
+ out[outOff] = (byte)num;
+ }
+
+ private byte[] CM5func(byte[] buf, int bufOff, byte[] mac)
+ {
+ byte[] sum = new byte[buf.length - bufOff];
+
+ System.arraycopy(buf, bufOff, sum, 0, mac.length);
+
+ for (int i = 0; i != mac.length; i++)
+ {
+ sum[i] = (byte)(sum[i] ^ mac[i]);
+ }
+
+ return sum;
+ }
+
+ public void update(byte in)
+ throws IllegalStateException
+ {
+ if (bufOff == buf.length)
+ {
+ byte[] sumbuf = new byte[buf.length];
+ System.arraycopy(buf, 0, sumbuf, 0, mac.length);
+
+ if (firstStep)
+ {
+ firstStep = false;
+ }
+ else
+ {
+ sumbuf = CM5func(buf, 0, mac);
+ }
+
+ gost28147MacFunc(workingKey, sumbuf, 0, mac, 0);
+ bufOff = 0;
+ }
+
+ buf[bufOff++] = in;
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ throws DataLengthException, IllegalStateException
+ {
+ if (len < 0)
+ {
+ throw new IllegalArgumentException("Can't have a negative input length!");
+ }
+
+ int gapLen = blockSize - bufOff;
+
+ if (len > gapLen)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+ byte[] sumbuf = new byte[buf.length];
+ System.arraycopy(buf, 0, sumbuf, 0, mac.length);
+
+ if (firstStep)
+ {
+ firstStep = false;
+ }
+ else
+ {
+ sumbuf = CM5func(buf, 0, mac);
+ }
+
+ gost28147MacFunc(workingKey, sumbuf, 0, mac, 0);
+
+ bufOff = 0;
+ len -= gapLen;
+ inOff += gapLen;
+
+ while (len > blockSize)
+ {
+ sumbuf = CM5func(in, inOff, mac);
+ gost28147MacFunc(workingKey, sumbuf, 0, mac, 0);
+
+ len -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ System.arraycopy(in, inOff, buf, bufOff, len);
+
+ bufOff += len;
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ //padding with zero
+ while (bufOff < blockSize)
+ {
+ buf[bufOff] = 0;
+ bufOff++;
+ }
+
+ byte[] sumbuf = new byte[buf.length];
+ System.arraycopy(buf, 0, sumbuf, 0, mac.length);
+
+ if (firstStep)
+ {
+ firstStep = false;
+ }
+ else
+ {
+ sumbuf = CM5func(buf, 0, mac);
+ }
+
+ gost28147MacFunc(workingKey, sumbuf, 0, mac, 0);
+
+ System.arraycopy(mac, (mac.length/2)-macSize, out, outOff, macSize);
+
+ reset();
+
+ return macSize;
+ }
+
+ public void reset()
+ {
+ /*
+ * clean the buffer.
+ */
+ for (int i = 0; i < buf.length; i++)
+ {
+ buf[i] = 0;
+ }
+
+ bufOff = 0;
+
+ firstStep = true;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/macs/HMac.java b/core/src/main/java/org/bouncycastle/crypto/macs/HMac.java
new file mode 100644
index 00000000..d4345d9b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/macs/HMac.java
@@ -0,0 +1,231 @@
+package org.bouncycastle.crypto.macs;
+
+import java.util.Hashtable;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+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
+ *
+ * H(K XOR opad, H(K XOR ipad, text))
+ */
+public class HMac
+ implements Mac
+{
+ private final static byte IPAD = (byte)0x36;
+ private final static byte OPAD = (byte)0x5C;
+
+ private Digest digest;
+ private int digestSize;
+ private int blockLength;
+ private Memoable ipadState;
+ private Memoable opadState;
+
+ private byte[] inputPad;
+ private byte[] outputBuf;
+
+ private static Hashtable blockLengths;
+
+ static
+ {
+ blockLengths = new Hashtable();
+
+ blockLengths.put("GOST3411", Integers.valueOf(32));
+
+ blockLengths.put("MD2", Integers.valueOf(16));
+ blockLengths.put("MD4", Integers.valueOf(64));
+ blockLengths.put("MD5", Integers.valueOf(64));
+
+ blockLengths.put("RIPEMD128", Integers.valueOf(64));
+ blockLengths.put("RIPEMD160", Integers.valueOf(64));
+
+ blockLengths.put("SHA-1", Integers.valueOf(64));
+ blockLengths.put("SHA-224", Integers.valueOf(64));
+ blockLengths.put("SHA-256", Integers.valueOf(64));
+ blockLengths.put("SHA-384", Integers.valueOf(128));
+ blockLengths.put("SHA-512", Integers.valueOf(128));
+
+ blockLengths.put("Tiger", Integers.valueOf(64));
+ blockLengths.put("Whirlpool", Integers.valueOf(64));
+ }
+
+ private static int getByteLength(
+ Digest digest)
+ {
+ if (digest instanceof ExtendedDigest)
+ {
+ return ((ExtendedDigest)digest).getByteLength();
+ }
+
+ Integer b = (Integer)blockLengths.get(digest.getAlgorithmName());
+
+ if (b == null)
+ {
+ throw new IllegalArgumentException("unknown digest passed: " + digest.getAlgorithmName());
+ }
+
+ return b.intValue();
+ }
+
+ /**
+ * Base constructor for one of the standard digest algorithms that the
+ * byteLength of the algorithm is know for.
+ *
+ * @param digest the digest.
+ */
+ public HMac(
+ Digest digest)
+ {
+ this(digest, getByteLength(digest));
+ }
+
+ private HMac(
+ Digest digest,
+ int byteLength)
+ {
+ this.digest = digest;
+ this.digestSize = digest.getDigestSize();
+ this.blockLength = byteLength;
+ this.inputPad = new byte[blockLength];
+ this.outputBuf = new byte[blockLength + digestSize];
+ }
+
+ public String getAlgorithmName()
+ {
+ return digest.getAlgorithmName() + "/HMAC";
+ }
+
+ public Digest getUnderlyingDigest()
+ {
+ return digest;
+ }
+
+ public void init(
+ CipherParameters params)
+ {
+ digest.reset();
+
+ byte[] key = ((KeyParameter)params).getKey();
+ int keyLength = key.length;
+
+ if (keyLength > blockLength)
+ {
+ digest.update(key, 0, keyLength);
+ digest.doFinal(inputPad, 0);
+
+ keyLength = digestSize;
+ }
+ else
+ {
+ System.arraycopy(key, 0, inputPad, 0, keyLength);
+ }
+
+ for (int i = keyLength; i < inputPad.length; i++)
+ {
+ inputPad[i] = 0;
+ }
+
+ System.arraycopy(inputPad, 0, outputBuf, 0, blockLength);
+
+ 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()
+ {
+ return digestSize;
+ }
+
+ public void update(
+ byte in)
+ {
+ digest.update(in);
+ }
+
+ public void update(
+ byte[] in,
+ int inOff,
+ int len)
+ {
+ digest.update(in, inOff, len);
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ digest.doFinal(outputBuf, blockLength);
+
+ 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++)
+ {
+ outputBuf[i] = 0;
+ }
+
+ if (ipadState != null)
+ {
+ ((Memoable)digest).reset(ipadState);
+ }
+ else
+ {
+ digest.update(inputPad, 0, inputPad.length);
+ }
+
+ return len;
+ }
+
+ /**
+ * Reset the mac generator.
+ */
+ public void reset()
+ {
+ /*
+ * reset the underlying digest.
+ */
+ digest.reset();
+
+ /*
+ * reinitialize the digest.
+ */
+ digest.update(inputPad, 0, inputPad.length);
+ }
+
+ private static void xorPad(byte[] pad, int len, byte n)
+ {
+ for (int i = 0; i < len; ++i)
+ {
+ pad[i] ^= n;
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/macs/ISO9797Alg3Mac.java b/core/src/main/java/org/bouncycastle/crypto/macs/ISO9797Alg3Mac.java
new file mode 100644
index 00000000..330b39e7
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/macs/ISO9797Alg3Mac.java
@@ -0,0 +1,305 @@
+package org.bouncycastle.crypto.macs;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.engines.DESEngine;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.paddings.BlockCipherPadding;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+
+/**
+ * DES based CBC Block Cipher MAC according to ISO9797, algorithm 3 (ANSI X9.19 Retail MAC)
+ *
+ * This could as well be derived from CBCBlockCipherMac, but then the property mac in the base
+ * class must be changed to protected
+ */
+
+public class ISO9797Alg3Mac
+ implements Mac
+{
+ private byte[] mac;
+
+ private byte[] buf;
+ private int bufOff;
+ private BlockCipher cipher;
+ private BlockCipherPadding padding;
+
+ private int macSize;
+ private KeyParameter lastKey2;
+ private KeyParameter lastKey3;
+
+ /**
+ * create a Retail-MAC based on a CBC block cipher. This will produce an
+ * authentication code of the length of the block size of the cipher.
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation. This must
+ * be DESEngine.
+ */
+ public ISO9797Alg3Mac(
+ BlockCipher cipher)
+ {
+ this(cipher, cipher.getBlockSize() * 8, null);
+ }
+
+ /**
+ * create a Retail-MAC based on a CBC block cipher. This will produce an
+ * authentication code of the length of the block size of the cipher.
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param padding the padding to be used to complete the last block.
+ */
+ public ISO9797Alg3Mac(
+ BlockCipher cipher,
+ BlockCipherPadding padding)
+ {
+ this(cipher, cipher.getBlockSize() * 8, padding);
+ }
+
+ /**
+ * create a Retail-MAC based on a block cipher with the size of the
+ * MAC been given in bits. This class uses single DES CBC mode as the basis for the
+ * MAC generation.
+ * <p>
+ * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
+ * or 16 bits if being used as a data authenticator (FIPS Publication 113),
+ * and in general should be less than the size of the block cipher as it reduces
+ * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
+ */
+ public ISO9797Alg3Mac(
+ BlockCipher cipher,
+ int macSizeInBits)
+ {
+ this(cipher, macSizeInBits, null);
+ }
+
+ /**
+ * create a standard MAC based on a block cipher with the size of the
+ * MAC been given in bits. This class uses single DES CBC mode as the basis for the
+ * MAC generation. The final block is decrypted and then encrypted using the
+ * middle and right part of the key.
+ * <p>
+ * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
+ * or 16 bits if being used as a data authenticator (FIPS Publication 113),
+ * and in general should be less than the size of the block cipher as it reduces
+ * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
+ * @param padding the padding to be used to complete the last block.
+ */
+ public ISO9797Alg3Mac(
+ BlockCipher cipher,
+ int macSizeInBits,
+ BlockCipherPadding padding)
+ {
+ if ((macSizeInBits % 8) != 0)
+ {
+ throw new IllegalArgumentException("MAC size must be multiple of 8");
+ }
+
+ if (!(cipher instanceof DESEngine))
+ {
+ throw new IllegalArgumentException("cipher must be instance of DESEngine");
+ }
+
+ this.cipher = new CBCBlockCipher(cipher);
+ this.padding = padding;
+ this.macSize = macSizeInBits / 8;
+
+ mac = new byte[cipher.getBlockSize()];
+
+ buf = new byte[cipher.getBlockSize()];
+ bufOff = 0;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "ISO9797Alg3";
+ }
+
+ public void init(CipherParameters params)
+ {
+ reset();
+
+ if (!(params instanceof KeyParameter || params instanceof ParametersWithIV))
+ {
+ throw new IllegalArgumentException(
+ "params must be an instance of KeyParameter or ParametersWithIV");
+ }
+
+ // KeyParameter must contain a double or triple length DES key,
+ // however the underlying cipher is a single DES. The middle and
+ // right key are used only in the final step.
+
+ KeyParameter kp;
+
+ if (params instanceof KeyParameter)
+ {
+ kp = (KeyParameter)params;
+ }
+ else
+ {
+ kp = (KeyParameter)((ParametersWithIV)params).getParameters();
+ }
+
+ KeyParameter key1;
+ byte[] keyvalue = kp.getKey();
+
+ if (keyvalue.length == 16)
+ { // Double length DES key
+ key1 = new KeyParameter(keyvalue, 0, 8);
+ this.lastKey2 = new KeyParameter(keyvalue, 8, 8);
+ this.lastKey3 = key1;
+ }
+ else if (keyvalue.length == 24)
+ { // Triple length DES key
+ key1 = new KeyParameter(keyvalue, 0, 8);
+ this.lastKey2 = new KeyParameter(keyvalue, 8, 8);
+ this.lastKey3 = new KeyParameter(keyvalue, 16, 8);
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "Key must be either 112 or 168 bit long");
+ }
+
+ if (params instanceof ParametersWithIV)
+ {
+ cipher.init(true, new ParametersWithIV(key1, ((ParametersWithIV)params).getIV()));
+ }
+ else
+ {
+ cipher.init(true, key1);
+ }
+ }
+
+ public int getMacSize()
+ {
+ return macSize;
+ }
+
+ public void update(
+ byte in)
+ {
+ if (bufOff == buf.length)
+ {
+ cipher.processBlock(buf, 0, mac, 0);
+ bufOff = 0;
+ }
+
+ buf[bufOff++] = in;
+ }
+
+
+ public void update(
+ byte[] in,
+ int inOff,
+ int len)
+ {
+ if (len < 0)
+ {
+ throw new IllegalArgumentException("Can't have a negative input length!");
+ }
+
+ int blockSize = cipher.getBlockSize();
+ int resultLen = 0;
+ int gapLen = blockSize - bufOff;
+
+ if (len > gapLen)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+ resultLen += cipher.processBlock(buf, 0, mac, 0);
+
+ bufOff = 0;
+ len -= gapLen;
+ inOff += gapLen;
+
+ while (len > blockSize)
+ {
+ resultLen += cipher.processBlock(in, inOff, mac, 0);
+
+ len -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ System.arraycopy(in, inOff, buf, bufOff, len);
+
+ bufOff += len;
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ int blockSize = cipher.getBlockSize();
+
+ if (padding == null)
+ {
+ //
+ // pad with zeroes
+ //
+ while (bufOff < blockSize)
+ {
+ buf[bufOff] = 0;
+ bufOff++;
+ }
+ }
+ else
+ {
+ if (bufOff == blockSize)
+ {
+ cipher.processBlock(buf, 0, mac, 0);
+ bufOff = 0;
+ }
+
+ padding.addPadding(buf, bufOff);
+ }
+
+ cipher.processBlock(buf, 0, mac, 0);
+
+ // Added to code from base class
+ DESEngine deseng = new DESEngine();
+
+ deseng.init(false, this.lastKey2);
+ deseng.processBlock(mac, 0, mac, 0);
+
+ deseng.init(true, this.lastKey3);
+ deseng.processBlock(mac, 0, mac, 0);
+ // ****
+
+ System.arraycopy(mac, 0, out, outOff, macSize);
+
+ reset();
+
+ return macSize;
+ }
+
+
+ /**
+ * Reset the mac generator.
+ */
+ public void reset()
+ {
+ /*
+ * clean the buffer.
+ */
+ for (int i = 0; i < buf.length; i++)
+ {
+ buf[i] = 0;
+ }
+
+ bufOff = 0;
+
+ /*
+ * reset the underlying cipher.
+ */
+ cipher.reset();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/macs/OldHMac.java b/core/src/main/java/org/bouncycastle/crypto/macs/OldHMac.java
new file mode 100644
index 00000000..7463afd3
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/macs/OldHMac.java
@@ -0,0 +1,138 @@
+package org.bouncycastle.crypto.macs;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * HMAC implementation based on RFC2104
+ *
+ * H(K XOR opad, H(K XOR ipad, text))
+ */
+public class OldHMac
+implements Mac
+{
+ private final static int BLOCK_LENGTH = 64;
+
+ private final static byte IPAD = (byte)0x36;
+ private final static byte OPAD = (byte)0x5C;
+
+ private Digest digest;
+ private int digestSize;
+ private byte[] inputPad = new byte[BLOCK_LENGTH];
+ private byte[] outputPad = new byte[BLOCK_LENGTH];
+
+ /**
+ * @deprecated uses incorrect pad for SHA-512 and SHA-384 use HMac.
+ */
+ public OldHMac(
+ Digest digest)
+ {
+ this.digest = digest;
+ digestSize = digest.getDigestSize();
+ }
+
+ public String getAlgorithmName()
+ {
+ return digest.getAlgorithmName() + "/HMAC";
+ }
+
+ public Digest getUnderlyingDigest()
+ {
+ return digest;
+ }
+
+ public void init(
+ CipherParameters params)
+ {
+ digest.reset();
+
+ byte[] key = ((KeyParameter)params).getKey();
+
+ if (key.length > BLOCK_LENGTH)
+ {
+ digest.update(key, 0, key.length);
+ digest.doFinal(inputPad, 0);
+ for (int i = digestSize; i < inputPad.length; i++)
+ {
+ inputPad[i] = 0;
+ }
+ }
+ else
+ {
+ System.arraycopy(key, 0, inputPad, 0, key.length);
+ for (int i = key.length; i < inputPad.length; i++)
+ {
+ inputPad[i] = 0;
+ }
+ }
+
+ outputPad = new byte[inputPad.length];
+ System.arraycopy(inputPad, 0, outputPad, 0, inputPad.length);
+
+ for (int i = 0; i < inputPad.length; i++)
+ {
+ inputPad[i] ^= IPAD;
+ }
+
+ for (int i = 0; i < outputPad.length; i++)
+ {
+ outputPad[i] ^= OPAD;
+ }
+
+ digest.update(inputPad, 0, inputPad.length);
+ }
+
+ public int getMacSize()
+ {
+ return digestSize;
+ }
+
+ public void update(
+ byte in)
+ {
+ digest.update(in);
+ }
+
+ public void update(
+ byte[] in,
+ int inOff,
+ int len)
+ {
+ digest.update(in, inOff, len);
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ byte[] tmp = new byte[digestSize];
+ digest.doFinal(tmp, 0);
+
+ digest.update(outputPad, 0, outputPad.length);
+ digest.update(tmp, 0, tmp.length);
+
+ int len = digest.doFinal(out, outOff);
+
+ reset();
+
+ return len;
+ }
+
+ /**
+ * Reset the mac generator.
+ */
+ public void reset()
+ {
+ /*
+ * reset the underlying digest.
+ */
+ digest.reset();
+
+ /*
+ * reinitialize the digest.
+ */
+ digest.update(inputPad, 0, inputPad.length);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/macs/SipHash.java b/core/src/main/java/org/bouncycastle/crypto/macs/SipHash.java
new file mode 100644
index 00000000..527c8040
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/macs/SipHash.java
@@ -0,0 +1,192 @@
+package org.bouncycastle.crypto.macs;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.util.Pack;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Implementation of SipHash as specified in "SipHash: a fast short-input PRF", by Jean-Philippe
+ * Aumasson and Daniel J. Bernstein (https://131002.net/siphash/siphash.pdf).
+ * <p/>
+ * "SipHash is a family of PRFs SipHash-c-d where the integer parameters c and d are the number of
+ * compression rounds and the number of finalization rounds. A compression round is identical to a
+ * finalization round and this round function is called SipRound. Given a 128-bit key k and a
+ * (possibly empty) byte string m, SipHash-c-d returns a 64-bit value..."
+ */
+public class SipHash
+ implements Mac
+{
+
+ protected final int c, d;
+
+ protected long k0, k1;
+ protected long v0, v1, v2, v3, v4;
+
+ protected byte[] buf = new byte[8];
+ protected int bufPos = 0;
+ protected int wordCount = 0;
+
+ /**
+ * SipHash-2-4
+ */
+ public SipHash()
+ {
+ // use of this confuses flow analyser on earlier JDKs.
+ this.c = 2;
+ this.d = 4;
+ }
+
+ /**
+ * SipHash-c-d
+ *
+ * @param c the number of compression rounds
+ * @param d the number of finalization rounds
+ */
+ public SipHash(int c, int d)
+ {
+ this.c = c;
+ this.d = d;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "SipHash-" + c + "-" + d;
+ }
+
+ public int getMacSize()
+ {
+ return 8;
+ }
+
+ public void init(CipherParameters params)
+ throws IllegalArgumentException
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("'params' must be an instance of KeyParameter");
+ }
+ KeyParameter keyParameter = (KeyParameter)params;
+ byte[] key = keyParameter.getKey();
+ if (key.length != 16)
+ {
+ throw new IllegalArgumentException("'params' must be a 128-bit key");
+ }
+
+ this.k0 = Pack.littleEndianToLong(key, 0);
+ this.k1 = Pack.littleEndianToLong(key, 8);
+
+ reset();
+ }
+
+ public void update(byte input)
+ throws IllegalStateException
+ {
+
+ buf[bufPos] = input;
+ if (++bufPos == buf.length)
+ {
+ processMessageWord();
+ bufPos = 0;
+ }
+ }
+
+ public void update(byte[] input, int offset, int length)
+ throws DataLengthException,
+ IllegalStateException
+ {
+
+ for (int i = 0; i < length; ++i)
+ {
+ buf[bufPos] = input[offset + i];
+ if (++bufPos == buf.length)
+ {
+ processMessageWord();
+ bufPos = 0;
+ }
+ }
+ }
+
+ public long doFinal()
+ throws DataLengthException, IllegalStateException
+ {
+
+ buf[7] = (byte)(((wordCount << 3) + bufPos) & 0xff);
+ while (bufPos < 7)
+ {
+ buf[bufPos++] = 0;
+ }
+
+ processMessageWord();
+
+ v2 ^= 0xffL;
+
+ applySipRounds(d);
+
+ long result = v0 ^ v1 ^ v2 ^ v3;
+
+ reset();
+
+ return result;
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+
+ long result = doFinal();
+ Pack.longToLittleEndian(result, out, outOff);
+ return 8;
+ }
+
+ public void reset()
+ {
+
+ v0 = k0 ^ 0x736f6d6570736575L;
+ v1 = k1 ^ 0x646f72616e646f6dL;
+ v2 = k0 ^ 0x6c7967656e657261L;
+ v3 = k1 ^ 0x7465646279746573L;
+
+ Arrays.fill(buf, (byte)0);
+ bufPos = 0;
+ wordCount = 0;
+ }
+
+ protected void processMessageWord()
+ {
+
+ ++wordCount;
+ long m = Pack.littleEndianToLong(buf, 0);
+ v3 ^= m;
+ applySipRounds(c);
+ v0 ^= m;
+ }
+
+ protected void applySipRounds(int n)
+ {
+ for (int r = 0; r < n; ++r)
+ {
+ v0 += v1;
+ v2 += v3;
+ v1 = rotateLeft(v1, 13);
+ v3 = rotateLeft(v3, 16);
+ v1 ^= v0;
+ v3 ^= v2;
+ v0 = rotateLeft(v0, 32);
+ v2 += v1;
+ v0 += v3;
+ v1 = rotateLeft(v1, 17);
+ v3 = rotateLeft(v3, 21);
+ v1 ^= v2;
+ v3 ^= v0;
+ v2 = rotateLeft(v2, 32);
+ }
+ }
+
+ protected static long rotateLeft(long x, int n)
+ {
+ return (x << n) | (x >>> (64 - n));
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/macs/VMPCMac.java b/core/src/main/java/org/bouncycastle/crypto/macs/VMPCMac.java
new file mode 100644
index 00000000..58d06d08
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/macs/VMPCMac.java
@@ -0,0 +1,186 @@
+package org.bouncycastle.crypto.macs;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+
+public class VMPCMac implements Mac
+{
+ private byte g;
+
+ private byte n = 0;
+ private byte[] P = null;
+ private byte s = 0;
+
+ private byte[] T;
+ private byte[] workingIV;
+
+ private byte[] workingKey;
+
+ private byte x1, x2, x3, x4;
+
+ public int doFinal(byte[] out, int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ // Execute the Post-Processing Phase
+ for (int r = 1; r < 25; r++)
+ {
+ s = P[(s + P[n & 0xff]) & 0xff];
+
+ x4 = P[(x4 + x3 + r) & 0xff];
+ x3 = P[(x3 + x2 + r) & 0xff];
+ x2 = P[(x2 + x1 + r) & 0xff];
+ x1 = P[(x1 + s + r) & 0xff];
+ T[g & 0x1f] = (byte) (T[g & 0x1f] ^ x1);
+ T[(g + 1) & 0x1f] = (byte) (T[(g + 1) & 0x1f] ^ x2);
+ T[(g + 2) & 0x1f] = (byte) (T[(g + 2) & 0x1f] ^ x3);
+ T[(g + 3) & 0x1f] = (byte) (T[(g + 3) & 0x1f] ^ x4);
+ g = (byte) ((g + 4) & 0x1f);
+
+ byte temp = P[n & 0xff];
+ P[n & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ n = (byte) ((n + 1) & 0xff);
+ }
+
+ // Input T to the IV-phase of the VMPC KSA
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + T[m & 0x1f]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+
+ // Store 20 new outputs of the VMPC Stream Cipher in table M
+ byte[] M = new byte[20];
+ for (int i = 0; i < 20; i++)
+ {
+ s = P[(s + P[i & 0xff]) & 0xff];
+ M[i] = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff];
+
+ byte temp = P[i & 0xff];
+ P[i & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+
+ System.arraycopy(M, 0, out, outOff, M.length);
+ reset();
+
+ return M.length;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "VMPC-MAC";
+ }
+
+ public int getMacSize()
+ {
+ return 20;
+ }
+
+ public void init(CipherParameters params) throws IllegalArgumentException
+ {
+ if (!(params instanceof ParametersWithIV))
+ {
+ throw new IllegalArgumentException(
+ "VMPC-MAC Init parameters must include an IV");
+ }
+
+ ParametersWithIV ivParams = (ParametersWithIV) params;
+ KeyParameter key = (KeyParameter) ivParams.getParameters();
+
+ if (!(ivParams.getParameters() instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException(
+ "VMPC-MAC Init parameters must include a key");
+ }
+
+ this.workingIV = ivParams.getIV();
+
+ if (workingIV == null || workingIV.length < 1 || workingIV.length > 768)
+ {
+ throw new IllegalArgumentException(
+ "VMPC-MAC requires 1 to 768 bytes of IV");
+ }
+
+ this.workingKey = key.getKey();
+
+ reset();
+
+ }
+
+ private void initKey(byte[] keyBytes, byte[] ivBytes)
+ {
+ s = 0;
+ P = new byte[256];
+ for (int i = 0; i < 256; i++)
+ {
+ P[i] = (byte) i;
+ }
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.length]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + ivBytes[m % ivBytes.length]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+ n = 0;
+ }
+
+ public void reset()
+ {
+ initKey(this.workingKey, this.workingIV);
+ g = x1 = x2 = x3 = x4 = n = 0;
+ T = new byte[32];
+ for (int i = 0; i < 32; i++)
+ {
+ T[i] = 0;
+ }
+ }
+
+ public void update(byte in) throws IllegalStateException
+ {
+ s = P[(s + P[n & 0xff]) & 0xff];
+ byte c = (byte) (in ^ P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff]);
+
+ x4 = P[(x4 + x3) & 0xff];
+ x3 = P[(x3 + x2) & 0xff];
+ x2 = P[(x2 + x1) & 0xff];
+ x1 = P[(x1 + s + c) & 0xff];
+ T[g & 0x1f] = (byte) (T[g & 0x1f] ^ x1);
+ T[(g + 1) & 0x1f] = (byte) (T[(g + 1) & 0x1f] ^ x2);
+ T[(g + 2) & 0x1f] = (byte) (T[(g + 2) & 0x1f] ^ x3);
+ T[(g + 3) & 0x1f] = (byte) (T[(g + 3) & 0x1f] ^ x4);
+ g = (byte) ((g + 4) & 0x1f);
+
+ byte temp = P[n & 0xff];
+ P[n & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ n = (byte) ((n + 1) & 0xff);
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + len) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ for (int i = 0; i < len; i++)
+ {
+ update(in[i]);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java
new file mode 100644
index 00000000..71b75954
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java
@@ -0,0 +1,126 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+
+/**
+ * A block cipher mode that includes authenticated encryption with a streaming mode and optional associated data.
+ * @see org.bouncycastle.crypto.params.AEADParameters
+ */
+public interface AEADBlockCipher
+{
+ /**
+ * initialise the underlying cipher. Parameter can either be an AEADParameters or a ParametersWithIV object.
+ *
+ * @param forEncryption true if we are setting up for encryption, false otherwise.
+ * @param params the necessary parameters for the underlying cipher to be initialised.
+ * @exception IllegalArgumentException if the params argument is inappropriate.
+ */
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException;
+
+ /**
+ * Return the name of the algorithm.
+ *
+ * @return the algorithm name.
+ */
+ public String getAlgorithmName();
+
+ /**
+ * return the cipher this object wraps.
+ *
+ * @return the cipher this object wraps.
+ */
+ public BlockCipher getUnderlyingCipher();
+
+ /**
+ * Add a single byte to the associated data check.
+ * <br>If the implementation supports it, this will be an online operation and will not retain the associated data.
+ *
+ * @param in the byte to be processed.
+ */
+ public void processAADByte(byte in);
+
+ /**
+ * Add a sequence of bytes to the associated data check.
+ * <br>If the implementation supports it, this will be an online operation and will not retain the associated data.
+ *
+ * @param in the input byte array.
+ * @param inOff the offset into the in array where the data to be processed starts.
+ * @param len the number of bytes to be processed.
+ */
+ public void processAADBytes(byte[] in, int inOff, int len);
+
+ /**
+ * encrypt/decrypt a single byte.
+ *
+ * @param in the byte to be processed.
+ * @param out the output buffer the processed byte goes into.
+ * @param outOff the offset into the output byte array the processed data starts at.
+ * @return the number of bytes written to out.
+ * @exception DataLengthException if the output buffer is too small.
+ */
+ public int processByte(byte in, byte[] out, int outOff)
+ throws DataLengthException;
+
+ /**
+ * process a block of bytes from in putting the result into out.
+ *
+ * @param in the input byte array.
+ * @param inOff the offset into the in array where the data to be processed starts.
+ * @param len the number of bytes to be processed.
+ * @param out the output buffer the processed bytes go into.
+ * @param outOff the offset into the output byte array the processed data starts at.
+ * @return the number of bytes written to out.
+ * @exception DataLengthException if the output buffer is too small.
+ */
+ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
+ throws DataLengthException;
+
+ /**
+ * Finish the operation either appending or verifying the MAC at the end of the data.
+ *
+ * @param out space for any resulting output data.
+ * @param outOff offset into out to start copying the data at.
+ * @return number of bytes written into out.
+ * @throws IllegalStateException if the cipher is in an inappropriate state.
+ * @throws org.bouncycastle.crypto.InvalidCipherTextException if the MAC fails to match.
+ */
+ public int doFinal(byte[] out, int outOff)
+ throws IllegalStateException, InvalidCipherTextException;
+
+ /**
+ * Return the value of the MAC associated with the last stream processed.
+ *
+ * @return MAC for plaintext data.
+ */
+ public byte[] getMac();
+
+ /**
+ * return the size of the output buffer required for a processBytes
+ * an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to processBytes
+ * with len bytes of input.
+ */
+ public int getUpdateOutputSize(int len);
+
+ /**
+ * return the size of the output buffer required for a processBytes plus a
+ * doFinal with an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to processBytes and doFinal
+ * with len bytes of input.
+ */
+ public int getOutputSize(int len);
+
+ /**
+ * Reset the cipher. After resetting the cipher is in the same state
+ * as it was after the last init (if there was one).
+ */
+ public void reset();
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java
new file mode 100644
index 00000000..d4800e62
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java
@@ -0,0 +1,253 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * implements Cipher-Block-Chaining (CBC) mode on top of a simple cipher.
+ */
+public class CBCBlockCipher
+ implements BlockCipher
+{
+ private byte[] IV;
+ private byte[] cbcV;
+ private byte[] cbcNextV;
+
+ private int blockSize;
+ private BlockCipher cipher = null;
+ private boolean encrypting;
+
+ /**
+ * Basic constructor.
+ *
+ * @param cipher the block cipher to be used as the basis of chaining.
+ */
+ public CBCBlockCipher(
+ BlockCipher cipher)
+ {
+ this.cipher = cipher;
+ this.blockSize = cipher.getBlockSize();
+
+ this.IV = new byte[blockSize];
+ this.cbcV = new byte[blockSize];
+ this.cbcNextV = new byte[blockSize];
+ }
+
+ /**
+ * return the underlying block cipher that we are wrapping.
+ *
+ * @return the underlying block cipher that we are wrapping.
+ */
+ public BlockCipher getUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+ /**
+ * Initialise the cipher and, possibly, the initialisation vector (IV).
+ * If an IV isn't passed as part of the parameter, the IV will be all zeros.
+ *
+ * @param encrypting if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param params the key and other data required by the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ throws IllegalArgumentException
+ {
+ boolean oldEncrypting = this.encrypting;
+
+ this.encrypting = encrypting;
+
+ if (params instanceof ParametersWithIV)
+ {
+ ParametersWithIV ivParam = (ParametersWithIV)params;
+ byte[] iv = ivParam.getIV();
+
+ if (iv.length != blockSize)
+ {
+ throw new IllegalArgumentException("initialisation vector must be the same length as block size");
+ }
+
+ System.arraycopy(iv, 0, IV, 0, iv.length);
+
+ reset();
+
+ // if null it's an IV changed only.
+ if (ivParam.getParameters() != null)
+ {
+ cipher.init(encrypting, ivParam.getParameters());
+ }
+ else if (oldEncrypting != encrypting)
+ {
+ throw new IllegalArgumentException("cannot change encrypting state without providing key.");
+ }
+ }
+ else
+ {
+ reset();
+
+ // if it's null, key is to be reused.
+ if (params != null)
+ {
+ cipher.init(encrypting, params);
+ }
+ else if (oldEncrypting != encrypting)
+ {
+ throw new IllegalArgumentException("cannot change encrypting state without providing key.");
+ }
+ }
+ }
+
+ /**
+ * return the algorithm name and mode.
+ *
+ * @return the name of the underlying algorithm followed by "/CBC".
+ */
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName() + "/CBC";
+ }
+
+ /**
+ * return the block size of the underlying cipher.
+ *
+ * @return the block size of the underlying cipher.
+ */
+ public int getBlockSize()
+ {
+ return cipher.getBlockSize();
+ }
+
+ /**
+ * Process one block of input from the array in and write it to
+ * the out array.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ return (encrypting) ? encryptBlock(in, inOff, out, outOff) : decryptBlock(in, inOff, out, outOff);
+ }
+
+ /**
+ * reset the chaining vector back to the IV and reset the underlying
+ * cipher.
+ */
+ public void reset()
+ {
+ System.arraycopy(IV, 0, cbcV, 0, IV.length);
+ Arrays.fill(cbcNextV, (byte)0);
+
+ cipher.reset();
+ }
+
+ /**
+ * Do the appropriate chaining step for CBC mode encryption.
+ *
+ * @param in the array containing the data to be encrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ private int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ /*
+ * XOR the cbcV and the input,
+ * then encrypt the cbcV
+ */
+ for (int i = 0; i < blockSize; i++)
+ {
+ cbcV[i] ^= in[inOff + i];
+ }
+
+ int length = cipher.processBlock(cbcV, 0, out, outOff);
+
+ /*
+ * copy ciphertext to cbcV
+ */
+ System.arraycopy(out, outOff, cbcV, 0, cbcV.length);
+
+ return length;
+ }
+
+ /**
+ * Do the appropriate chaining step for CBC mode decryption.
+ *
+ * @param in the array containing the data to be decrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the decrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ private int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ System.arraycopy(in, inOff, cbcNextV, 0, blockSize);
+
+ int length = cipher.processBlock(in, inOff, out, outOff);
+
+ /*
+ * XOR the cbcV and the output
+ */
+ for (int i = 0; i < blockSize; i++)
+ {
+ out[outOff + i] ^= cbcV[i];
+ }
+
+ /*
+ * swap the back up buffer into next position
+ */
+ byte[] tmp;
+
+ tmp = cbcV;
+ cbcV = cbcNextV;
+ cbcNextV = tmp;
+
+ return length;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java
new file mode 100644
index 00000000..9a6e2e0c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java
@@ -0,0 +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;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java
new file mode 100644
index 00000000..d0fb9bb6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java
@@ -0,0 +1,258 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+
+/**
+ * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher.
+ */
+public class CFBBlockCipher
+ implements BlockCipher
+{
+ private byte[] IV;
+ private byte[] cfbV;
+ private byte[] cfbOutV;
+
+ private int blockSize;
+ private BlockCipher cipher = null;
+ private boolean encrypting;
+
+ /**
+ * Basic constructor.
+ *
+ * @param cipher the block cipher to be used as the basis of the
+ * feedback mode.
+ * @param bitBlockSize the block size in bits (note: a multiple of 8)
+ */
+ public CFBBlockCipher(
+ BlockCipher cipher,
+ int bitBlockSize)
+ {
+ this.cipher = cipher;
+ this.blockSize = bitBlockSize / 8;
+
+ this.IV = new byte[cipher.getBlockSize()];
+ this.cfbV = new byte[cipher.getBlockSize()];
+ this.cfbOutV = new byte[cipher.getBlockSize()];
+ }
+
+ /**
+ * return the underlying block cipher that we are wrapping.
+ *
+ * @return the underlying block cipher that we are wrapping.
+ */
+ public BlockCipher getUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+ /**
+ * Initialise the cipher and, possibly, the initialisation vector (IV).
+ * If an IV isn't passed as part of the parameter, the IV will be all zeros.
+ * An IV which is too short is handled in FIPS compliant fashion.
+ *
+ * @param encrypting if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param params the key and other data required by the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ throws IllegalArgumentException
+ {
+ this.encrypting = encrypting;
+
+ if (params instanceof ParametersWithIV)
+ {
+ ParametersWithIV ivParam = (ParametersWithIV)params;
+ byte[] iv = ivParam.getIV();
+
+ if (iv.length < IV.length)
+ {
+ // prepend the supplied IV with zeros (per FIPS PUB 81)
+ System.arraycopy(iv, 0, IV, IV.length - iv.length, iv.length);
+ for (int i = 0; i < IV.length - iv.length; i++)
+ {
+ IV[i] = 0;
+ }
+ }
+ else
+ {
+ System.arraycopy(iv, 0, IV, 0, IV.length);
+ }
+
+ reset();
+
+ // if null it's an IV changed only.
+ if (ivParam.getParameters() != null)
+ {
+ cipher.init(true, ivParam.getParameters());
+ }
+ }
+ else
+ {
+ reset();
+
+ // if it's null, key is to be reused.
+ if (params != null)
+ {
+ cipher.init(true, params);
+ }
+ }
+ }
+
+ /**
+ * return the algorithm name and mode.
+ *
+ * @return the name of the underlying algorithm followed by "/CFB"
+ * and the block size in bits.
+ */
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName() + "/CFB" + (blockSize * 8);
+ }
+
+ /**
+ * return the block size we are operating at.
+ *
+ * @return the block size we are operating at (in bytes).
+ */
+ public int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ /**
+ * Process one block of input from the array in and write it to
+ * the out array.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ return (encrypting) ? encryptBlock(in, inOff, out, outOff) : decryptBlock(in, inOff, out, outOff);
+ }
+
+ /**
+ * Do the appropriate processing for CFB mode encryption.
+ *
+ * @param in the array containing the data to be encrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ cipher.processBlock(cfbV, 0, cfbOutV, 0);
+
+ //
+ // XOR the cfbV with the plaintext producing the ciphertext
+ //
+ for (int i = 0; i < blockSize; i++)
+ {
+ out[outOff + i] = (byte)(cfbOutV[i] ^ in[inOff + i]);
+ }
+
+ //
+ // change over the input block.
+ //
+ System.arraycopy(cfbV, blockSize, cfbV, 0, cfbV.length - blockSize);
+ System.arraycopy(out, outOff, cfbV, cfbV.length - blockSize, blockSize);
+
+ return blockSize;
+ }
+
+ /**
+ * Do the appropriate processing for CFB mode decryption.
+ *
+ * @param in the array containing the data to be decrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ cipher.processBlock(cfbV, 0, cfbOutV, 0);
+
+ //
+ // change over the input block.
+ //
+ System.arraycopy(cfbV, blockSize, cfbV, 0, cfbV.length - blockSize);
+ System.arraycopy(in, inOff, cfbV, cfbV.length - blockSize, blockSize);
+
+ //
+ // XOR the cfbV with the ciphertext producing the plaintext
+ //
+ for (int i = 0; i < blockSize; i++)
+ {
+ out[outOff + i] = (byte)(cfbOutV[i] ^ in[inOff + i]);
+ }
+
+ return blockSize;
+ }
+
+ /**
+ * reset the chaining vector back to the IV and reset the underlying
+ * cipher.
+ */
+ public void reset()
+ {
+ System.arraycopy(IV, 0, cfbV, 0, IV.length);
+
+ cipher.reset();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java
new file mode 100644
index 00000000..b8e5b610
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java
@@ -0,0 +1,265 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+
+/**
+ * A Cipher Text Stealing (CTS) mode cipher. CTS allows block ciphers to
+ * be used to produce cipher text which is the same length as the plain text.
+ */
+public class CTSBlockCipher
+ extends BufferedBlockCipher
+{
+ private int blockSize;
+
+ /**
+ * Create a buffered block cipher that uses Cipher Text Stealing
+ *
+ * @param cipher the underlying block cipher this buffering object wraps.
+ */
+ public CTSBlockCipher(
+ BlockCipher cipher)
+ {
+ if ((cipher instanceof OFBBlockCipher) || (cipher instanceof CFBBlockCipher))
+ {
+ throw new IllegalArgumentException("CTSBlockCipher can only accept ECB, or CBC ciphers");
+ }
+
+ this.cipher = cipher;
+
+ blockSize = cipher.getBlockSize();
+
+ buf = new byte[blockSize * 2];
+ bufOff = 0;
+ }
+
+ /**
+ * return the size of the output buffer required for an update
+ * an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to update
+ * with len bytes of input.
+ */
+ public int getUpdateOutputSize(
+ int len)
+ {
+ int total = len + bufOff;
+ int leftOver = total % buf.length;
+
+ if (leftOver == 0)
+ {
+ return total - buf.length;
+ }
+
+ return total - leftOver;
+ }
+
+ /**
+ * return the size of the output buffer required for an update plus a
+ * doFinal with an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to update and doFinal
+ * with len bytes of input.
+ */
+ public int getOutputSize(
+ int len)
+ {
+ return len + bufOff;
+ }
+
+ /**
+ * process a single byte, producing an output block if neccessary.
+ *
+ * @param in the input byte.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ */
+ public int processByte(
+ byte in,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ int resultLen = 0;
+
+ if (bufOff == buf.length)
+ {
+ resultLen = cipher.processBlock(buf, 0, out, outOff);
+ System.arraycopy(buf, blockSize, buf, 0, blockSize);
+
+ bufOff = blockSize;
+ }
+
+ buf[bufOff++] = in;
+
+ return resultLen;
+ }
+
+ /**
+ * process an array of bytes, producing output if necessary.
+ *
+ * @param in the input byte array.
+ * @param inOff the offset at which the input data starts.
+ * @param len the number of bytes to be copied out of the input array.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ */
+ public int processBytes(
+ byte[] in,
+ int inOff,
+ int len,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if (len < 0)
+ {
+ throw new IllegalArgumentException("Can't have a negative input length!");
+ }
+
+ int blockSize = getBlockSize();
+ int length = getUpdateOutputSize(len);
+
+ if (length > 0)
+ {
+ if ((outOff + length) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+ }
+
+ int resultLen = 0;
+ int gapLen = buf.length - bufOff;
+
+ if (len > gapLen)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+ resultLen += cipher.processBlock(buf, 0, out, outOff);
+ System.arraycopy(buf, blockSize, buf, 0, blockSize);
+
+ bufOff = blockSize;
+
+ len -= gapLen;
+ inOff += gapLen;
+
+ while (len > blockSize)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, blockSize);
+ resultLen += cipher.processBlock(buf, 0, out, outOff + resultLen);
+ System.arraycopy(buf, blockSize, buf, 0, blockSize);
+
+ len -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ System.arraycopy(in, inOff, buf, bufOff, len);
+
+ bufOff += len;
+
+ return resultLen;
+ }
+
+ /**
+ * Process the last block in the buffer.
+ *
+ * @param out the array the block currently being held is copied into.
+ * @param outOff the offset at which the copying starts.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there is insufficient space in out for
+ * the output.
+ * @exception IllegalStateException if the underlying cipher is not
+ * initialised.
+ * @exception InvalidCipherTextException if cipher text decrypts wrongly (in
+ * case the exception will never get thrown).
+ */
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException, InvalidCipherTextException
+ {
+ if (bufOff + outOff > out.length)
+ {
+ throw new DataLengthException("output buffer to small in doFinal");
+ }
+
+ int blockSize = cipher.getBlockSize();
+ int len = bufOff - blockSize;
+ byte[] block = new byte[blockSize];
+
+ if (forEncryption)
+ {
+ cipher.processBlock(buf, 0, block, 0);
+
+ if (bufOff < blockSize)
+ {
+ throw new DataLengthException("need at least one block of input for CTS");
+ }
+
+ for (int i = bufOff; i != buf.length; i++)
+ {
+ buf[i] = block[i - blockSize];
+ }
+
+ for (int i = blockSize; i != bufOff; i++)
+ {
+ buf[i] ^= block[i - blockSize];
+ }
+
+ if (cipher instanceof CBCBlockCipher)
+ {
+ BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
+
+ c.processBlock(buf, blockSize, out, outOff);
+ }
+ else
+ {
+ cipher.processBlock(buf, blockSize, out, outOff);
+ }
+
+ System.arraycopy(block, 0, out, outOff + blockSize, len);
+ }
+ else
+ {
+ byte[] lastBlock = new byte[blockSize];
+
+ if (cipher instanceof CBCBlockCipher)
+ {
+ BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
+
+ c.processBlock(buf, 0, block, 0);
+ }
+ else
+ {
+ cipher.processBlock(buf, 0, block, 0);
+ }
+
+ for (int i = blockSize; i != bufOff; i++)
+ {
+ lastBlock[i - blockSize] = (byte)(block[i - blockSize] ^ buf[i]);
+ }
+
+ System.arraycopy(buf, blockSize, block, 0, len);
+
+ cipher.processBlock(block, 0, out, outOff);
+ System.arraycopy(lastBlock, 0, out, outOff + blockSize, len);
+ }
+
+ int offset = bufOff;
+
+ reset();
+
+ return offset;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java
new file mode 100644
index 00000000..4999caae
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java
@@ -0,0 +1,368 @@
+package org.bouncycastle.crypto.modes;
+
+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.CMac;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A Two-Pass Authenticated-Encryption Scheme Optimized for Simplicity and
+ * Efficiency - by M. Bellare, P. Rogaway, D. Wagner.
+ *
+ * http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf
+ *
+ * EAX is an AEAD scheme based on CTR and OMAC1/CMAC, that uses a single block
+ * cipher to encrypt and authenticate data. It's on-line (the length of a
+ * message isn't needed to begin processing it), has good performances, it's
+ * simple and provably secure (provided the underlying block cipher is secure).
+ *
+ * Of course, this implementations is NOT thread-safe.
+ */
+public class EAXBlockCipher
+ implements AEADBlockCipher
+{
+ private static final byte nTAG = 0x0;
+
+ private static final byte hTAG = 0x1;
+
+ private static final byte cTAG = 0x2;
+
+ private SICBlockCipher cipher;
+
+ private boolean forEncryption;
+
+ private int blockSize;
+
+ private Mac mac;
+
+ private byte[] nonceMac;
+ private byte[] associatedTextMac;
+ private byte[] macBlock;
+
+ private int macSize;
+ private byte[] bufBlock;
+ private int bufOff;
+
+ private boolean cipherInitialized;
+ private byte[] initialAssociatedText;
+
+ /**
+ * Constructor that accepts an instance of a block cipher engine.
+ *
+ * @param cipher the engine to use
+ */
+ public EAXBlockCipher(BlockCipher cipher)
+ {
+ blockSize = cipher.getBlockSize();
+ mac = new CMac(cipher);
+ macBlock = new byte[blockSize];
+ bufBlock = new byte[blockSize * 2];
+ associatedTextMac = new byte[mac.getMacSize()];
+ nonceMac = new byte[mac.getMacSize()];
+ this.cipher = new SICBlockCipher(cipher);
+ }
+
+ public String getAlgorithmName()
+ {
+ return cipher.getUnderlyingCipher().getAlgorithmName() + "/EAX";
+ }
+
+ public BlockCipher getUnderlyingCipher()
+ {
+ return cipher.getUnderlyingCipher();
+ }
+
+ public int getBlockSize()
+ {
+ return cipher.getBlockSize();
+ }
+
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ this.forEncryption = forEncryption;
+
+ byte[] nonce;
+ CipherParameters keyParam;
+
+ 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 = mac.getMacSize() / 2;
+ keyParam = param.getParameters();
+ }
+ else
+ {
+ throw new IllegalArgumentException("invalid parameters passed to EAX");
+ }
+
+ byte[] tag = new byte[blockSize];
+
+ // Key reuse implemented in CBC mode of underlying CMac
+ mac.init(keyParam);
+
+ tag[blockSize - 1] = nTAG;
+ mac.update(tag, 0, blockSize);
+ mac.update(nonce, 0, nonce.length);
+ mac.doFinal(nonceMac, 0);
+
+ tag[blockSize - 1] = hTAG;
+ mac.update(tag, 0, blockSize);
+
+ if (initialAssociatedText != null)
+ {
+ processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
+ }
+
+ // Same BlockCipher underlies this and the mac, so reuse last key on cipher
+ cipher.init(true, new ParametersWithIV(null, nonceMac));
+ }
+
+ private void initCipher()
+ {
+ if (cipherInitialized)
+ {
+ return;
+ }
+
+ cipherInitialized = true;
+
+ mac.doFinal(associatedTextMac, 0);
+
+ byte[] tag = new byte[blockSize];
+ tag[blockSize - 1] = cTAG;
+ mac.update(tag, 0, blockSize);
+ }
+
+ private void calculateMac()
+ {
+ byte[] outC = new byte[blockSize];
+ mac.doFinal(outC, 0);
+
+ for (int i = 0; i < macBlock.length; i++)
+ {
+ macBlock[i] = (byte)(nonceMac[i] ^ associatedTextMac[i] ^ outC[i]);
+ }
+ }
+
+ public void reset()
+ {
+ reset(true);
+ }
+
+ private void reset(
+ boolean clearMac)
+ {
+ cipher.reset(); // TODO Redundant since the mac will reset it?
+ mac.reset();
+
+ bufOff = 0;
+ Arrays.fill(bufBlock, (byte)0);
+
+ if (clearMac)
+ {
+ Arrays.fill(macBlock, (byte)0);
+ }
+
+ byte[] tag = new byte[blockSize];
+ tag[blockSize - 1] = hTAG;
+ mac.update(tag, 0, blockSize);
+
+ cipherInitialized = false;
+
+ if (initialAssociatedText != null)
+ {
+ processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
+ }
+ }
+
+ public void processAADByte(byte in)
+ {
+ if (cipherInitialized)
+ {
+ throw new IllegalStateException("AAD data cannot be added after encryption/decription processing has begun.");
+ }
+ mac.update(in);
+ }
+
+ public void processAADBytes(byte[] in, int inOff, int len)
+ {
+ if (cipherInitialized)
+ {
+ throw new IllegalStateException("AAD data cannot be added after encryption/decription processing has begun.");
+ }
+ mac.update(in, inOff, len);
+ }
+
+ public int processByte(byte in, byte[] out, int outOff)
+ throws DataLengthException
+ {
+ initCipher();
+
+ return process(in, out, outOff);
+ }
+
+ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
+ throws DataLengthException
+ {
+ initCipher();
+
+ int resultLen = 0;
+
+ for (int i = 0; i != len; i++)
+ {
+ resultLen += process(in[inOff + i], out, outOff + resultLen);
+ }
+
+ return resultLen;
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ throws IllegalStateException, InvalidCipherTextException
+ {
+ initCipher();
+
+ int extra = bufOff;
+ byte[] tmp = new byte[bufBlock.length];
+
+ bufOff = 0;
+
+ if (forEncryption)
+ {
+ cipher.processBlock(bufBlock, 0, tmp, 0);
+ cipher.processBlock(bufBlock, blockSize, tmp, blockSize);
+
+ System.arraycopy(tmp, 0, out, outOff, extra);
+
+ mac.update(tmp, 0, extra);
+
+ calculateMac();
+
+ System.arraycopy(macBlock, 0, out, outOff + extra, macSize);
+
+ reset(false);
+
+ return extra + macSize;
+ }
+ else
+ {
+ if (extra > macSize)
+ {
+ mac.update(bufBlock, 0, extra - macSize);
+
+ cipher.processBlock(bufBlock, 0, tmp, 0);
+ cipher.processBlock(bufBlock, blockSize, tmp, blockSize);
+
+ System.arraycopy(tmp, 0, out, outOff, extra - macSize);
+ }
+
+ calculateMac();
+
+ if (!verifyMac(bufBlock, extra - macSize))
+ {
+ throw new InvalidCipherTextException("mac check in EAX failed");
+ }
+
+ reset(false);
+
+ return extra - macSize;
+ }
+ }
+
+ public byte[] getMac()
+ {
+ byte[] mac = new byte[macSize];
+
+ System.arraycopy(macBlock, 0, mac, 0, macSize);
+
+ return mac;
+ }
+
+ public int getUpdateOutputSize(int len)
+ {
+ int totalData = len + bufOff;
+ if (!forEncryption)
+ {
+ if (totalData < macSize)
+ {
+ return 0;
+ }
+ totalData -= macSize;
+ }
+ return totalData - totalData % blockSize;
+ }
+
+ public int getOutputSize(int len)
+ {
+ int totalData = len + bufOff;
+
+ if (forEncryption)
+ {
+ return totalData + macSize;
+ }
+
+ return totalData < macSize ? 0 : totalData - macSize;
+ }
+
+ private int process(byte b, byte[] out, int outOff)
+ {
+ bufBlock[bufOff++] = b;
+
+ if (bufOff == bufBlock.length)
+ {
+ // TODO Could move the processByte(s) calls to here
+// initCipher();
+
+ int size;
+
+ if (forEncryption)
+ {
+ size = cipher.processBlock(bufBlock, 0, out, outOff);
+
+ mac.update(out, outOff, blockSize);
+ }
+ else
+ {
+ mac.update(bufBlock, 0, blockSize);
+
+ size = cipher.processBlock(bufBlock, 0, out, outOff);
+ }
+
+ bufOff = blockSize;
+ System.arraycopy(bufBlock, blockSize, bufBlock, 0, blockSize);
+
+ return size;
+ }
+
+ return 0;
+ }
+
+ private boolean verifyMac(byte[] mac, int off)
+ {
+ int nonEqual = 0;
+
+ for (int i = 0; i < macSize; i++)
+ {
+ nonEqual |= (macBlock[i] ^ mac[off + i]);
+ }
+
+ return nonEqual == 0;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java
new file mode 100644
index 00000000..9e617ec9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java
@@ -0,0 +1,574 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.modes.gcm.GCMExponentiator;
+import org.bouncycastle.crypto.modes.gcm.GCMMultiplier;
+import org.bouncycastle.crypto.modes.gcm.Tables1kGCMExponentiator;
+import org.bouncycastle.crypto.modes.gcm.Tables8kGCMMultiplier;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.util.Pack;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Implements the Galois/Counter mode (GCM) detailed in
+ * NIST Special Publication 800-38D.
+ */
+public class GCMBlockCipher
+ implements AEADBlockCipher
+{
+ private static final int BLOCK_SIZE = 16;
+
+ // not final due to a compiler bug
+ private BlockCipher cipher;
+ private GCMMultiplier multiplier;
+ private GCMExponentiator exp;
+
+ // These fields are set by init and not modified by processing
+ private boolean forEncryption;
+ private int macSize;
+ private byte[] nonce;
+ private byte[] initialAssociatedText;
+ private byte[] H;
+ private byte[] J0;
+
+ // These fields are modified during processing
+ private byte[] bufBlock;
+ private byte[] macBlock;
+ private byte[] S, S_at, S_atPre;
+ private byte[] counter;
+ private int bufOff;
+ private long totalLength;
+ private byte[] atBlock;
+ private int atBlockPos;
+ private long atLength;
+ private long atLengthPre;
+
+ public GCMBlockCipher(BlockCipher c)
+ {
+ this(c, null);
+ }
+
+ public GCMBlockCipher(BlockCipher c, GCMMultiplier m)
+ {
+ if (c.getBlockSize() != BLOCK_SIZE)
+ {
+ throw new IllegalArgumentException(
+ "cipher required with a block size of " + BLOCK_SIZE + ".");
+ }
+
+ if (m == null)
+ {
+ // TODO Consider a static property specifying default multiplier
+ m = new Tables8kGCMMultiplier();
+ }
+
+ this.cipher = c;
+ this.multiplier = m;
+ }
+
+ public BlockCipher getUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName() + "/GCM";
+ }
+
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ this.forEncryption = forEncryption;
+ this.macBlock = null;
+
+ KeyParameter keyParam;
+
+ if (params instanceof AEADParameters)
+ {
+ AEADParameters param = (AEADParameters)params;
+
+ nonce = param.getNonce();
+ initialAssociatedText = param.getAssociatedText();
+
+ int macSizeBits = param.getMacSize();
+ if (macSizeBits < 96 || macSizeBits > 128 || macSizeBits % 8 != 0)
+ {
+ throw new IllegalArgumentException("Invalid value for MAC size: " + macSizeBits);
+ }
+
+ macSize = macSizeBits / 8;
+ keyParam = param.getKey();
+ }
+ else if (params instanceof ParametersWithIV)
+ {
+ ParametersWithIV param = (ParametersWithIV)params;
+
+ nonce = param.getIV();
+ initialAssociatedText = null;
+ macSize = 16;
+ keyParam = (KeyParameter)param.getParameters();
+ }
+ else
+ {
+ throw new IllegalArgumentException("invalid parameters passed to GCM");
+ }
+
+ int bufLength = forEncryption ? BLOCK_SIZE : (BLOCK_SIZE + macSize);
+ this.bufBlock = new byte[bufLength];
+
+ if (nonce == null || nonce.length < 1)
+ {
+ throw new IllegalArgumentException("IV must be at least 1 byte");
+ }
+
+ // TODO This should be configurable by init parameters
+ // (but must be 16 if nonce length not 12) (BLOCK_SIZE?)
+// this.tagLength = 16;
+
+ // Cipher always used in forward mode
+ // if keyParam is null we're reusing the last key.
+ if (keyParam != null)
+ {
+ cipher.init(true, keyParam);
+
+ this.H = new byte[BLOCK_SIZE];
+ cipher.processBlock(H, 0, H, 0);
+
+ // GCMMultiplier tables don't change unless the key changes (and are expensive to init)
+ multiplier.init(H);
+ exp = null;
+ }
+
+ this.J0 = new byte[BLOCK_SIZE];
+
+ if (nonce.length == 12)
+ {
+ System.arraycopy(nonce, 0, J0, 0, nonce.length);
+ this.J0[BLOCK_SIZE - 1] = 0x01;
+ }
+ else
+ {
+ gHASH(J0, nonce, nonce.length);
+ byte[] X = new byte[BLOCK_SIZE];
+ Pack.longToBigEndian((long)nonce.length * 8, X, 8);
+ gHASHBlock(J0, X);
+ }
+
+ this.S = new byte[BLOCK_SIZE];
+ this.S_at = new byte[BLOCK_SIZE];
+ this.S_atPre = new byte[BLOCK_SIZE];
+ this.atBlock = new byte[BLOCK_SIZE];
+ this.atBlockPos = 0;
+ this.atLength = 0;
+ this.atLengthPre = 0;
+ this.counter = Arrays.clone(J0);
+ this.bufOff = 0;
+ this.totalLength = 0;
+
+ if (initialAssociatedText != null)
+ {
+ processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
+ }
+ }
+
+ public byte[] getMac()
+ {
+ return Arrays.clone(macBlock);
+ }
+
+ public int getOutputSize(int len)
+ {
+ int totalData = len + bufOff;
+
+ if (forEncryption)
+ {
+ return totalData + macSize;
+ }
+
+ return totalData < macSize ? 0 : totalData - macSize;
+ }
+
+ public int getUpdateOutputSize(int len)
+ {
+ int totalData = len + bufOff;
+ if (!forEncryption)
+ {
+ if (totalData < macSize)
+ {
+ return 0;
+ }
+ totalData -= macSize;
+ }
+ return totalData - totalData % BLOCK_SIZE;
+ }
+
+ public void processAADByte(byte in)
+ {
+ atBlock[atBlockPos] = in;
+ if (++atBlockPos == BLOCK_SIZE)
+ {
+ // Hash each block as it fills
+ gHASHBlock(S_at, atBlock);
+ atBlockPos = 0;
+ atLength += BLOCK_SIZE;
+ }
+ }
+
+ public void processAADBytes(byte[] in, int inOff, int len)
+ {
+ for (int i = 0; i < len; ++i)
+ {
+ atBlock[atBlockPos] = in[inOff + i];
+ if (++atBlockPos == BLOCK_SIZE)
+ {
+ // Hash each block as it fills
+ gHASHBlock(S_at, atBlock);
+ atBlockPos = 0;
+ atLength += BLOCK_SIZE;
+ }
+ }
+ }
+
+ private void initCipher()
+ {
+ if (atLength > 0)
+ {
+ System.arraycopy(S_at, 0, S_atPre, 0, BLOCK_SIZE);
+ atLengthPre = atLength;
+ }
+
+ // Finish hash for partial AAD block
+ if (atBlockPos > 0)
+ {
+ gHASHPartial(S_atPre, atBlock, 0, atBlockPos);
+ atLengthPre += atBlockPos;
+ }
+
+ if (atLengthPre > 0)
+ {
+ System.arraycopy(S_atPre, 0, S, 0, BLOCK_SIZE);
+ }
+ }
+
+ public int processByte(byte in, byte[] out, int outOff)
+ throws DataLengthException
+ {
+ bufBlock[bufOff] = in;
+ if (++bufOff == bufBlock.length)
+ {
+ outputBlock(out, outOff);
+ return BLOCK_SIZE;
+ }
+ return 0;
+ }
+
+ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
+ throws DataLengthException
+ {
+ int resultLen = 0;
+
+ for (int i = 0; i < len; ++i)
+ {
+ bufBlock[bufOff] = in[inOff + i];
+ if (++bufOff == bufBlock.length)
+ {
+ outputBlock(out, outOff + resultLen);
+ resultLen += BLOCK_SIZE;
+ }
+ }
+
+ return resultLen;
+ }
+
+ private void outputBlock(byte[] output, int offset)
+ {
+ if (totalLength == 0)
+ {
+ initCipher();
+ }
+ gCTRBlock(bufBlock, output, offset);
+ if (forEncryption)
+ {
+ bufOff = 0;
+ }
+ else
+ {
+ System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, macSize);
+ bufOff = macSize;
+ }
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ throws IllegalStateException, InvalidCipherTextException
+ {
+ if (totalLength == 0)
+ {
+ initCipher();
+ }
+
+ int extra = bufOff;
+ if (!forEncryption)
+ {
+ if (extra < macSize)
+ {
+ throw new InvalidCipherTextException("data too short");
+ }
+ extra -= macSize;
+ }
+
+ if (extra > 0)
+ {
+ gCTRPartial(bufBlock, 0, extra, out, outOff);
+ }
+
+ atLength += atBlockPos;
+
+ if (atLength > atLengthPre)
+ {
+ /*
+ * Some AAD was sent after the cipher started. We determine the difference b/w the hash value
+ * we actually used when the cipher started (S_atPre) and the final hash value calculated (S_at).
+ * Then we carry this difference forward by multiplying by H^c, where c is the number of (full or
+ * partial) cipher-text blocks produced, and adjust the current hash.
+ */
+
+ // Finish hash for partial AAD block
+ if (atBlockPos > 0)
+ {
+ gHASHPartial(S_at, atBlock, 0, atBlockPos);
+ }
+
+ // Find the difference between the AAD hashes
+ if (atLengthPre > 0)
+ {
+ xor(S_at, S_atPre);
+ }
+
+ // Number of cipher-text blocks produced
+ long c = ((totalLength * 8) + 127) >>> 7;
+
+ // Calculate the adjustment factor
+ byte[] H_c = new byte[16];
+ if (exp == null)
+ {
+ exp = new Tables1kGCMExponentiator();
+ exp.init(H);
+ }
+ exp.exponentiateX(c, H_c);
+
+ // Carry the difference forward
+ multiply(S_at, H_c);
+
+ // Adjust the current hash
+ xor(S, S_at);
+ }
+
+ // Final gHASH
+ byte[] X = new byte[BLOCK_SIZE];
+ Pack.longToBigEndian(atLength * 8, X, 0);
+ Pack.longToBigEndian(totalLength * 8, X, 8);
+
+ gHASHBlock(S, X);
+
+ // TODO Fix this if tagLength becomes configurable
+ // T = MSBt(GCTRk(J0,S))
+ byte[] tag = new byte[BLOCK_SIZE];
+ cipher.processBlock(J0, 0, tag, 0);
+ xor(tag, S);
+
+ int resultLen = extra;
+
+ // We place into macBlock our calculated value for T
+ this.macBlock = new byte[macSize];
+ System.arraycopy(tag, 0, macBlock, 0, macSize);
+
+ if (forEncryption)
+ {
+ // Append T to the message
+ System.arraycopy(macBlock, 0, out, outOff + bufOff, macSize);
+ resultLen += macSize;
+ }
+ else
+ {
+ // Retrieve the T value from the message and compare to calculated one
+ byte[] msgMac = new byte[macSize];
+ System.arraycopy(bufBlock, extra, msgMac, 0, macSize);
+ if (!Arrays.constantTimeAreEqual(this.macBlock, msgMac))
+ {
+ throw new InvalidCipherTextException("mac check in GCM failed");
+ }
+ }
+
+ reset(false);
+
+ return resultLen;
+ }
+
+ public void reset()
+ {
+ reset(true);
+ }
+
+ private void reset(
+ boolean clearMac)
+ {
+ cipher.reset();
+
+ S = new byte[BLOCK_SIZE];
+ S_at = new byte[BLOCK_SIZE];
+ S_atPre = new byte[BLOCK_SIZE];
+ atBlock = new byte[BLOCK_SIZE];
+ atBlockPos = 0;
+ atLength = 0;
+ atLengthPre = 0;
+ counter = Arrays.clone(J0);
+ bufOff = 0;
+ totalLength = 0;
+
+ if (bufBlock != null)
+ {
+ Arrays.fill(bufBlock, (byte)0);
+ }
+
+ if (clearMac)
+ {
+ macBlock = null;
+ }
+
+ if (initialAssociatedText != null)
+ {
+ processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
+ }
+ }
+
+ private void gCTRBlock(byte[] block, byte[] out, int outOff)
+ {
+ byte[] tmp = getNextCounterBlock();
+
+ xor(tmp, block);
+ System.arraycopy(tmp, 0, out, outOff, BLOCK_SIZE);
+
+ gHASHBlock(S, forEncryption ? tmp : block);
+
+ totalLength += BLOCK_SIZE;
+ }
+
+ private void gCTRPartial(byte[] buf, int off, int len, byte[] out, int outOff)
+ {
+ byte[] tmp = getNextCounterBlock();
+
+ xor(tmp, buf, off, len);
+ System.arraycopy(tmp, 0, out, outOff, len);
+
+ gHASHPartial(S, forEncryption ? tmp : buf, 0, len);
+
+ totalLength += len;
+ }
+
+ private void gHASH(byte[] Y, byte[] b, int len)
+ {
+ for (int pos = 0; pos < len; pos += BLOCK_SIZE)
+ {
+ int num = Math.min(len - pos, BLOCK_SIZE);
+ gHASHPartial(Y, b, pos, num);
+ }
+ }
+
+ private void gHASHBlock(byte[] Y, byte[] b)
+ {
+ xor(Y, b);
+ multiplier.multiplyH(Y);
+ }
+
+ private void gHASHPartial(byte[] Y, byte[] b, int off, int len)
+ {
+ xor(Y, b, off, len);
+ multiplier.multiplyH(Y);
+ }
+
+ private byte[] getNextCounterBlock()
+ {
+ for (int i = 15; i >= 12; --i)
+ {
+ byte b = (byte)((counter[i] + 1) & 0xff);
+ counter[i] = b;
+
+ if (b != 0)
+ {
+ break;
+ }
+ }
+
+ byte[] tmp = new byte[BLOCK_SIZE];
+ // TODO Sure would be nice if ciphers could operate on int[]
+ cipher.processBlock(counter, 0, tmp, 0);
+ return tmp;
+ }
+
+ private static void multiply(byte[] block, byte[] val)
+ {
+ byte[] tmp = Arrays.clone(block);
+ byte[] c = new byte[16];
+
+ for (int i = 0; i < 16; ++i)
+ {
+ byte bits = val[i];
+ for (int j = 7; j >= 0; --j)
+ {
+ if ((bits & (1 << j)) != 0)
+ {
+ xor(c, tmp);
+ }
+
+ boolean lsb = (tmp[15] & 1) != 0;
+ shiftRight(tmp);
+ if (lsb)
+ {
+ // R = new byte[]{ 0xe1, ... };
+// xor(v, R);
+ tmp[0] ^= (byte)0xe1;
+ }
+ }
+ }
+
+ System.arraycopy(c, 0, block, 0, 16);
+ }
+
+ private static void shiftRight(byte[] block)
+ {
+ int i = 0;
+ int bit = 0;
+ for (;;)
+ {
+ int b = block[i] & 0xff;
+ block[i] = (byte) ((b >>> 1) | bit);
+ if (++i == 16)
+ {
+ break;
+ }
+ bit = (b & 1) << 7;
+ }
+ }
+
+ private static void xor(byte[] block, byte[] val)
+ {
+ for (int i = 15; i >= 0; --i)
+ {
+ block[i] ^= val[i];
+ }
+ }
+
+ private static void xor(byte[] block, byte[] val, int off, int len)
+ {
+ while (len-- > 0)
+ {
+ block[len] ^= val[off + len];
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java
new file mode 100644
index 00000000..1178974f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java
@@ -0,0 +1,234 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+
+/**
+ * implements the GOST 28147 OFB counter mode (GCTR).
+ */
+public class GOFBBlockCipher
+ implements BlockCipher
+{
+ private byte[] IV;
+ private byte[] ofbV;
+ private byte[] ofbOutV;
+
+ private final int blockSize;
+ private final BlockCipher cipher;
+
+ boolean firstStep = true;
+ int N3;
+ int N4;
+ static final int C1 = 16843012; //00000001000000010000000100000100
+ static final int C2 = 16843009; //00000001000000010000000100000001
+
+
+ /**
+ * Basic constructor.
+ *
+ * @param cipher the block cipher to be used as the basis of the
+ * counter mode (must have a 64 bit block size).
+ */
+ public GOFBBlockCipher(
+ BlockCipher cipher)
+ {
+ this.cipher = cipher;
+ this.blockSize = cipher.getBlockSize();
+
+ if (blockSize != 8)
+ {
+ throw new IllegalArgumentException("GCTR only for 64 bit block ciphers");
+ }
+
+ this.IV = new byte[cipher.getBlockSize()];
+ this.ofbV = new byte[cipher.getBlockSize()];
+ this.ofbOutV = new byte[cipher.getBlockSize()];
+ }
+
+ /**
+ * return the underlying block cipher that we are wrapping.
+ *
+ * @return the underlying block cipher that we are wrapping.
+ */
+ public BlockCipher getUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+ /**
+ * Initialise the cipher and, possibly, the initialisation vector (IV).
+ * If an IV isn't passed as part of the parameter, the IV will be all zeros.
+ * An IV which is too short is handled in FIPS compliant fashion.
+ *
+ * @param encrypting if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param params the key and other data required by the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting, //ignored by this CTR mode
+ CipherParameters params)
+ throws IllegalArgumentException
+ {
+ firstStep = true;
+ N3 = 0;
+ N4 = 0;
+
+ if (params instanceof ParametersWithIV)
+ {
+ ParametersWithIV ivParam = (ParametersWithIV)params;
+ byte[] iv = ivParam.getIV();
+
+ if (iv.length < IV.length)
+ {
+ // prepend the supplied IV with zeros (per FIPS PUB 81)
+ System.arraycopy(iv, 0, IV, IV.length - iv.length, iv.length);
+ for (int i = 0; i < IV.length - iv.length; i++)
+ {
+ IV[i] = 0;
+ }
+ }
+ else
+ {
+ System.arraycopy(iv, 0, IV, 0, IV.length);
+ }
+
+ reset();
+
+ // if params is null we reuse the current working key.
+ if (ivParam.getParameters() != null)
+ {
+ cipher.init(true, ivParam.getParameters());
+ }
+ }
+ else
+ {
+ reset();
+
+ // if params is null we reuse the current working key.
+ if (params != null)
+ {
+ cipher.init(true, params);
+ }
+ }
+ }
+
+ /**
+ * return the algorithm name and mode.
+ *
+ * @return the name of the underlying algorithm followed by "/GCTR"
+ * and the block size in bits
+ */
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName() + "/GCTR";
+ }
+
+
+ /**
+ * return the block size we are operating at (in bytes).
+ *
+ * @return the block size we are operating at (in bytes).
+ */
+ public int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ /**
+ * Process one block of input from the array in and write it to
+ * the out array.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ if (firstStep)
+ {
+ firstStep = false;
+ cipher.processBlock(ofbV, 0, ofbOutV, 0);
+ N3 = bytesToint(ofbOutV, 0);
+ N4 = bytesToint(ofbOutV, 4);
+ }
+ N3 += C2;
+ N4 += C1;
+ intTobytes(N3, ofbV, 0);
+ intTobytes(N4, ofbV, 4);
+
+ cipher.processBlock(ofbV, 0, ofbOutV, 0);
+
+ //
+ // XOR the ofbV with the plaintext producing the cipher text (and
+ // the next input block).
+ //
+ for (int i = 0; i < blockSize; i++)
+ {
+ out[outOff + i] = (byte)(ofbOutV[i] ^ in[inOff + i]);
+ }
+
+ //
+ // change over the input block.
+ //
+ System.arraycopy(ofbV, blockSize, ofbV, 0, ofbV.length - blockSize);
+ System.arraycopy(ofbOutV, 0, ofbV, ofbV.length - blockSize, blockSize);
+
+ return blockSize;
+ }
+
+ /**
+ * reset the feedback vector back to the IV and reset the underlying
+ * cipher.
+ */
+ public void reset()
+ {
+ System.arraycopy(IV, 0, ofbV, 0, IV.length);
+
+ cipher.reset();
+ }
+
+ //array of bytes to type int
+ private int bytesToint(
+ byte[] in,
+ int inOff)
+ {
+ return ((in[inOff + 3] << 24) & 0xff000000) + ((in[inOff + 2] << 16) & 0xff0000) +
+ ((in[inOff + 1] << 8) & 0xff00) + (in[inOff] & 0xff);
+ }
+
+ //int to array of bytes
+ private void intTobytes(
+ int num,
+ byte[] out,
+ int outOff)
+ {
+ out[outOff + 3] = (byte)(num >>> 24);
+ out[outOff + 2] = (byte)(num >>> 16);
+ out[outOff + 1] = (byte)(num >>> 8);
+ out[outOff] = (byte)num;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java
new file mode 100644
index 00000000..d4d29104
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java
@@ -0,0 +1,581 @@
+package org.bouncycastle.crypto.modes;
+
+import java.util.Vector;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * An implementation of the "work in progress" Internet-Draft <a
+ * href="http://tools.ietf.org/html/draft-irtf-cfrg-ocb-00">The OCB Authenticated-Encryption
+ * Algorithm</a>, licensed per:
+ * <p/>
+ * <blockquote> <a href="http://www.cs.ucdavis.edu/~rogaway/ocb/license1.pdf">License for
+ * Open-Source Software Implementations of OCB</a> (Jan 9, 2013) &mdash; &ldquo;License 1&rdquo; <br>
+ * Under this license, you are authorized to make, use, and distribute open-source software
+ * implementations of OCB. This license terminates for you if you sue someone over their open-source
+ * software implementation of OCB claiming that you have a patent covering their implementation.
+ * <p/>
+ * This is a non-binding summary of a legal document (the link above). The parameters of the license
+ * are specified in the license document and that document is controlling. </blockquote>
+ */
+public class OCBBlockCipher
+ implements AEADBlockCipher
+{
+
+ private static final int BLOCK_SIZE = 16;
+
+ private BlockCipher hashCipher;
+ private BlockCipher mainCipher;
+
+ /*
+ * CONFIGURATION
+ */
+ private boolean forEncryption;
+ private int macSize;
+ private byte[] initialAssociatedText;
+
+ /*
+ * KEY-DEPENDENT
+ */
+ // NOTE: elements are lazily calculated
+ private Vector L;
+ private byte[] L_Asterisk, L_Dollar;
+
+ /*
+ * NONCE-DEPENDENT
+ */
+ private byte[] OffsetMAIN_0;
+
+ /*
+ * PER-ENCRYPTION/DECRYPTION
+ */
+ private byte[] hashBlock, mainBlock;
+ private int hashBlockPos, mainBlockPos;
+ private long hashBlockCount, mainBlockCount;
+ private byte[] OffsetHASH;
+ private byte[] Sum;
+ private byte[] OffsetMAIN;
+ private byte[] Checksum;
+
+ // NOTE: The MAC value is preserved after doFinal
+ private byte[] macBlock;
+
+ public OCBBlockCipher(BlockCipher hashCipher, BlockCipher mainCipher)
+ {
+ if (hashCipher == null)
+ {
+ throw new IllegalArgumentException("'hashCipher' cannot be null");
+ }
+ if (hashCipher.getBlockSize() != BLOCK_SIZE)
+ {
+ throw new IllegalArgumentException("'hashCipher' must have a block size of "
+ + BLOCK_SIZE);
+ }
+ if (mainCipher == null)
+ {
+ throw new IllegalArgumentException("'mainCipher' cannot be null");
+ }
+ if (mainCipher.getBlockSize() != BLOCK_SIZE)
+ {
+ throw new IllegalArgumentException("'mainCipher' must have a block size of "
+ + BLOCK_SIZE);
+ }
+
+ if (!hashCipher.getAlgorithmName().equals(mainCipher.getAlgorithmName()))
+ {
+ throw new IllegalArgumentException(
+ "'hashCipher' and 'mainCipher' must be the same algorithm");
+ }
+
+ this.hashCipher = hashCipher;
+ this.mainCipher = mainCipher;
+ }
+
+ public BlockCipher getUnderlyingCipher()
+ {
+ return mainCipher;
+ }
+
+ public String getAlgorithmName()
+ {
+ return mainCipher.getAlgorithmName() + "/OCB";
+ }
+
+ public void init(boolean forEncryption, CipherParameters parameters)
+ throws IllegalArgumentException
+ {
+
+ this.forEncryption = forEncryption;
+ this.macBlock = null;
+
+ KeyParameter keyParameter;
+
+ byte[] N;
+ if (parameters instanceof AEADParameters)
+ {
+ AEADParameters aeadParameters = (AEADParameters)parameters;
+
+ N = aeadParameters.getNonce();
+ initialAssociatedText = aeadParameters.getAssociatedText();
+
+ int macSizeBits = aeadParameters.getMacSize();
+ if (macSizeBits < 64 || macSizeBits > 128 || macSizeBits % 8 != 0)
+ {
+ throw new IllegalArgumentException("Invalid value for MAC size: " + macSizeBits);
+ }
+
+ macSize = macSizeBits / 8;
+ keyParameter = aeadParameters.getKey();
+ }
+ else if (parameters instanceof ParametersWithIV)
+ {
+ ParametersWithIV parametersWithIV = (ParametersWithIV)parameters;
+
+ N = parametersWithIV.getIV();
+ initialAssociatedText = null;
+ macSize = 16;
+ keyParameter = (KeyParameter)parametersWithIV.getParameters();
+ }
+ else
+ {
+ throw new IllegalArgumentException("invalid parameters passed to OCB");
+ }
+
+ this.hashBlock = new byte[16];
+ this.mainBlock = new byte[forEncryption ? BLOCK_SIZE : (BLOCK_SIZE + macSize)];
+
+ if (N == null)
+ {
+ N = new byte[0];
+ }
+
+ if (N.length > 16 || (N.length == 16 && (N[0] & 0x80) != 0))
+ {
+ /*
+ * NOTE: We don't just ignore bit 128 because it would hide from the caller the fact
+ * that two nonces differing only in bit 128 are not different.
+ */
+ throw new IllegalArgumentException("IV must be no more than 127 bits");
+ }
+
+ /*
+ * KEY-DEPENDENT INITIALISATION
+ */
+
+ // if keyParam is null we're reusing the last key.
+ if (keyParameter != null)
+ {
+ // TODO
+ }
+
+ // hashCipher always used in forward mode
+ hashCipher.init(true, keyParameter);
+ mainCipher.init(forEncryption, keyParameter);
+
+ this.L_Asterisk = new byte[16];
+ hashCipher.processBlock(L_Asterisk, 0, L_Asterisk, 0);
+
+ this.L_Dollar = OCB_double(L_Asterisk);
+
+ this.L = new Vector();
+ this.L.addElement(OCB_double(L_Dollar));
+
+ /*
+ * NONCE-DEPENDENT AND PER-ENCRYPTION/DECRYPTION INITIALISATION
+ */
+
+ byte[] nonce = new byte[16];
+ System.arraycopy(N, 0, nonce, nonce.length - N.length, N.length);
+ if (N.length == 16)
+ {
+ nonce[0] &= 0x80;
+ }
+ else
+ {
+ nonce[15 - N.length] = 1;
+ }
+
+ int bottom = nonce[15] & 0x3F;
+ // System.out.println("bottom: " + bottom);
+
+ byte[] Ktop = new byte[16];
+ nonce[15] &= 0xC0;
+ hashCipher.processBlock(nonce, 0, Ktop, 0);
+
+ byte[] Stretch = new byte[24];
+ System.arraycopy(Ktop, 0, Stretch, 0, 16);
+ for (int i = 0; i < 8; ++i)
+ {
+ Stretch[16 + i] = (byte)(Ktop[i] ^ Ktop[i + 1]);
+ }
+
+ this.OffsetMAIN_0 = new byte[16];
+ int bits = bottom % 8, bytes = bottom / 8;
+ if (bits == 0)
+ {
+ System.arraycopy(Stretch, bytes, OffsetMAIN_0, 0, 16);
+ }
+ else
+ {
+ for (int i = 0; i < 16; ++i)
+ {
+ int b1 = Stretch[bytes] & 0xff;
+ int b2 = Stretch[++bytes] & 0xff;
+ this.OffsetMAIN_0[i] = (byte)((b1 << bits) | (b2 >>> (8 - bits)));
+ }
+ }
+
+ this.hashBlockPos = 0;
+ this.mainBlockPos = 0;
+
+ this.hashBlockCount = 0;
+ this.mainBlockCount = 0;
+
+ this.OffsetHASH = new byte[16];
+ this.Sum = new byte[16];
+ this.OffsetMAIN = Arrays.clone(this.OffsetMAIN_0);
+ this.Checksum = new byte[16];
+
+ if (initialAssociatedText != null)
+ {
+ processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
+ }
+ }
+
+ public byte[] getMac()
+ {
+ return Arrays.clone(macBlock);
+ }
+
+ public int getOutputSize(int len)
+ {
+ int totalData = len + mainBlockPos;
+ if (forEncryption)
+ {
+ return totalData + macSize;
+ }
+ return totalData < macSize ? 0 : totalData - macSize;
+ }
+
+ public int getUpdateOutputSize(int len)
+ {
+ int totalData = len + mainBlockPos;
+ if (!forEncryption)
+ {
+ if (totalData < macSize)
+ {
+ return 0;
+ }
+ totalData -= macSize;
+ }
+ return totalData - totalData % BLOCK_SIZE;
+ }
+
+ public void processAADByte(byte input)
+ {
+ hashBlock[hashBlockPos] = input;
+ if (++hashBlockPos == hashBlock.length)
+ {
+ processHashBlock();
+ }
+ }
+
+ public void processAADBytes(byte[] input, int off, int len)
+ {
+ for (int i = 0; i < len; ++i)
+ {
+ hashBlock[hashBlockPos] = input[off + i];
+ if (++hashBlockPos == hashBlock.length)
+ {
+ processHashBlock();
+ }
+ }
+ }
+
+ public int processByte(byte input, byte[] output, int outOff)
+ throws DataLengthException
+ {
+ mainBlock[mainBlockPos] = input;
+ if (++mainBlockPos == mainBlock.length)
+ {
+ processMainBlock(output, outOff);
+ return BLOCK_SIZE;
+ }
+ return 0;
+ }
+
+ public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
+ throws DataLengthException
+ {
+
+ int resultLen = 0;
+
+ for (int i = 0; i < len; ++i)
+ {
+ mainBlock[mainBlockPos] = input[inOff + i];
+ if (++mainBlockPos == mainBlock.length)
+ {
+ processMainBlock(output, outOff + resultLen);
+ resultLen += BLOCK_SIZE;
+ }
+ }
+
+ return resultLen;
+ }
+
+ public int doFinal(byte[] output, int outOff)
+ throws IllegalStateException,
+ InvalidCipherTextException
+ {
+
+ /*
+ * For decryption, get the tag from the end of the message
+ */
+ byte[] tag = null;
+ if (!forEncryption)
+ {
+ if (mainBlockPos < macSize)
+ {
+ throw new InvalidCipherTextException("data too short");
+ }
+ mainBlockPos -= macSize;
+ tag = new byte[macSize];
+ System.arraycopy(mainBlock, mainBlockPos, tag, 0, macSize);
+ }
+
+ /*
+ * HASH: Process any final partial block; compute final hash value
+ */
+ if (hashBlockPos > 0)
+ {
+ OCB_extend(hashBlock, hashBlockPos);
+ updateHASH(L_Asterisk);
+ }
+
+ /*
+ * OCB-ENCRYPT/OCB-DECRYPT: Process any final partial block
+ */
+ if (mainBlockPos > 0)
+ {
+ if (forEncryption)
+ {
+ OCB_extend(mainBlock, mainBlockPos);
+ xor(Checksum, mainBlock);
+ }
+
+ xor(OffsetMAIN, L_Asterisk);
+
+ byte[] Pad = new byte[16];
+ hashCipher.processBlock(OffsetMAIN, 0, Pad, 0);
+
+ xor(mainBlock, Pad);
+
+ System.arraycopy(mainBlock, 0, output, outOff, mainBlockPos);
+
+ if (!forEncryption)
+ {
+ OCB_extend(mainBlock, mainBlockPos);
+ xor(Checksum, mainBlock);
+ }
+ }
+
+ /*
+ * OCB-ENCRYPT/OCB-DECRYPT: Compute raw tag
+ */
+ xor(Checksum, OffsetMAIN);
+ xor(Checksum, L_Dollar);
+ hashCipher.processBlock(Checksum, 0, Checksum, 0);
+ xor(Checksum, Sum);
+
+ this.macBlock = new byte[macSize];
+ System.arraycopy(Checksum, 0, macBlock, 0, macSize);
+
+ /*
+ * Validate or append tag and reset this cipher for the next run
+ */
+ int resultLen = mainBlockPos;
+
+ if (forEncryption)
+ {
+ // Append tag to the message
+ System.arraycopy(macBlock, 0, output, outOff + resultLen, macSize);
+ resultLen += macSize;
+ }
+ else
+ {
+ // Compare the tag from the message with the calculated one
+ if (!Arrays.constantTimeAreEqual(macBlock, tag))
+ {
+ throw new InvalidCipherTextException("mac check in OCB failed");
+ }
+ }
+
+ reset(false);
+
+ return resultLen;
+ }
+
+ public void reset()
+ {
+ reset(true);
+ }
+
+ protected void clear(byte[] bs)
+ {
+ if (bs != null)
+ {
+ Arrays.fill(bs, (byte)0);
+ }
+ }
+
+ protected byte[] getLSub(int n)
+ {
+ while (n >= L.size())
+ {
+ L.addElement(OCB_double((byte[])L.lastElement()));
+ }
+ return (byte[])L.elementAt(n);
+ }
+
+ protected void processHashBlock()
+ {
+ /*
+ * HASH: Process any whole blocks
+ */
+ updateHASH(getLSub(OCB_ntz(++hashBlockCount)));
+ hashBlockPos = 0;
+ }
+
+ protected void processMainBlock(byte[] output, int outOff)
+ {
+ /*
+ * OCB-ENCRYPT/OCB-DECRYPT: Process any whole blocks
+ */
+
+ if (forEncryption)
+ {
+ xor(Checksum, mainBlock);
+ mainBlockPos = 0;
+ }
+
+ xor(OffsetMAIN, getLSub(OCB_ntz(++mainBlockCount)));
+
+ xor(mainBlock, OffsetMAIN);
+ mainCipher.processBlock(mainBlock, 0, mainBlock, 0);
+ xor(mainBlock, OffsetMAIN);
+
+ System.arraycopy(mainBlock, 0, output, outOff, 16);
+
+ if (!forEncryption)
+ {
+ xor(Checksum, mainBlock);
+ System.arraycopy(mainBlock, BLOCK_SIZE, mainBlock, 0, macSize);
+ mainBlockPos = macSize;
+ }
+ }
+
+ protected void reset(boolean clearMac)
+ {
+
+ hashCipher.reset();
+ mainCipher.reset();
+
+ clear(hashBlock);
+ clear(mainBlock);
+
+ hashBlockPos = 0;
+ mainBlockPos = 0;
+
+ hashBlockCount = 0;
+ mainBlockCount = 0;
+
+ clear(OffsetHASH);
+ clear(Sum);
+ System.arraycopy(OffsetMAIN_0, 0, OffsetMAIN, 0, 16);
+ clear(Checksum);
+
+ if (clearMac)
+ {
+ macBlock = null;
+ }
+
+ if (initialAssociatedText != null)
+ {
+ processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
+ }
+ }
+
+ protected void updateHASH(byte[] LSub)
+ {
+ xor(OffsetHASH, LSub);
+ xor(hashBlock, OffsetHASH);
+ hashCipher.processBlock(hashBlock, 0, hashBlock, 0);
+ xor(Sum, hashBlock);
+ }
+
+ protected static byte[] OCB_double(byte[] block)
+ {
+ byte[] result = new byte[16];
+ int carry = shiftLeft(block, result);
+
+ /*
+ * NOTE: This construction is an attempt at a constant-time implementation.
+ */
+ result[15] ^= (0x87 >>> ((1 - carry) << 3));
+
+ return result;
+ }
+
+ protected static void OCB_extend(byte[] block, int pos)
+ {
+ block[pos] = (byte)0x80;
+ while (++pos < 16)
+ {
+ block[pos] = 0;
+ }
+ }
+
+ protected static int OCB_ntz(long x)
+ {
+ if (x == 0)
+ {
+ return 64;
+ }
+
+ int n = 0;
+ while ((x & 1L) == 0L)
+ {
+ ++n;
+ x >>= 1;
+ }
+ return n;
+ }
+
+ protected static int shiftLeft(byte[] block, byte[] output)
+ {
+ int i = 16;
+ int bit = 0;
+ while (--i >= 0)
+ {
+ int b = block[i] & 0xff;
+ output[i] = (byte)((b << 1) | bit);
+ bit = (b >>> 7) & 1;
+ }
+ return bit;
+ }
+
+ protected static void xor(byte[] block, byte[] val)
+ {
+ for (int i = 15; i >= 0; --i)
+ {
+ block[i] ^= val[i];
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/OFBBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/OFBBlockCipher.java
new file mode 100644
index 00000000..5297698f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/modes/OFBBlockCipher.java
@@ -0,0 +1,187 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+
+/**
+ * implements a Output-FeedBack (OFB) mode on top of a simple cipher.
+ */
+public class OFBBlockCipher
+ implements BlockCipher
+{
+ private byte[] IV;
+ private byte[] ofbV;
+ private byte[] ofbOutV;
+
+ private final int blockSize;
+ private final BlockCipher cipher;
+
+ /**
+ * Basic constructor.
+ *
+ * @param cipher the block cipher to be used as the basis of the
+ * feedback mode.
+ * @param blockSize the block size in bits (note: a multiple of 8)
+ */
+ public OFBBlockCipher(
+ BlockCipher cipher,
+ int blockSize)
+ {
+ this.cipher = cipher;
+ this.blockSize = blockSize / 8;
+
+ this.IV = new byte[cipher.getBlockSize()];
+ this.ofbV = new byte[cipher.getBlockSize()];
+ this.ofbOutV = new byte[cipher.getBlockSize()];
+ }
+
+ /**
+ * return the underlying block cipher that we are wrapping.
+ *
+ * @return the underlying block cipher that we are wrapping.
+ */
+ public BlockCipher getUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+ /**
+ * Initialise the cipher and, possibly, the initialisation vector (IV).
+ * If an IV isn't passed as part of the parameter, the IV will be all zeros.
+ * An IV which is too short is handled in FIPS compliant fashion.
+ *
+ * @param encrypting if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param params the key and other data required by the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting, //ignored by this OFB mode
+ CipherParameters params)
+ throws IllegalArgumentException
+ {
+ if (params instanceof ParametersWithIV)
+ {
+ ParametersWithIV ivParam = (ParametersWithIV)params;
+ byte[] iv = ivParam.getIV();
+
+ if (iv.length < IV.length)
+ {
+ // prepend the supplied IV with zeros (per FIPS PUB 81)
+ System.arraycopy(iv, 0, IV, IV.length - iv.length, iv.length);
+ for (int i = 0; i < IV.length - iv.length; i++)
+ {
+ IV[i] = 0;
+ }
+ }
+ else
+ {
+ System.arraycopy(iv, 0, IV, 0, IV.length);
+ }
+
+ reset();
+
+ // if null it's an IV changed only.
+ if (ivParam.getParameters() != null)
+ {
+ cipher.init(true, ivParam.getParameters());
+ }
+ }
+ else
+ {
+ reset();
+
+ // if it's null, key is to be reused.
+ if (params != null)
+ {
+ cipher.init(true, params);
+ }
+ }
+ }
+
+ /**
+ * return the algorithm name and mode.
+ *
+ * @return the name of the underlying algorithm followed by "/OFB"
+ * and the block size in bits
+ */
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName() + "/OFB" + (blockSize * 8);
+ }
+
+
+ /**
+ * return the block size we are operating at (in bytes).
+ *
+ * @return the block size we are operating at (in bytes).
+ */
+ public int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ /**
+ * Process one block of input from the array in and write it to
+ * the out array.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ cipher.processBlock(ofbV, 0, ofbOutV, 0);
+
+ //
+ // XOR the ofbV with the plaintext producing the cipher text (and
+ // the next input block).
+ //
+ for (int i = 0; i < blockSize; i++)
+ {
+ out[outOff + i] = (byte)(ofbOutV[i] ^ in[inOff + i]);
+ }
+
+ //
+ // change over the input block.
+ //
+ System.arraycopy(ofbV, blockSize, ofbV, 0, ofbV.length - blockSize);
+ System.arraycopy(ofbOutV, 0, ofbV, ofbV.length - blockSize, blockSize);
+
+ return blockSize;
+ }
+
+ /**
+ * reset the feedback vector back to the IV and reset the underlying
+ * cipher.
+ */
+ public void reset()
+ {
+ System.arraycopy(IV, 0, ofbV, 0, IV.length);
+
+ cipher.reset();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/OpenPGPCFBBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/OpenPGPCFBBlockCipher.java
new file mode 100644
index 00000000..e48731b3
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/modes/OpenPGPCFBBlockCipher.java
@@ -0,0 +1,312 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+
+/**
+ * Implements OpenPGP's rather strange version of Cipher-FeedBack (CFB) mode
+ * on top of a simple cipher. This class assumes the IV has been prepended
+ * to the data stream already, and just accomodates the reset after
+ * (blockSize + 2) bytes have been read.
+ * <p>
+ * For further info see <a href="http://www.ietf.org/rfc/rfc2440.html">RFC 2440</a>.
+ */
+public class OpenPGPCFBBlockCipher
+ implements BlockCipher
+{
+ private byte[] IV;
+ private byte[] FR;
+ private byte[] FRE;
+
+ private BlockCipher cipher;
+
+ private int count;
+ private int blockSize;
+ private boolean forEncryption;
+
+ /**
+ * Basic constructor.
+ *
+ * @param cipher the block cipher to be used as the basis of the
+ * feedback mode.
+ */
+ public OpenPGPCFBBlockCipher(
+ BlockCipher cipher)
+ {
+ this.cipher = cipher;
+
+ this.blockSize = cipher.getBlockSize();
+ this.IV = new byte[blockSize];
+ this.FR = new byte[blockSize];
+ this.FRE = new byte[blockSize];
+ }
+
+ /**
+ * return the underlying block cipher that we are wrapping.
+ *
+ * @return the underlying block cipher that we are wrapping.
+ */
+ public BlockCipher getUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+ /**
+ * return the algorithm name and mode.
+ *
+ * @return the name of the underlying algorithm followed by "/PGPCFB"
+ * and the block size in bits.
+ */
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName() + "/OpenPGPCFB";
+ }
+
+ /**
+ * return the block size we are operating at.
+ *
+ * @return the block size we are operating at (in bytes).
+ */
+ public int getBlockSize()
+ {
+ return cipher.getBlockSize();
+ }
+
+ /**
+ * Process one block of input from the array in and write it to
+ * the out array.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ return (forEncryption) ? encryptBlock(in, inOff, out, outOff) : decryptBlock(in, inOff, out, outOff);
+ }
+
+ /**
+ * reset the chaining vector back to the IV and reset the underlying
+ * cipher.
+ */
+ public void reset()
+ {
+ count = 0;
+
+ System.arraycopy(IV, 0, FR, 0, FR.length);
+
+ cipher.reset();
+ }
+
+ /**
+ * Initialise the cipher and, possibly, the initialisation vector (IV).
+ * If an IV isn't passed as part of the parameter, the IV will be all zeros.
+ * An IV which is too short is handled in FIPS compliant fashion.
+ *
+ * @param forEncryption if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param params the key and other data required by the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ throws IllegalArgumentException
+ {
+ this.forEncryption = forEncryption;
+
+ reset();
+
+ cipher.init(true, params);
+ }
+
+ /**
+ * Encrypt one byte of data according to CFB mode.
+ * @param data the byte to encrypt
+ * @param blockOff offset in the current block
+ * @return the encrypted byte
+ */
+ private byte encryptByte(byte data, int blockOff)
+ {
+ return (byte)(FRE[blockOff] ^ data);
+ }
+
+ /**
+ * Do the appropriate processing for CFB IV mode encryption.
+ *
+ * @param in the array containing the data to be encrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ private int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ if (count > blockSize)
+ {
+ FR[blockSize - 2] = out[outOff] = encryptByte(in[inOff], blockSize - 2);
+ FR[blockSize - 1] = out[outOff + 1] = encryptByte(in[inOff + 1], blockSize - 1);
+
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ for (int n = 2; n < blockSize; n++)
+ {
+ FR[n - 2] = out[outOff + n] = encryptByte(in[inOff + n], n - 2);
+ }
+ }
+ else if (count == 0)
+ {
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ for (int n = 0; n < blockSize; n++)
+ {
+ FR[n] = out[outOff + n] = encryptByte(in[inOff + n], n);
+ }
+
+ count += blockSize;
+ }
+ else if (count == blockSize)
+ {
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ out[outOff] = encryptByte(in[inOff], 0);
+ out[outOff + 1] = encryptByte(in[inOff + 1], 1);
+
+ //
+ // do reset
+ //
+ System.arraycopy(FR, 2, FR, 0, blockSize - 2);
+ System.arraycopy(out, outOff, FR, blockSize - 2, 2);
+
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ for (int n = 2; n < blockSize; n++)
+ {
+ FR[n - 2] = out[outOff + n] = encryptByte(in[inOff + n], n - 2);
+ }
+
+ count += blockSize;
+ }
+
+ return blockSize;
+ }
+
+ /**
+ * Do the appropriate processing for CFB IV mode decryption.
+ *
+ * @param in the array containing the data to be decrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ private int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ if (count > blockSize)
+ {
+ byte inVal = in[inOff];
+ FR[blockSize - 2] = inVal;
+ out[outOff] = encryptByte(inVal, blockSize - 2);
+
+ inVal = in[inOff + 1];
+ FR[blockSize - 1] = inVal;
+ out[outOff + 1] = encryptByte(inVal, blockSize - 1);
+
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ for (int n = 2; n < blockSize; n++)
+ {
+ inVal = in[inOff + n];
+ FR[n - 2] = inVal;
+ out[outOff + n] = encryptByte(inVal, n - 2);
+ }
+ }
+ else if (count == 0)
+ {
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ for (int n = 0; n < blockSize; n++)
+ {
+ FR[n] = in[inOff + n];
+ out[n] = encryptByte(in[inOff + n], n);
+ }
+
+ count += blockSize;
+ }
+ else if (count == blockSize)
+ {
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ byte inVal1 = in[inOff];
+ byte inVal2 = in[inOff + 1];
+ out[outOff ] = encryptByte(inVal1, 0);
+ out[outOff + 1] = encryptByte(inVal2, 1);
+
+ System.arraycopy(FR, 2, FR, 0, blockSize - 2);
+
+ FR[blockSize - 2] = inVal1;
+ FR[blockSize - 1] = inVal2;
+
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ for (int n = 2; n < blockSize; n++)
+ {
+ byte inVal = in[inOff + n];
+ FR[n - 2] = inVal;
+ out[outOff + n] = encryptByte(inVal, n - 2);
+ }
+
+ count += blockSize;
+ }
+
+ return blockSize;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/PGPCFBBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/PGPCFBBlockCipher.java
new file mode 100644
index 00000000..18e612b3
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/modes/PGPCFBBlockCipher.java
@@ -0,0 +1,450 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+
+/**
+ * Implements OpenPGP's rather strange version of Cipher-FeedBack (CFB) mode on top of a simple cipher. For further info see <a href="http://www.ietf.org/rfc/rfc2440.html">RFC 2440</a>.
+ */
+public class PGPCFBBlockCipher
+ implements BlockCipher
+{
+ private byte[] IV;
+ private byte[] FR;
+ private byte[] FRE;
+ private byte[] tmp;
+
+ private BlockCipher cipher;
+
+ private int count;
+ private int blockSize;
+ private boolean forEncryption;
+
+ private boolean inlineIv; // if false we don't need to prepend an IV
+
+ /**
+ * Basic constructor.
+ *
+ * @param cipher the block cipher to be used as the basis of the
+ * feedback mode.
+ * @param inlineIv if true this is for PGP CFB with a prepended iv.
+ */
+ public PGPCFBBlockCipher(
+ BlockCipher cipher,
+ boolean inlineIv)
+ {
+ this.cipher = cipher;
+ this.inlineIv = inlineIv;
+
+ this.blockSize = cipher.getBlockSize();
+ this.IV = new byte[blockSize];
+ this.FR = new byte[blockSize];
+ this.FRE = new byte[blockSize];
+ this.tmp = new byte[blockSize];
+ }
+
+ /**
+ * return the underlying block cipher that we are wrapping.
+ *
+ * @return the underlying block cipher that we are wrapping.
+ */
+ public BlockCipher getUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+ /**
+ * return the algorithm name and mode.
+ *
+ * @return the name of the underlying algorithm followed by "/PGPCFB"
+ * and the block size in bits.
+ */
+ public String getAlgorithmName()
+ {
+ if (inlineIv)
+ {
+ return cipher.getAlgorithmName() + "/PGPCFBwithIV";
+ }
+ else
+ {
+ return cipher.getAlgorithmName() + "/PGPCFB";
+ }
+ }
+
+ /**
+ * return the block size we are operating at.
+ *
+ * @return the block size we are operating at (in bytes).
+ */
+ public int getBlockSize()
+ {
+ return cipher.getBlockSize();
+ }
+
+ /**
+ * Process one block of input from the array in and write it to
+ * the out array.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if (inlineIv)
+ {
+ return (forEncryption) ? encryptBlockWithIV(in, inOff, out, outOff) : decryptBlockWithIV(in, inOff, out, outOff);
+ }
+ else
+ {
+ return (forEncryption) ? encryptBlock(in, inOff, out, outOff) : decryptBlock(in, inOff, out, outOff);
+ }
+ }
+
+ /**
+ * reset the chaining vector back to the IV and reset the underlying
+ * cipher.
+ */
+ public void reset()
+ {
+ count = 0;
+
+ for (int i = 0; i != FR.length; i++)
+ {
+ if (inlineIv)
+ {
+ FR[i] = 0;
+ }
+ else
+ {
+ FR[i] = IV[i]; // if simple mode, key is IV (even if this is zero)
+ }
+ }
+
+ cipher.reset();
+ }
+
+ /**
+ * Initialise the cipher and, possibly, the initialisation vector (IV).
+ * If an IV isn't passed as part of the parameter, the IV will be all zeros.
+ * An IV which is too short is handled in FIPS compliant fashion.
+ *
+ * @param forEncryption if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param params the key and other data required by the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ throws IllegalArgumentException
+ {
+ this.forEncryption = forEncryption;
+
+ if (params instanceof ParametersWithIV)
+ {
+ ParametersWithIV ivParam = (ParametersWithIV)params;
+ byte[] iv = ivParam.getIV();
+
+ if (iv.length < IV.length)
+ {
+ // prepend the supplied IV with zeros (per FIPS PUB 81)
+ System.arraycopy(iv, 0, IV, IV.length - iv.length, iv.length);
+ for (int i = 0; i < IV.length - iv.length; i++)
+ {
+ IV[i] = 0;
+ }
+ }
+ else
+ {
+ System.arraycopy(iv, 0, IV, 0, IV.length);
+ }
+
+ reset();
+
+ cipher.init(true, ivParam.getParameters());
+ }
+ else
+ {
+ reset();
+
+ cipher.init(true, params);
+ }
+ }
+
+ /**
+ * Encrypt one byte of data according to CFB mode.
+ * @param data the byte to encrypt
+ * @param blockOff where am i in the current block, determines when to resync the block
+ * @returns the encrypted byte
+ */
+ private byte encryptByte(byte data, int blockOff)
+ {
+ return (byte)(FRE[blockOff] ^ data);
+ }
+
+ /**
+ * Do the appropriate processing for CFB IV mode encryption.
+ *
+ * @param in the array containing the data to be encrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ private int encryptBlockWithIV(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ if (count == 0)
+ {
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ for (int n = 0; n < blockSize; n++)
+ {
+ out[outOff + n] = encryptByte(IV[n], n);
+ }
+
+ System.arraycopy(out, outOff, FR, 0, blockSize);
+
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ out[outOff + blockSize] = encryptByte(IV[blockSize - 2], 0);
+ out[outOff + blockSize + 1] = encryptByte(IV[blockSize - 1], 1);
+
+ System.arraycopy(out, outOff + 2, FR, 0, blockSize);
+
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ for (int n = 0; n < blockSize; n++)
+ {
+ out[outOff + blockSize + 2 + n] = encryptByte(in[inOff + n], n);
+ }
+
+ System.arraycopy(out, outOff + blockSize + 2, FR, 0, blockSize);
+
+ count += 2 * blockSize + 2;
+
+ return 2 * blockSize + 2;
+ }
+ else if (count >= blockSize + 2)
+ {
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ for (int n = 0; n < blockSize; n++)
+ {
+ out[outOff + n] = encryptByte(in[inOff + n], n);
+ }
+
+ System.arraycopy(out, outOff, FR, 0, blockSize);
+ }
+
+ return blockSize;
+ }
+
+ /**
+ * Do the appropriate processing for CFB IV mode decryption.
+ *
+ * @param in the array containing the data to be decrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ private int decryptBlockWithIV(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ if (count == 0)
+ {
+ for (int n = 0; n < blockSize; n++)
+ {
+ FR[n] = in[inOff + n];
+ }
+
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ count += blockSize;
+
+ return 0;
+ }
+ else if (count == blockSize)
+ {
+ // copy in buffer so that this mode works if in and out are the same
+ System.arraycopy(in, inOff, tmp, 0, blockSize);
+
+ System.arraycopy(FR, 2, FR, 0, blockSize - 2);
+
+ FR[blockSize - 2] = tmp[0];
+ FR[blockSize - 1] = tmp[1];
+
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ for (int n = 0; n < blockSize - 2; n++)
+ {
+ out[outOff + n] = encryptByte(tmp[n + 2], n);
+ }
+
+ System.arraycopy(tmp, 2, FR, 0, blockSize - 2);
+
+ count += 2;
+
+ return blockSize - 2;
+ }
+ else if (count >= blockSize + 2)
+ {
+ // copy in buffer so that this mode works if in and out are the same
+ System.arraycopy(in, inOff, tmp, 0, blockSize);
+
+ out[outOff + 0] = encryptByte(tmp[0], blockSize - 2);
+ out[outOff + 1] = encryptByte(tmp[1], blockSize - 1);
+
+ System.arraycopy(tmp, 0, FR, blockSize - 2, 2);
+
+ cipher.processBlock(FR, 0, FRE, 0);
+
+ for (int n = 0; n < blockSize - 2; n++)
+ {
+ out[outOff + n + 2] = encryptByte(tmp[n + 2], n);
+ }
+
+ System.arraycopy(tmp, 2, FR, 0, blockSize - 2);
+
+ }
+
+ return blockSize;
+ }
+
+ /**
+ * Do the appropriate processing for CFB mode encryption.
+ *
+ * @param in the array containing the data to be encrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ private int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ cipher.processBlock(FR, 0, FRE, 0);
+ for (int n = 0; n < blockSize; n++)
+ {
+ out[outOff + n] = encryptByte(in[inOff + n], n);
+ }
+
+ for (int n = 0; n < blockSize; n++)
+ {
+ FR[n] = out[outOff + n];
+ }
+
+ return blockSize;
+
+ }
+
+ /**
+ * Do the appropriate processing for CFB mode decryption.
+ *
+ * @param in the array containing the data to be decrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ private int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ cipher.processBlock(FR, 0, FRE, 0);
+ for (int n = 0; n < blockSize; n++)
+ {
+ out[outOff + n] = encryptByte(in[inOff + n], n);
+ }
+
+ for (int n = 0; n < blockSize; n++)
+ {
+ FR[n] = in[inOff + n];
+ }
+
+ return blockSize;
+
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/PaddedBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/PaddedBlockCipher.java
new file mode 100644
index 00000000..f15ed673
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/modes/PaddedBlockCipher.java
@@ -0,0 +1,253 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+
+/**
+ * A wrapper class that allows block ciphers to be used to process data in
+ * a piecemeal fashion with PKCS5/PKCS7 padding. The PaddedBlockCipher
+ * outputs a block only when the buffer is full and more data is being added,
+ * or on a doFinal (unless the current block in the buffer is a pad block).
+ * The padding mechanism used is the one outlined in PKCS5/PKCS7.
+ *
+ * @deprecated use org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher instead.
+ */
+public class PaddedBlockCipher
+ extends BufferedBlockCipher
+{
+ /**
+ * Create a buffered block cipher with, or without, padding.
+ *
+ * @param cipher the underlying block cipher this buffering object wraps.
+ */
+ public PaddedBlockCipher(
+ BlockCipher cipher)
+ {
+ this.cipher = cipher;
+
+ buf = new byte[cipher.getBlockSize()];
+ bufOff = 0;
+ }
+
+ /**
+ * return the size of the output buffer required for an update plus a
+ * doFinal with an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to update and doFinal
+ * with len bytes of input.
+ */
+ public int getOutputSize(
+ int len)
+ {
+ int total = len + bufOff;
+ int leftOver = total % buf.length;
+
+ if (leftOver == 0)
+ {
+ if (forEncryption)
+ {
+ return total + buf.length;
+ }
+
+ return total;
+ }
+
+ return total - leftOver + buf.length;
+ }
+
+ /**
+ * return the size of the output buffer required for an update
+ * an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to update
+ * with len bytes of input.
+ */
+ public int getUpdateOutputSize(
+ int len)
+ {
+ int total = len + bufOff;
+ int leftOver = total % buf.length;
+
+ if (leftOver == 0)
+ {
+ return total - buf.length;
+ }
+
+ return total - leftOver;
+ }
+
+ /**
+ * process a single byte, producing an output block if neccessary.
+ *
+ * @param in the input byte.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ */
+ public int processByte(
+ byte in,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ int resultLen = 0;
+
+ if (bufOff == buf.length)
+ {
+ resultLen = cipher.processBlock(buf, 0, out, outOff);
+ bufOff = 0;
+ }
+
+ buf[bufOff++] = in;
+
+ return resultLen;
+ }
+
+ /**
+ * process an array of bytes, producing output if necessary.
+ *
+ * @param in the input byte array.
+ * @param inOff the offset at which the input data starts.
+ * @param len the number of bytes to be copied out of the input array.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ */
+ public int processBytes(
+ byte[] in,
+ int inOff,
+ int len,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if (len < 0)
+ {
+ throw new IllegalArgumentException("Can't have a negative input length!");
+ }
+
+ int blockSize = getBlockSize();
+ int length = getUpdateOutputSize(len);
+
+ if (length > 0)
+ {
+ if ((outOff + length) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+ }
+
+ int resultLen = 0;
+ int gapLen = buf.length - bufOff;
+
+ if (len > gapLen)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+ resultLen += cipher.processBlock(buf, 0, out, outOff);
+
+ bufOff = 0;
+ len -= gapLen;
+ inOff += gapLen;
+
+ while (len > buf.length)
+ {
+ resultLen += cipher.processBlock(in, inOff, out, outOff + resultLen);
+
+ len -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ System.arraycopy(in, inOff, buf, bufOff, len);
+
+ bufOff += len;
+
+ return resultLen;
+ }
+
+ /**
+ * Process the last block in the buffer. If the buffer is currently
+ * full and padding needs to be added a call to doFinal will produce
+ * 2 * getBlockSize() bytes.
+ *
+ * @param out the array the block currently being held is copied into.
+ * @param outOff the offset at which the copying starts.
+ * @exception DataLengthException if there is insufficient space in out for
+ * the output or we are decrypting and the input is not block size aligned.
+ * @exception IllegalStateException if the underlying cipher is not
+ * initialised.
+ * @exception InvalidCipherTextException if padding is expected and not found.
+ */
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException, InvalidCipherTextException
+ {
+ int blockSize = cipher.getBlockSize();
+ int resultLen = 0;
+
+ if (forEncryption)
+ {
+ if (bufOff == blockSize)
+ {
+ if ((outOff + 2 * blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ resultLen = cipher.processBlock(buf, 0, out, outOff);
+ bufOff = 0;
+ }
+
+ //
+ // add PKCS7 padding
+ //
+ byte code = (byte)(blockSize - bufOff);
+
+ while (bufOff < blockSize)
+ {
+ buf[bufOff] = code;
+ bufOff++;
+ }
+
+ resultLen += cipher.processBlock(buf, 0, out, outOff + resultLen);
+ }
+ else
+ {
+ if (bufOff == blockSize)
+ {
+ resultLen = cipher.processBlock(buf, 0, buf, 0);
+ bufOff = 0;
+ }
+ else
+ {
+ throw new DataLengthException("last block incomplete in decryption");
+ }
+
+ //
+ // remove PKCS7 padding
+ //
+ int count = buf[blockSize - 1] & 0xff;
+
+ if ((count < 0) || (count > blockSize))
+ {
+ throw new InvalidCipherTextException("pad block corrupted");
+ }
+
+ resultLen -= count;
+
+ System.arraycopy(buf, 0, out, outOff, resultLen);
+ }
+
+ reset();
+
+ return resultLen;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java
new file mode 100644
index 00000000..da8c4ae1
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java
@@ -0,0 +1,113 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+
+/**
+ * Implements the Segmented Integer Counter (SIC) mode on top of a simple
+ * block cipher. This mode is also known as CTR mode.
+ */
+public class SICBlockCipher
+ implements BlockCipher
+{
+ private final BlockCipher cipher;
+ private final int blockSize;
+
+ private byte[] IV;
+ private byte[] counter;
+ private byte[] counterOut;
+
+
+ /**
+ * Basic constructor.
+ *
+ * @param c the block cipher to be used.
+ */
+ public SICBlockCipher(BlockCipher c)
+ {
+ this.cipher = c;
+ this.blockSize = cipher.getBlockSize();
+ this.IV = new byte[blockSize];
+ this.counter = new byte[blockSize];
+ this.counterOut = new byte[blockSize];
+ }
+
+
+ /**
+ * 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, //ignored by this CTR mode
+ CipherParameters params)
+ throws IllegalArgumentException
+ {
+ if (params instanceof ParametersWithIV)
+ {
+ ParametersWithIV ivParam = (ParametersWithIV)params;
+ byte[] iv = ivParam.getIV();
+ System.arraycopy(iv, 0, IV, 0, IV.length);
+
+ reset();
+
+ // if null it's an IV changed only.
+ if (ivParam.getParameters() != null)
+ {
+ cipher.init(true, ivParam.getParameters());
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("SIC mode requires ParametersWithIV");
+ }
+ }
+
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName() + "/SIC";
+ }
+
+ public int getBlockSize()
+ {
+ return cipher.getBlockSize();
+ }
+
+
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ cipher.processBlock(counter, 0, counterOut, 0);
+
+ //
+ // XOR the counterOut with the plaintext producing the cipher text
+ //
+ for (int i = 0; i < counterOut.length; i++)
+ {
+ out[outOff + i] = (byte)(counterOut[i] ^ in[inOff + i]);
+ }
+
+ // increment counter by 1.
+ for (int i = counter.length - 1; i >= 0 && ++counter[i] == 0; i--)
+ {
+ ; // do nothing - pre-increment and test for 0 in counter does the job.
+ }
+
+ return counter.length;
+ }
+
+
+ public void reset()
+ {
+ System.arraycopy(IV, 0, counter, 0, counter.length);
+ cipher.reset();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java b/core/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java
new file mode 100644
index 00000000..f2be2fc4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java
@@ -0,0 +1,36 @@
+package org.bouncycastle.crypto.modes.gcm;
+
+import org.bouncycastle.util.Arrays;
+
+public class BasicGCMExponentiator implements GCMExponentiator
+{
+ private byte[] x;
+
+ public void init(byte[] x)
+ {
+ this.x = Arrays.clone(x);
+ }
+
+ public void exponentiateX(long pow, byte[] output)
+ {
+ // Initial value is little-endian 1
+ byte[] y = GCMUtil.oneAsBytes();
+
+ if (pow > 0)
+ {
+ byte[] powX = Arrays.clone(x);
+ do
+ {
+ if ((pow & 1L) != 0)
+ {
+ GCMUtil.multiply(y, powX);
+ }
+ GCMUtil.multiply(powX, powX);
+ pow >>>= 1;
+ }
+ while (pow > 0);
+ }
+
+ System.arraycopy(y, 0, output, 0, 16);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMMultiplier.java b/core/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMMultiplier.java
new file mode 100644
index 00000000..a98d5b2a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMMultiplier.java
@@ -0,0 +1,18 @@
+package org.bouncycastle.crypto.modes.gcm;
+
+import org.bouncycastle.util.Arrays;
+
+public class BasicGCMMultiplier implements GCMMultiplier
+{
+ private byte[] H;
+
+ public void init(byte[] H)
+ {
+ this.H = Arrays.clone(H);
+ }
+
+ public void multiplyH(byte[] x)
+ {
+ GCMUtil.multiply(x, H);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMExponentiator.java b/core/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMExponentiator.java
new file mode 100644
index 00000000..e1cc5c76
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMExponentiator.java
@@ -0,0 +1,7 @@
+package org.bouncycastle.crypto.modes.gcm;
+
+public interface GCMExponentiator
+{
+ void init(byte[] x);
+ void exponentiateX(long pow, byte[] output);
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMMultiplier.java b/core/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMMultiplier.java
new file mode 100644
index 00000000..f52f6105
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMMultiplier.java
@@ -0,0 +1,7 @@
+package org.bouncycastle.crypto.modes.gcm;
+
+public interface GCMMultiplier
+{
+ void init(byte[] H);
+ void multiplyH(byte[] x);
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java b/core/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
new file mode 100644
index 00000000..48753011
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
@@ -0,0 +1,260 @@
+package org.bouncycastle.crypto.modes.gcm;
+
+import org.bouncycastle.crypto.util.Pack;
+import org.bouncycastle.util.Arrays;
+
+abstract class GCMUtil
+{
+ static byte[] oneAsBytes()
+ {
+ byte[] tmp = new byte[16];
+ tmp[0] = (byte)0x80;
+ return tmp;
+ }
+
+ static int[] oneAsInts()
+ {
+ int[] tmp = new int[4];
+ tmp[0] = 0x80000000;
+ return tmp;
+ }
+
+ static byte[] asBytes(int[] ns)
+ {
+ byte[] output = new byte[16];
+ Pack.intToBigEndian(ns, output, 0);
+ return output;
+ }
+
+ static int[] asInts(byte[] bs)
+ {
+ int[] output = new int[4];
+ Pack.bigEndianToInt(bs, 0, output);
+ return output;
+ }
+
+ static void asInts(byte[] bs, int[] output)
+ {
+ Pack.bigEndianToInt(bs, 0, output);
+ }
+
+ static void multiply(byte[] block, byte[] val)
+ {
+ byte[] tmp = Arrays.clone(block);
+ byte[] c = new byte[16];
+
+ for (int i = 0; i < 16; ++i)
+ {
+ byte bits = val[i];
+ for (int j = 7; j >= 0; --j)
+ {
+ if ((bits & (1 << j)) != 0)
+ {
+ xor(c, tmp);
+ }
+
+ boolean lsb = (tmp[15] & 1) != 0;
+ shiftRight(tmp);
+ if (lsb)
+ {
+ // R = new byte[]{ 0xe1, ... };
+// GCMUtil.xor(v, R);
+ tmp[0] ^= (byte)0xe1;
+ }
+ }
+ }
+
+ System.arraycopy(c, 0, block, 0, 16);
+ }
+
+ // P is the value with only bit i=1 set
+ static void multiplyP(int[] x)
+ {
+ boolean lsb = (x[3] & 1) != 0;
+ shiftRight(x);
+ if (lsb)
+ {
+ // R = new int[]{ 0xe1000000, 0, 0, 0 };
+// xor(v, R);
+ x[0] ^= 0xe1000000;
+ }
+ }
+
+ static void multiplyP(int[] x, int[] output)
+ {
+ boolean lsb = (x[3] & 1) != 0;
+ shiftRight(x, output);
+ if (lsb)
+ {
+ output[0] ^= 0xe1000000;
+ }
+ }
+
+ // P is the value with only bit i=1 set
+ static void multiplyP8(int[] x)
+ {
+// for (int i = 8; i != 0; --i)
+// {
+// multiplyP(x);
+// }
+
+ int lsw = x[3];
+ shiftRightN(x, 8);
+ for (int i = 7; i >= 0; --i)
+ {
+ if ((lsw & (1 << i)) != 0)
+ {
+ x[0] ^= (0xe1000000 >>> (7 - i));
+ }
+ }
+ }
+
+ static void multiplyP8(int[] x, int[] output)
+ {
+ int lsw = x[3];
+ shiftRightN(x, 8, output);
+ for (int i = 7; i >= 0; --i)
+ {
+ if ((lsw & (1 << i)) != 0)
+ {
+ output[0] ^= (0xe1000000 >>> (7 - i));
+ }
+ }
+ }
+
+ static void shiftRight(byte[] block)
+ {
+ int i = 0;
+ int bit = 0;
+ for (;;)
+ {
+ int b = block[i] & 0xff;
+ block[i] = (byte) ((b >>> 1) | bit);
+ if (++i == 16)
+ {
+ break;
+ }
+ bit = (b & 1) << 7;
+ }
+ }
+
+ static void shiftRight(byte[] block, byte[] output)
+ {
+ int i = 0;
+ int bit = 0;
+ for (;;)
+ {
+ int b = block[i] & 0xff;
+ output[i] = (byte) ((b >>> 1) | bit);
+ if (++i == 16)
+ {
+ break;
+ }
+ bit = (b & 1) << 7;
+ }
+ }
+
+ static void shiftRight(int[] block)
+ {
+ int i = 0;
+ int bit = 0;
+ for (;;)
+ {
+ int b = block[i];
+ block[i] = (b >>> 1) | bit;
+ if (++i == 4)
+ {
+ break;
+ }
+ bit = b << 31;
+ }
+ }
+
+ static void shiftRight(int[] block, int[] output)
+ {
+ int i = 0;
+ int bit = 0;
+ for (;;)
+ {
+ int b = block[i];
+ output[i] = (b >>> 1) | bit;
+ if (++i == 4)
+ {
+ break;
+ }
+ bit = b << 31;
+ }
+ }
+
+ static void shiftRightN(int[] block, int n)
+ {
+ int i = 0;
+ int bits = 0;
+ for (;;)
+ {
+ int b = block[i];
+ block[i] = (b >>> n) | bits;
+ if (++i == 4)
+ {
+ break;
+ }
+ bits = b << (32 - n);
+ }
+ }
+
+ static void shiftRightN(int[] block, int n, int[] output)
+ {
+ int i = 0;
+ int bits = 0;
+ for (;;)
+ {
+ int b = block[i];
+ output[i] = (b >>> n) | bits;
+ if (++i == 4)
+ {
+ break;
+ }
+ bits = b << (32 - n);
+ }
+ }
+
+ static void xor(byte[] block, byte[] val)
+ {
+ for (int i = 15; i >= 0; --i)
+ {
+ block[i] ^= val[i];
+ }
+ }
+
+ static void xor(byte[] block, byte[] val, int off, int len)
+ {
+ while (len-- > 0)
+ {
+ block[len] ^= val[off + len];
+ }
+ }
+
+ static void xor(byte[] block, byte[] val, byte[] output)
+ {
+ for (int i = 15; i >= 0; --i)
+ {
+ output[i] = (byte)(block[i] ^ val[i]);
+ }
+ }
+
+ static void xor(int[] block, int[] val)
+ {
+ for (int i = 3; i >= 0; --i)
+ {
+ block[i] ^= val[i];
+ }
+ }
+
+ static void xor(int[] block, int[] val, int[] output)
+ {
+ for (int i = 3; i >= 0; --i)
+ {
+ output[i] = block[i] ^ val[i];
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java b/core/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
new file mode 100644
index 00000000..a0512086
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
@@ -0,0 +1,57 @@
+package org.bouncycastle.crypto.modes.gcm;
+
+import java.util.Vector;
+
+import org.bouncycastle.util.Arrays;
+
+public class Tables1kGCMExponentiator implements GCMExponentiator
+{
+ // A lookup table of the power-of-two powers of 'x'
+ // - lookupPowX2[i] = x^(2^i)
+ private Vector lookupPowX2;
+
+ public void init(byte[] x)
+ {
+ if (lookupPowX2 != null && Arrays.areEqual(x, (byte[])lookupPowX2.elementAt(0)))
+ {
+ return;
+ }
+
+ lookupPowX2 = new Vector(8);
+ lookupPowX2.addElement(Arrays.clone(x));
+ }
+
+ public void exponentiateX(long pow, byte[] output)
+ {
+ byte[] y = GCMUtil.oneAsBytes();
+ int bit = 0;
+ while (pow > 0)
+ {
+ if ((pow & 1L) != 0)
+ {
+ ensureAvailable(bit);
+ GCMUtil.multiply(y, (byte[])lookupPowX2.elementAt(bit));
+ }
+ ++bit;
+ pow >>>= 1;
+ }
+
+ System.arraycopy(y, 0, output, 0, 16);
+ }
+
+ private void ensureAvailable(int bit)
+ {
+ int count = lookupPowX2.size();
+ if (count <= bit)
+ {
+ byte[] tmp = (byte[])lookupPowX2.elementAt(count - 1);
+ do
+ {
+ tmp = Arrays.clone(tmp);
+ GCMUtil.multiply(tmp, tmp);
+ lookupPowX2.addElement(tmp);
+ }
+ while (++count <= bit);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables64kGCMMultiplier.java b/core/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables64kGCMMultiplier.java
new file mode 100644
index 00000000..a34a6ea4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables64kGCMMultiplier.java
@@ -0,0 +1,73 @@
+package org.bouncycastle.crypto.modes.gcm;
+
+import org.bouncycastle.crypto.util.Pack;
+import org.bouncycastle.util.Arrays;
+
+public class Tables64kGCMMultiplier implements GCMMultiplier
+{
+ private byte[] H;
+ private int[][][] M;
+
+ public void init(byte[] H)
+ {
+ if (M == null)
+ {
+ M = new int[16][256][4];
+ }
+ else if (Arrays.areEqual(this.H, H))
+ {
+ return;
+ }
+
+ this.H = Arrays.clone(H);
+
+ // M[0][0] is ZEROES;
+ GCMUtil.asInts(H, M[0][128]);
+
+ for (int j = 64; j >= 1; j >>= 1)
+ {
+ GCMUtil.multiplyP(M[0][j + j], M[0][j]);
+ }
+
+ int i = 0;
+ for (;;)
+ {
+ for (int j = 2; j < 256; j += j)
+ {
+ for (int k = 1; k < j; ++k)
+ {
+ GCMUtil.xor(M[i][j], M[i][k], M[i][j + k]);
+ }
+ }
+
+ if (++i == 16)
+ {
+ return;
+ }
+
+ // M[i][0] is ZEROES;
+ for (int j = 128; j > 0; j >>= 1)
+ {
+ GCMUtil.multiplyP8(M[i - 1][j], M[i][j]);
+ }
+ }
+ }
+
+ public void multiplyH(byte[] x)
+ {
+// assert x.Length == 16;
+
+ int[] z = new int[4];
+ for (int i = 15; i >= 0; --i)
+ {
+// GCMUtil.xor(z, M[i][x[i] & 0xff]);
+ int[] m = M[i][x[i] & 0xff];
+ z[0] ^= m[0];
+ z[1] ^= m[1];
+ z[2] ^= m[2];
+ z[3] ^= m[3];
+ }
+
+ Pack.intToBigEndian(z, x, 0);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java b/core/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
new file mode 100644
index 00000000..8535db5a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
@@ -0,0 +1,90 @@
+package org.bouncycastle.crypto.modes.gcm;
+
+import org.bouncycastle.crypto.util.Pack;
+import org.bouncycastle.util.Arrays;
+
+public class Tables8kGCMMultiplier implements GCMMultiplier
+{
+ private byte[] H;
+ private int[][][] M;
+
+ public void init(byte[] H)
+ {
+ if (M == null)
+ {
+ M = new int[32][16][4];
+ }
+ else if (Arrays.areEqual(this.H, H))
+ {
+ return;
+ }
+
+ this.H = Arrays.clone(H);
+
+ // M[0][0] is ZEROES;
+ // M[1][0] is ZEROES;
+ GCMUtil.asInts(H, M[1][8]);
+
+ for (int j = 4; j >= 1; j >>= 1)
+ {
+ GCMUtil.multiplyP(M[1][j + j], M[1][j]);
+ }
+
+ GCMUtil.multiplyP(M[1][1], M[0][8]);
+
+ for (int j = 4; j >= 1; j >>= 1)
+ {
+ GCMUtil.multiplyP(M[0][j + j], M[0][j]);
+ }
+
+ int i = 0;
+ for (;;)
+ {
+ for (int j = 2; j < 16; j += j)
+ {
+ for (int k = 1; k < j; ++k)
+ {
+ GCMUtil.xor(M[i][j], M[i][k], M[i][j + k]);
+ }
+ }
+
+ if (++i == 32)
+ {
+ return;
+ }
+
+ if (i > 1)
+ {
+ // M[i][0] is ZEROES;
+ for(int j = 8; j > 0; j >>= 1)
+ {
+ GCMUtil.multiplyP8(M[i - 2][j], M[i][j]);
+ }
+ }
+ }
+ }
+
+ public void multiplyH(byte[] x)
+ {
+// assert x.Length == 16;
+
+ int[] z = new int[4];
+ for (int i = 15; i >= 0; --i)
+ {
+// GCMUtil.xor(z, M[i + i][x[i] & 0x0f]);
+ int[] m = M[i + i][x[i] & 0x0f];
+ z[0] ^= m[0];
+ z[1] ^= m[1];
+ z[2] ^= m[2];
+ z[3] ^= m[3];
+// GCMUtil.xor(z, M[i + i + 1][(x[i] & 0xf0) >>> 4]);
+ m = M[i + i + 1][(x[i] & 0xf0) >>> 4];
+ z[0] ^= m[0];
+ z[1] ^= m[1];
+ z[2] ^= m[2];
+ z[3] ^= m[3];
+ }
+
+ Pack.intToBigEndian(z, x, 0);
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/bouncycastle/crypto/paddings/BlockCipherPadding.java b/core/src/main/java/org/bouncycastle/crypto/paddings/BlockCipherPadding.java
new file mode 100644
index 00000000..7c4f0aee
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/paddings/BlockCipherPadding.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.crypto.paddings;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.InvalidCipherTextException;
+
+/**
+ * Block cipher padders are expected to conform to this interface
+ */
+public interface BlockCipherPadding
+{
+ /**
+ * Initialise the padder.
+ *
+ * @param random the source of randomness for the padding, if required.
+ */
+ public void init(SecureRandom random)
+ throws IllegalArgumentException;
+
+ /**
+ * Return the name of the algorithm the cipher implements.
+ *
+ * @return the name of the algorithm the cipher implements.
+ */
+ public String getPaddingName();
+
+ /**
+ * add the pad bytes to the passed in block, returning the
+ * number of bytes added.
+ * <p>
+ * Note: this assumes that the last block of plain text is always
+ * passed to it inside in. i.e. if inOff is zero, indicating the
+ * entire block is to be overwritten with padding the value of in
+ * should be the same as the last block of plain text. The reason
+ * for this is that some modes such as "trailing bit compliment"
+ * base the padding on the last byte of plain text.
+ * </p>
+ */
+ public int addPadding(byte[] in, int inOff);
+
+ /**
+ * return the number of pad bytes present in the block.
+ * @exception InvalidCipherTextException if the padding is badly formed
+ * or invalid.
+ */
+ public int padCount(byte[] in)
+ throws InvalidCipherTextException;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java b/core/src/main/java/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java
new file mode 100644
index 00000000..63e29d84
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java
@@ -0,0 +1,79 @@
+package org.bouncycastle.crypto.paddings;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.InvalidCipherTextException;
+
+/**
+ * A padder that adds ISO10126-2 padding to a block.
+ */
+public class ISO10126d2Padding
+ implements BlockCipherPadding
+{
+ SecureRandom random;
+
+ /**
+ * Initialise the padder.
+ *
+ * @param random a SecureRandom if available.
+ */
+ public void init(SecureRandom random)
+ throws IllegalArgumentException
+ {
+ if (random != null)
+ {
+ this.random = random;
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ }
+ }
+
+ /**
+ * Return the name of the algorithm the padder implements.
+ *
+ * @return the name of the algorithm the padder implements.
+ */
+ public String getPaddingName()
+ {
+ return "ISO10126-2";
+ }
+
+ /**
+ * add the pad bytes to the passed in block, returning the
+ * number of bytes added.
+ */
+ public int addPadding(
+ byte[] in,
+ int inOff)
+ {
+ byte code = (byte)(in.length - inOff);
+
+ while (inOff < (in.length - 1))
+ {
+ in[inOff] = (byte)random.nextInt();
+ inOff++;
+ }
+
+ in[inOff] = code;
+
+ return code;
+ }
+
+ /**
+ * return the number of pad bytes present in the block.
+ */
+ public int padCount(byte[] in)
+ throws InvalidCipherTextException
+ {
+ int count = in[in.length - 1] & 0xff;
+
+ if (count > in.length)
+ {
+ throw new InvalidCipherTextException("pad block corrupted");
+ }
+
+ return count;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/paddings/ISO7816d4Padding.java b/core/src/main/java/org/bouncycastle/crypto/paddings/ISO7816d4Padding.java
new file mode 100644
index 00000000..54c31a9b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/paddings/ISO7816d4Padding.java
@@ -0,0 +1,77 @@
+package org.bouncycastle.crypto.paddings;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.InvalidCipherTextException;
+
+/**
+ * A padder that adds the padding according to the scheme referenced in
+ * ISO 7814-4 - scheme 2 from ISO 9797-1. The first byte is 0x80, rest is 0x00
+ */
+public class ISO7816d4Padding
+ implements BlockCipherPadding
+{
+ /**
+ * Initialise the padder.
+ *
+ * @param random - a SecureRandom if available.
+ */
+ public void init(SecureRandom random)
+ throws IllegalArgumentException
+ {
+ // nothing to do.
+ }
+
+ /**
+ * Return the name of the algorithm the padder implements.
+ *
+ * @return the name of the algorithm the padder implements.
+ */
+ public String getPaddingName()
+ {
+ return "ISO7816-4";
+ }
+
+ /**
+ * add the pad bytes to the passed in block, returning the
+ * number of bytes added.
+ */
+ public int addPadding(
+ byte[] in,
+ int inOff)
+ {
+ int added = (in.length - inOff);
+
+ in [inOff]= (byte) 0x80;
+ inOff ++;
+
+ while (inOff < in.length)
+ {
+ in[inOff] = (byte) 0;
+ inOff++;
+ }
+
+ return added;
+ }
+
+ /**
+ * return the number of pad bytes present in the block.
+ */
+ public int padCount(byte[] in)
+ throws InvalidCipherTextException
+ {
+ int count = in.length - 1;
+
+ while (count > 0 && in[count] == 0)
+ {
+ count--;
+ }
+
+ if (in[count] != (byte)0x80)
+ {
+ throw new InvalidCipherTextException("pad block corrupted");
+ }
+
+ return in.length - count;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/paddings/PKCS7Padding.java b/core/src/main/java/org/bouncycastle/crypto/paddings/PKCS7Padding.java
new file mode 100644
index 00000000..93b149fa
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/paddings/PKCS7Padding.java
@@ -0,0 +1,76 @@
+package org.bouncycastle.crypto.paddings;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.InvalidCipherTextException;
+
+/**
+ * A padder that adds PKCS7/PKCS5 padding to a block.
+ */
+public class PKCS7Padding
+ implements BlockCipherPadding
+{
+ /**
+ * Initialise the padder.
+ *
+ * @param random - a SecureRandom if available.
+ */
+ public void init(SecureRandom random)
+ throws IllegalArgumentException
+ {
+ // nothing to do.
+ }
+
+ /**
+ * Return the name of the algorithm the padder implements.
+ *
+ * @return the name of the algorithm the padder implements.
+ */
+ public String getPaddingName()
+ {
+ return "PKCS7";
+ }
+
+ /**
+ * add the pad bytes to the passed in block, returning the
+ * number of bytes added.
+ */
+ public int addPadding(
+ byte[] in,
+ int inOff)
+ {
+ byte code = (byte)(in.length - inOff);
+
+ while (inOff < in.length)
+ {
+ in[inOff] = code;
+ inOff++;
+ }
+
+ return code;
+ }
+
+ /**
+ * return the number of pad bytes present in the block.
+ */
+ public int padCount(byte[] in)
+ throws InvalidCipherTextException
+ {
+ int count = in[in.length - 1] & 0xff;
+
+ if (count > in.length || count == 0)
+ {
+ throw new InvalidCipherTextException("pad block corrupted");
+ }
+
+ for (int i = 1; i <= count; i++)
+ {
+ if (in[in.length - i] != count)
+ {
+ throw new InvalidCipherTextException("pad block corrupted");
+ }
+ }
+
+ return count;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java
new file mode 100644
index 00000000..ee3fd60e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java
@@ -0,0 +1,299 @@
+package org.bouncycastle.crypto.paddings;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+
+/**
+ * A wrapper class that allows block ciphers to be used to process data in
+ * a piecemeal fashion with padding. The PaddedBufferedBlockCipher
+ * outputs a block only when the buffer is full and more data is being added,
+ * or on a doFinal (unless the current block in the buffer is a pad block).
+ * The default padding mechanism used is the one outlined in PKCS5/PKCS7.
+ */
+public class PaddedBufferedBlockCipher
+ extends BufferedBlockCipher
+{
+ BlockCipherPadding padding;
+
+ /**
+ * Create a buffered block cipher with the desired padding.
+ *
+ * @param cipher the underlying block cipher this buffering object wraps.
+ * @param padding the padding type.
+ */
+ public PaddedBufferedBlockCipher(
+ BlockCipher cipher,
+ BlockCipherPadding padding)
+ {
+ this.cipher = cipher;
+ this.padding = padding;
+
+ buf = new byte[cipher.getBlockSize()];
+ bufOff = 0;
+ }
+
+ /**
+ * Create a buffered block cipher PKCS7 padding
+ *
+ * @param cipher the underlying block cipher this buffering object wraps.
+ */
+ public PaddedBufferedBlockCipher(
+ BlockCipher cipher)
+ {
+ this(cipher, new PKCS7Padding());
+ }
+
+ /**
+ * initialise the cipher.
+ *
+ * @param forEncryption if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param params the key and other data required by the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ throws IllegalArgumentException
+ {
+ this.forEncryption = forEncryption;
+
+ reset();
+
+ if (params instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom p = (ParametersWithRandom)params;
+
+ padding.init(p.getRandom());
+
+ cipher.init(forEncryption, p.getParameters());
+ }
+ else
+ {
+ padding.init(null);
+
+ cipher.init(forEncryption, params);
+ }
+ }
+
+ /**
+ * return the minimum size of the output buffer required for an update
+ * plus a doFinal with an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to update and doFinal
+ * with len bytes of input.
+ */
+ public int getOutputSize(
+ int len)
+ {
+ int total = len + bufOff;
+ int leftOver = total % buf.length;
+
+ if (leftOver == 0)
+ {
+ if (forEncryption)
+ {
+ return total + buf.length;
+ }
+
+ return total;
+ }
+
+ return total - leftOver + buf.length;
+ }
+
+ /**
+ * return the size of the output buffer required for an update
+ * an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to update
+ * with len bytes of input.
+ */
+ public int getUpdateOutputSize(
+ int len)
+ {
+ int total = len + bufOff;
+ int leftOver = total % buf.length;
+
+ if (leftOver == 0)
+ {
+ return total - buf.length;
+ }
+
+ return total - leftOver;
+ }
+
+ /**
+ * process a single byte, producing an output block if neccessary.
+ *
+ * @param in the input byte.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ */
+ public int processByte(
+ byte in,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ int resultLen = 0;
+
+ if (bufOff == buf.length)
+ {
+ resultLen = cipher.processBlock(buf, 0, out, outOff);
+ bufOff = 0;
+ }
+
+ buf[bufOff++] = in;
+
+ return resultLen;
+ }
+
+ /**
+ * process an array of bytes, producing output if necessary.
+ *
+ * @param in the input byte array.
+ * @param inOff the offset at which the input data starts.
+ * @param len the number of bytes to be copied out of the input array.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ */
+ public int processBytes(
+ byte[] in,
+ int inOff,
+ int len,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if (len < 0)
+ {
+ throw new IllegalArgumentException("Can't have a negative input length!");
+ }
+
+ int blockSize = getBlockSize();
+ int length = getUpdateOutputSize(len);
+
+ if (length > 0)
+ {
+ if ((outOff + length) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+ }
+
+ int resultLen = 0;
+ int gapLen = buf.length - bufOff;
+
+ if (len > gapLen)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+ resultLen += cipher.processBlock(buf, 0, out, outOff);
+
+ bufOff = 0;
+ len -= gapLen;
+ inOff += gapLen;
+
+ while (len > buf.length)
+ {
+ resultLen += cipher.processBlock(in, inOff, out, outOff + resultLen);
+
+ len -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ System.arraycopy(in, inOff, buf, bufOff, len);
+
+ bufOff += len;
+
+ return resultLen;
+ }
+
+ /**
+ * Process the last block in the buffer. If the buffer is currently
+ * full and padding needs to be added a call to doFinal will produce
+ * 2 * getBlockSize() bytes.
+ *
+ * @param out the array the block currently being held is copied into.
+ * @param outOff the offset at which the copying starts.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there is insufficient space in out for
+ * the output or we are decrypting and the input is not block size aligned.
+ * @exception IllegalStateException if the underlying cipher is not
+ * initialised.
+ * @exception InvalidCipherTextException if padding is expected and not found.
+ */
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException, InvalidCipherTextException
+ {
+ int blockSize = cipher.getBlockSize();
+ int resultLen = 0;
+
+ if (forEncryption)
+ {
+ if (bufOff == blockSize)
+ {
+ if ((outOff + 2 * blockSize) > out.length)
+ {
+ reset();
+
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ resultLen = cipher.processBlock(buf, 0, out, outOff);
+ bufOff = 0;
+ }
+
+ padding.addPadding(buf, bufOff);
+
+ resultLen += cipher.processBlock(buf, 0, out, outOff + resultLen);
+
+ reset();
+ }
+ else
+ {
+ if (bufOff == blockSize)
+ {
+ resultLen = cipher.processBlock(buf, 0, buf, 0);
+ bufOff = 0;
+ }
+ else
+ {
+ reset();
+
+ throw new DataLengthException("last block incomplete in decryption");
+ }
+
+ try
+ {
+ resultLen -= padding.padCount(buf);
+
+ System.arraycopy(buf, 0, out, outOff, resultLen);
+ }
+ finally
+ {
+ reset();
+ }
+ }
+
+ return resultLen;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/paddings/TBCPadding.java b/core/src/main/java/org/bouncycastle/crypto/paddings/TBCPadding.java
new file mode 100644
index 00000000..219912fe
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/paddings/TBCPadding.java
@@ -0,0 +1,89 @@
+package org.bouncycastle.crypto.paddings;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.InvalidCipherTextException;
+
+/**
+ * A padder that adds Trailing-Bit-Compliment padding to a block.
+ * <p>
+ * This padding pads the block out with the compliment of the last bit
+ * of the plain text.
+ * </p>
+ */
+public class TBCPadding
+ implements BlockCipherPadding
+{
+ /**
+ * Initialise the padder.
+ *
+ * @param random - a SecureRandom if available.
+ */
+ public void init(SecureRandom random)
+ throws IllegalArgumentException
+ {
+ // nothing to do.
+ }
+
+ /**
+ * Return the name of the algorithm the padder implements.
+ *
+ * @return the name of the algorithm the padder implements.
+ */
+ public String getPaddingName()
+ {
+ return "TBC";
+ }
+
+ /**
+ * add the pad bytes to the passed in block, returning the
+ * number of bytes added.
+ * <p>
+ * Note: this assumes that the last block of plain text is always
+ * passed to it inside in. i.e. if inOff is zero, indicating the
+ * entire block is to be overwritten with padding the value of in
+ * should be the same as the last block of plain text.
+ * </p>
+ */
+ public int addPadding(
+ byte[] in,
+ int inOff)
+ {
+ int count = in.length - inOff;
+ byte code;
+
+ if (inOff > 0)
+ {
+ code = (byte)((in[inOff - 1] & 0x01) == 0 ? 0xff : 0x00);
+ }
+ else
+ {
+ code = (byte)((in[in.length - 1] & 0x01) == 0 ? 0xff : 0x00);
+ }
+
+ while (inOff < in.length)
+ {
+ in[inOff] = code;
+ inOff++;
+ }
+
+ return count;
+ }
+
+ /**
+ * return the number of pad bytes present in the block.
+ */
+ public int padCount(byte[] in)
+ throws InvalidCipherTextException
+ {
+ byte code = in[in.length - 1];
+
+ int index = in.length - 1;
+ while (index > 0 && in[index - 1] == code)
+ {
+ index--;
+ }
+
+ return in.length - index;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/paddings/X923Padding.java b/core/src/main/java/org/bouncycastle/crypto/paddings/X923Padding.java
new file mode 100644
index 00000000..d4d34aad
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/paddings/X923Padding.java
@@ -0,0 +1,80 @@
+package org.bouncycastle.crypto.paddings;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.InvalidCipherTextException;
+
+/**
+ * A padder that adds X9.23 padding to a block - if a SecureRandom is
+ * passed in random padding is assumed, otherwise padding with zeros is used.
+ */
+public class X923Padding
+ implements BlockCipherPadding
+{
+ SecureRandom random = null;
+
+ /**
+ * Initialise the padder.
+ *
+ * @param random a SecureRandom if one is available.
+ */
+ public void init(SecureRandom random)
+ throws IllegalArgumentException
+ {
+ this.random = random;
+ }
+
+ /**
+ * Return the name of the algorithm the padder implements.
+ *
+ * @return the name of the algorithm the padder implements.
+ */
+ public String getPaddingName()
+ {
+ return "X9.23";
+ }
+
+ /**
+ * add the pad bytes to the passed in block, returning the
+ * number of bytes added.
+ */
+ public int addPadding(
+ byte[] in,
+ int inOff)
+ {
+ byte code = (byte)(in.length - inOff);
+
+ while (inOff < in.length - 1)
+ {
+ if (random == null)
+ {
+ in[inOff] = 0;
+ }
+ else
+ {
+ in[inOff] = (byte)random.nextInt();
+ }
+ inOff++;
+ }
+
+ in[inOff] = code;
+
+ return code;
+ }
+
+ /**
+ * return the number of pad bytes present in the block.
+ */
+ public int padCount(byte[] in)
+ throws InvalidCipherTextException
+ {
+ int count = in[in.length - 1] & 0xff;
+
+ if (count > in.length)
+ {
+ throw new InvalidCipherTextException("pad block corrupted");
+ }
+
+ return count;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/paddings/ZeroBytePadding.java b/core/src/main/java/org/bouncycastle/crypto/paddings/ZeroBytePadding.java
new file mode 100644
index 00000000..c7560286
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/paddings/ZeroBytePadding.java
@@ -0,0 +1,73 @@
+package org.bouncycastle.crypto.paddings;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.InvalidCipherTextException;
+
+/**
+ * A padder that adds NULL byte padding to a block.
+ */
+public class ZeroBytePadding
+ implements BlockCipherPadding
+{
+ /**
+ * Initialise the padder.
+ *
+ * @param random - a SecureRandom if available.
+ */
+ public void init(SecureRandom random)
+ throws IllegalArgumentException
+ {
+ // nothing to do.
+ }
+
+ /**
+ * Return the name of the algorithm the padder implements.
+ *
+ * @return the name of the algorithm the padder implements.
+ */
+ public String getPaddingName()
+ {
+ return "ZeroByte";
+ }
+
+ /**
+ * add the pad bytes to the passed in block, returning the
+ * number of bytes added.
+ */
+ public int addPadding(
+ byte[] in,
+ int inOff)
+ {
+ int added = (in.length - inOff);
+
+ while (inOff < in.length)
+ {
+ in[inOff] = (byte) 0;
+ inOff++;
+ }
+
+ return added;
+ }
+
+ /**
+ * return the number of pad bytes present in the block.
+ */
+ public int padCount(byte[] in)
+ throws InvalidCipherTextException
+ {
+ int count = in.length;
+
+ while (count > 0)
+ {
+ if (in[count - 1] != 0)
+ {
+ break;
+ }
+
+ count--;
+ }
+
+ return in.length - count;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/AEADParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/AEADParameters.java
new file mode 100644
index 00000000..9a9272ba
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/AEADParameters.java
@@ -0,0 +1,60 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public class AEADParameters
+ implements CipherParameters
+{
+ private byte[] associatedText;
+ private byte[] nonce;
+ private KeyParameter key;
+ private int macSize;
+
+ /**
+ * Base constructor.
+ *
+ * @param key key to be used by underlying cipher
+ * @param macSize macSize in bits
+ * @param nonce nonce to be used
+ */
+ public AEADParameters(KeyParameter key, int macSize, byte[] nonce)
+ {
+ this(key, macSize, nonce, null);
+ }
+
+ /**
+ * Base constructor.
+ *
+ * @param key key to be used by underlying cipher
+ * @param macSize macSize in bits
+ * @param nonce nonce to be used
+ * @param associatedText initial associated text, if any
+ */
+ public AEADParameters(KeyParameter key, int macSize, byte[] nonce, byte[] associatedText)
+ {
+ this.key = key;
+ this.nonce = nonce;
+ this.macSize = macSize;
+ this.associatedText = associatedText;
+ }
+
+ public KeyParameter getKey()
+ {
+ return key;
+ }
+
+ public int getMacSize()
+ {
+ return macSize;
+ }
+
+ public byte[] getAssociatedText()
+ {
+ return associatedText;
+ }
+
+ public byte[] getNonce()
+ {
+ return nonce;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/AsymmetricKeyParameter.java b/core/src/main/java/org/bouncycastle/crypto/params/AsymmetricKeyParameter.java
new file mode 100644
index 00000000..03ba7253
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/AsymmetricKeyParameter.java
@@ -0,0 +1,20 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public class AsymmetricKeyParameter
+ implements CipherParameters
+{
+ boolean privateKey;
+
+ public AsymmetricKeyParameter(
+ boolean privateKey)
+ {
+ this.privateKey = privateKey;
+ }
+
+ public boolean isPrivate()
+ {
+ return privateKey;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/CCMParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/CCMParameters.java
new file mode 100644
index 00000000..4924dcca
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/CCMParameters.java
@@ -0,0 +1,21 @@
+package org.bouncycastle.crypto.params;
+
+/**
+ * @deprecated use AEADParameters
+ */
+public class CCMParameters
+ extends AEADParameters
+{
+ /**
+ * Base constructor.
+ *
+ * @param key key to be used by underlying cipher
+ * @param macSize macSize in bits
+ * @param nonce nonce to be used
+ * @param associatedText associated text, if any
+ */
+ public CCMParameters(KeyParameter key, int macSize, byte[] nonce, byte[] associatedText)
+ {
+ super(key, macSize, nonce, associatedText);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/DESParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/DESParameters.java
new file mode 100644
index 00000000..5bee3604
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/DESParameters.java
@@ -0,0 +1,107 @@
+package org.bouncycastle.crypto.params;
+
+public class DESParameters
+ extends KeyParameter
+{
+ public DESParameters(
+ byte[] key)
+ {
+ super(key);
+
+ if (isWeakKey(key, 0))
+ {
+ throw new IllegalArgumentException("attempt to create weak DES key");
+ }
+ }
+
+ /*
+ * DES Key length in bytes.
+ */
+ static public final int DES_KEY_LENGTH = 8;
+
+ /*
+ * Table of weak and semi-weak keys taken from Schneier pp281
+ */
+ static private final int N_DES_WEAK_KEYS = 16;
+
+ static private byte[] DES_weak_keys =
+ {
+ /* weak keys */
+ (byte)0x01,(byte)0x01,(byte)0x01,(byte)0x01, (byte)0x01,(byte)0x01,(byte)0x01,(byte)0x01,
+ (byte)0x1f,(byte)0x1f,(byte)0x1f,(byte)0x1f, (byte)0x0e,(byte)0x0e,(byte)0x0e,(byte)0x0e,
+ (byte)0xe0,(byte)0xe0,(byte)0xe0,(byte)0xe0, (byte)0xf1,(byte)0xf1,(byte)0xf1,(byte)0xf1,
+ (byte)0xfe,(byte)0xfe,(byte)0xfe,(byte)0xfe, (byte)0xfe,(byte)0xfe,(byte)0xfe,(byte)0xfe,
+
+ /* semi-weak keys */
+ (byte)0x01,(byte)0xfe,(byte)0x01,(byte)0xfe, (byte)0x01,(byte)0xfe,(byte)0x01,(byte)0xfe,
+ (byte)0x1f,(byte)0xe0,(byte)0x1f,(byte)0xe0, (byte)0x0e,(byte)0xf1,(byte)0x0e,(byte)0xf1,
+ (byte)0x01,(byte)0xe0,(byte)0x01,(byte)0xe0, (byte)0x01,(byte)0xf1,(byte)0x01,(byte)0xf1,
+ (byte)0x1f,(byte)0xfe,(byte)0x1f,(byte)0xfe, (byte)0x0e,(byte)0xfe,(byte)0x0e,(byte)0xfe,
+ (byte)0x01,(byte)0x1f,(byte)0x01,(byte)0x1f, (byte)0x01,(byte)0x0e,(byte)0x01,(byte)0x0e,
+ (byte)0xe0,(byte)0xfe,(byte)0xe0,(byte)0xfe, (byte)0xf1,(byte)0xfe,(byte)0xf1,(byte)0xfe,
+ (byte)0xfe,(byte)0x01,(byte)0xfe,(byte)0x01, (byte)0xfe,(byte)0x01,(byte)0xfe,(byte)0x01,
+ (byte)0xe0,(byte)0x1f,(byte)0xe0,(byte)0x1f, (byte)0xf1,(byte)0x0e,(byte)0xf1,(byte)0x0e,
+ (byte)0xe0,(byte)0x01,(byte)0xe0,(byte)0x01, (byte)0xf1,(byte)0x01,(byte)0xf1,(byte)0x01,
+ (byte)0xfe,(byte)0x1f,(byte)0xfe,(byte)0x1f, (byte)0xfe,(byte)0x0e,(byte)0xfe,(byte)0x0e,
+ (byte)0x1f,(byte)0x01,(byte)0x1f,(byte)0x01, (byte)0x0e,(byte)0x01,(byte)0x0e,(byte)0x01,
+ (byte)0xfe,(byte)0xe0,(byte)0xfe,(byte)0xe0, (byte)0xfe,(byte)0xf1,(byte)0xfe,(byte)0xf1
+ };
+
+ /**
+ * DES has 16 weak keys. This method will check
+ * if the given DES key material is weak or semi-weak.
+ * Key material that is too short is regarded as weak.
+ * <p>
+ * See <a href="http://www.counterpane.com/applied.html">"Applied
+ * Cryptography"</a> by Bruce Schneier for more information.
+ *
+ * @return true if the given DES key material is weak or semi-weak,
+ * false otherwise.
+ */
+ public static boolean isWeakKey(
+ byte[] key,
+ int offset)
+ {
+ if (key.length - offset < DES_KEY_LENGTH)
+ {
+ throw new IllegalArgumentException("key material too short.");
+ }
+
+ nextkey: for (int i = 0; i < N_DES_WEAK_KEYS; i++)
+ {
+ for (int j = 0; j < DES_KEY_LENGTH; j++)
+ {
+ if (key[j + offset] != DES_weak_keys[i * DES_KEY_LENGTH + j])
+ {
+ continue nextkey;
+ }
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * DES Keys use the LSB as the odd parity bit. This can
+ * be used to check for corrupt keys.
+ *
+ * @param bytes the byte array to set the parity on.
+ */
+ public static void setOddParity(
+ byte[] bytes)
+ {
+ for (int i = 0; i < bytes.length; i++)
+ {
+ int b = bytes[i];
+ bytes[i] = (byte)((b & 0xfe) |
+ ((((b >> 1) ^
+ (b >> 2) ^
+ (b >> 3) ^
+ (b >> 4) ^
+ (b >> 5) ^
+ (b >> 6) ^
+ (b >> 7)) ^ 0x01) & 0x01));
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/DESedeParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/DESedeParameters.java
new file mode 100644
index 00000000..3a4bbfca
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/DESedeParameters.java
@@ -0,0 +1,57 @@
+package org.bouncycastle.crypto.params;
+
+public class DESedeParameters
+ extends DESParameters
+{
+ /*
+ * DES-EDE Key length in bytes.
+ */
+ static public final int DES_EDE_KEY_LENGTH = 24;
+
+ public DESedeParameters(
+ byte[] key)
+ {
+ super(key);
+
+ if (isWeakKey(key, 0, key.length))
+ {
+ throw new IllegalArgumentException("attempt to create weak DESede key");
+ }
+ }
+
+ /**
+ * return true if the passed in key is a DES-EDE weak key.
+ *
+ * @param key bytes making up the key
+ * @param offset offset into the byte array the key starts at
+ * @param length number of bytes making up the key
+ */
+ public static boolean isWeakKey(
+ byte[] key,
+ int offset,
+ int length)
+ {
+ for (int i = offset; i < length; i += DES_KEY_LENGTH)
+ {
+ if (DESParameters.isWeakKey(key, i))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * return true if the passed in key is a DES-EDE weak key.
+ *
+ * @param key bytes making up the key
+ * @param offset offset into the byte array the key starts at
+ */
+ public static boolean isWeakKey(
+ byte[] key,
+ int offset)
+ {
+ return isWeakKey(key, offset, key.length - offset);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/DHKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/DHKeyGenerationParameters.java
new file mode 100644
index 00000000..34c730eb
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/DHKeyGenerationParameters.java
@@ -0,0 +1,30 @@
+package org.bouncycastle.crypto.params;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.KeyGenerationParameters;
+
+public class DHKeyGenerationParameters
+ extends KeyGenerationParameters
+{
+ private DHParameters params;
+
+ public DHKeyGenerationParameters(
+ SecureRandom random,
+ DHParameters params)
+ {
+ super(random, getStrength(params));
+
+ this.params = params;
+ }
+
+ public DHParameters getParameters()
+ {
+ return params;
+ }
+
+ static int getStrength(DHParameters params)
+ {
+ return params.getL() != 0 ? params.getL() : params.getP().bitLength();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/DHKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/DHKeyParameters.java
new file mode 100644
index 00000000..e686f357
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/DHKeyParameters.java
@@ -0,0 +1,54 @@
+package org.bouncycastle.crypto.params;
+
+
+public class DHKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private DHParameters params;
+
+ protected DHKeyParameters(
+ boolean isPrivate,
+ DHParameters params)
+ {
+ super(isPrivate);
+
+ this.params = params;
+ }
+
+ public DHParameters getParameters()
+ {
+ return params;
+ }
+
+ public boolean equals(
+ Object obj)
+ {
+ if (!(obj instanceof DHKeyParameters))
+ {
+ return false;
+ }
+
+ DHKeyParameters dhKey = (DHKeyParameters)obj;
+
+ if (params == null)
+ {
+ return dhKey.getParameters() == null;
+ }
+ else
+ {
+ return params.equals(dhKey.getParameters());
+ }
+ }
+
+ public int hashCode()
+ {
+ int code = isPrivate() ? 0 : 1;
+
+ if (params != null)
+ {
+ code ^= params.hashCode();
+ }
+
+ return code;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/DHParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/DHParameters.java
new file mode 100644
index 00000000..b679287c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/DHParameters.java
@@ -0,0 +1,189 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public class DHParameters
+ implements CipherParameters
+{
+ private static final int DEFAULT_MINIMUM_LENGTH = 160;
+
+ // not final due to compiler bug in "simpler" JDKs
+ private BigInteger g;
+ private BigInteger p;
+ private BigInteger q;
+ private BigInteger j;
+ private int m;
+ private int l;
+ private DHValidationParameters validation;
+
+ private static int getDefaultMParam(
+ int lParam)
+ {
+ if (lParam == 0)
+ {
+ return DEFAULT_MINIMUM_LENGTH;
+ }
+
+ return lParam < DEFAULT_MINIMUM_LENGTH ? lParam : DEFAULT_MINIMUM_LENGTH;
+ }
+
+ public DHParameters(
+ BigInteger p,
+ BigInteger g)
+ {
+ this(p, g, null, 0);
+ }
+
+ public DHParameters(
+ BigInteger p,
+ BigInteger g,
+ BigInteger q)
+ {
+ this(p, g, q, 0);
+ }
+
+ public DHParameters(
+ BigInteger p,
+ BigInteger g,
+ BigInteger q,
+ int l)
+ {
+ this(p, g, q, getDefaultMParam(l), l, null, null);
+ }
+
+ public DHParameters(
+ BigInteger p,
+ BigInteger g,
+ BigInteger q,
+ int m,
+ int l)
+ {
+ this(p, g, q, m, l, null, null);
+ }
+
+ public DHParameters(
+ BigInteger p,
+ BigInteger g,
+ BigInteger q,
+ BigInteger j,
+ DHValidationParameters validation)
+ {
+ this(p, g, q, DEFAULT_MINIMUM_LENGTH, 0, j, validation);
+ }
+
+ public DHParameters(
+ BigInteger p,
+ BigInteger g,
+ BigInteger q,
+ int m,
+ int l,
+ BigInteger j,
+ DHValidationParameters validation)
+ {
+ if (l != 0)
+ {
+ BigInteger bigL = BigInteger.valueOf(2L ^ (l - 1));
+ if (bigL.compareTo(p) == 1)
+ {
+ throw new IllegalArgumentException("when l value specified, it must satisfy 2^(l-1) <= p");
+ }
+ if (l < m)
+ {
+ throw new IllegalArgumentException("when l value specified, it may not be less than m value");
+ }
+ }
+
+ this.g = g;
+ this.p = p;
+ this.q = q;
+ this.m = m;
+ this.l = l;
+ this.j = j;
+ this.validation = validation;
+ }
+
+ public BigInteger getP()
+ {
+ return p;
+ }
+
+ public BigInteger getG()
+ {
+ return g;
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ /**
+ * Return the subgroup factor J.
+ *
+ * @return subgroup factor
+ */
+ public BigInteger getJ()
+ {
+ return j;
+ }
+
+ /**
+ * Return the minimum length of the private value.
+ *
+ * @return the minimum length of the private value in bits.
+ */
+ public int getM()
+ {
+ return m;
+ }
+
+ /**
+ * Return the private value length in bits - if set, zero otherwise
+ *
+ * @return the private value length in bits, zero otherwise.
+ */
+ public int getL()
+ {
+ return l;
+ }
+
+ public DHValidationParameters getValidationParameters()
+ {
+ return validation;
+ }
+
+ public boolean equals(
+ Object obj)
+ {
+ if (!(obj instanceof DHParameters))
+ {
+ return false;
+ }
+
+ DHParameters pm = (DHParameters)obj;
+
+ if (this.getQ() != null)
+ {
+ if (!this.getQ().equals(pm.getQ()))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (pm.getQ() != null)
+ {
+ return false;
+ }
+ }
+
+ return pm.getP().equals(p) && pm.getG().equals(g);
+ }
+
+ public int hashCode()
+ {
+ return getP().hashCode() ^ getG().hashCode() ^ (getQ() != null ? getQ().hashCode() : 0);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/DHPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/DHPrivateKeyParameters.java
new file mode 100644
index 00000000..ee1b34f9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/DHPrivateKeyParameters.java
@@ -0,0 +1,41 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class DHPrivateKeyParameters
+ extends DHKeyParameters
+{
+ private BigInteger x;
+
+ public DHPrivateKeyParameters(
+ BigInteger x,
+ DHParameters params)
+ {
+ super(true, params);
+
+ this.x = x;
+ }
+
+ public BigInteger getX()
+ {
+ return x;
+ }
+
+ public int hashCode()
+ {
+ return x.hashCode() ^ super.hashCode();
+ }
+
+ public boolean equals(
+ Object obj)
+ {
+ if (!(obj instanceof DHPrivateKeyParameters))
+ {
+ return false;
+ }
+
+ DHPrivateKeyParameters other = (DHPrivateKeyParameters)obj;
+
+ return other.getX().equals(this.x) && super.equals(obj);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/DHPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/DHPublicKeyParameters.java
new file mode 100644
index 00000000..ed531603
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/DHPublicKeyParameters.java
@@ -0,0 +1,41 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class DHPublicKeyParameters
+ extends DHKeyParameters
+{
+ private BigInteger y;
+
+ public DHPublicKeyParameters(
+ BigInteger y,
+ DHParameters params)
+ {
+ super(false, params);
+
+ this.y = y;
+ }
+
+ public BigInteger getY()
+ {
+ return y;
+ }
+
+ public int hashCode()
+ {
+ return y.hashCode() ^ super.hashCode();
+ }
+
+ public boolean equals(
+ Object obj)
+ {
+ if (!(obj instanceof DHPublicKeyParameters))
+ {
+ return false;
+ }
+
+ DHPublicKeyParameters other = (DHPublicKeyParameters)obj;
+
+ return other.getY().equals(y) && super.equals(obj);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/DHValidationParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/DHValidationParameters.java
new file mode 100644
index 00000000..b22f7a03
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/DHValidationParameters.java
@@ -0,0 +1,50 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.util.Arrays;
+
+public class DHValidationParameters
+{
+ private byte[] seed;
+ private int counter;
+
+ public DHValidationParameters(
+ byte[] seed,
+ int counter)
+ {
+ this.seed = seed;
+ this.counter = counter;
+ }
+
+ public int getCounter()
+ {
+ return counter;
+ }
+
+ public byte[] getSeed()
+ {
+ return seed;
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof DHValidationParameters))
+ {
+ return false;
+ }
+
+ DHValidationParameters other = (DHValidationParameters)o;
+
+ if (other.counter != this.counter)
+ {
+ return false;
+ }
+
+ return Arrays.areEqual(this.seed, other.seed);
+ }
+
+ public int hashCode()
+ {
+ return counter ^ Arrays.hashCode(seed);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/DSAKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/DSAKeyGenerationParameters.java
new file mode 100644
index 00000000..29fa91e6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/DSAKeyGenerationParameters.java
@@ -0,0 +1,25 @@
+package org.bouncycastle.crypto.params;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.KeyGenerationParameters;
+
+public class DSAKeyGenerationParameters
+ extends KeyGenerationParameters
+{
+ private DSAParameters params;
+
+ public DSAKeyGenerationParameters(
+ SecureRandom random,
+ DSAParameters params)
+ {
+ super(random, params.getP().bitLength() - 1);
+
+ this.params = params;
+ }
+
+ public DSAParameters getParameters()
+ {
+ return params;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/DSAKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/DSAKeyParameters.java
new file mode 100644
index 00000000..11bb9d97
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/DSAKeyParameters.java
@@ -0,0 +1,21 @@
+package org.bouncycastle.crypto.params;
+
+public class DSAKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private DSAParameters params;
+
+ public DSAKeyParameters(
+ boolean isPrivate,
+ DSAParameters params)
+ {
+ super(isPrivate);
+
+ this.params = params;
+ }
+
+ public DSAParameters getParameters()
+ {
+ return params;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/DSAParameterGenerationParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/DSAParameterGenerationParameters.java
new file mode 100644
index 00000000..ba841b8e
--- /dev/null
+++ b/core/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/core/src/main/java/org/bouncycastle/crypto/params/DSAParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/DSAParameters.java
new file mode 100644
index 00000000..7f76d117
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/DSAParameters.java
@@ -0,0 +1,74 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public class DSAParameters
+ implements CipherParameters
+{
+ private BigInteger g;
+ private BigInteger q;
+ private BigInteger p;
+ private DSAValidationParameters validation;
+
+ public DSAParameters(
+ BigInteger p,
+ BigInteger q,
+ BigInteger g)
+ {
+ this.g = g;
+ this.p = p;
+ this.q = q;
+ }
+
+ public DSAParameters(
+ BigInteger p,
+ BigInteger q,
+ BigInteger g,
+ DSAValidationParameters params)
+ {
+ this.g = g;
+ this.p = p;
+ this.q = q;
+ this.validation = params;
+ }
+
+ public BigInteger getP()
+ {
+ return p;
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public BigInteger getG()
+ {
+ return g;
+ }
+
+ public DSAValidationParameters getValidationParameters()
+ {
+ return validation;
+ }
+
+ public boolean equals(
+ Object obj)
+ {
+ if (!(obj instanceof DSAParameters))
+ {
+ return false;
+ }
+
+ DSAParameters pm = (DSAParameters)obj;
+
+ return (pm.getP().equals(p) && pm.getQ().equals(q) && pm.getG().equals(g));
+ }
+
+ public int hashCode()
+ {
+ return getP().hashCode() ^ getQ().hashCode() ^ getG().hashCode();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/DSAPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/DSAPrivateKeyParameters.java
new file mode 100644
index 00000000..3bef3f40
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/DSAPrivateKeyParameters.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class DSAPrivateKeyParameters
+ extends DSAKeyParameters
+{
+ private BigInteger x;
+
+ public DSAPrivateKeyParameters(
+ BigInteger x,
+ DSAParameters params)
+ {
+ super(true, params);
+
+ this.x = x;
+ }
+
+ public BigInteger getX()
+ {
+ return x;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/DSAPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/DSAPublicKeyParameters.java
new file mode 100644
index 00000000..c0066568
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/DSAPublicKeyParameters.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class DSAPublicKeyParameters
+ extends DSAKeyParameters
+{
+ private BigInteger y;
+
+ public DSAPublicKeyParameters(
+ BigInteger y,
+ DSAParameters params)
+ {
+ super(false, params);
+
+ this.y = y;
+ }
+
+ public BigInteger getY()
+ {
+ return y;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/DSAValidationParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/DSAValidationParameters.java
new file mode 100644
index 00000000..07d93d07
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/DSAValidationParameters.java
@@ -0,0 +1,65 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.util.Arrays;
+
+public class DSAValidationParameters
+{
+ private int usageIndex;
+ private byte[] seed;
+ private int counter;
+
+ public 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()
+ {
+ return counter;
+ }
+
+ public byte[] getSeed()
+ {
+ return seed;
+ }
+
+ public int getUsageIndex()
+ {
+ return usageIndex;
+ }
+
+ public int hashCode()
+ {
+ return counter ^ Arrays.hashCode(seed);
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof DSAValidationParameters))
+ {
+ return false;
+ }
+
+ DSAValidationParameters other = (DSAValidationParameters)o;
+
+ if (other.counter != this.counter)
+ {
+ return false;
+ }
+
+ return Arrays.areEqual(this.seed, other.seed);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java
new file mode 100644
index 00000000..05a13274
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java
@@ -0,0 +1,74 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.Arrays;
+
+public class ECDomainParameters
+ implements ECConstants
+{
+ private ECCurve curve;
+ private byte[] seed;
+ private ECPoint G;
+ private BigInteger n;
+ private BigInteger h;
+
+ public ECDomainParameters(
+ ECCurve curve,
+ ECPoint G,
+ BigInteger n)
+ {
+ this(curve, G, n, ONE, null);
+ }
+
+ public ECDomainParameters(
+ ECCurve curve,
+ ECPoint G,
+ BigInteger n,
+ BigInteger h)
+ {
+ this(curve, G, n, h, null);
+ }
+
+ public ECDomainParameters(
+ ECCurve curve,
+ ECPoint G,
+ BigInteger n,
+ BigInteger h,
+ byte[] seed)
+ {
+ this.curve = curve;
+ this.G = G;
+ this.n = n;
+ this.h = h;
+ this.seed = seed;
+ }
+
+ public ECCurve getCurve()
+ {
+ return curve;
+ }
+
+ public ECPoint getG()
+ {
+ return G;
+ }
+
+ public BigInteger getN()
+ {
+ return n;
+ }
+
+ public BigInteger getH()
+ {
+ return h;
+ }
+
+ public byte[] getSeed()
+ {
+ return Arrays.clone(seed);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECKeyGenerationParameters.java
new file mode 100644
index 00000000..be3f20f5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/ECKeyGenerationParameters.java
@@ -0,0 +1,25 @@
+package org.bouncycastle.crypto.params;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.KeyGenerationParameters;
+
+public class ECKeyGenerationParameters
+ extends KeyGenerationParameters
+{
+ private ECDomainParameters domainParams;
+
+ public ECKeyGenerationParameters(
+ ECDomainParameters domainParams,
+ SecureRandom random)
+ {
+ super(random, domainParams.getN().bitLength());
+
+ this.domainParams = domainParams;
+ }
+
+ public ECDomainParameters getDomainParameters()
+ {
+ return domainParams;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECKeyParameters.java
new file mode 100644
index 00000000..19825c5b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/ECKeyParameters.java
@@ -0,0 +1,21 @@
+package org.bouncycastle.crypto.params;
+
+public class ECKeyParameters
+ extends AsymmetricKeyParameter
+{
+ ECDomainParameters params;
+
+ protected ECKeyParameters(
+ boolean isPrivate,
+ ECDomainParameters params)
+ {
+ super(isPrivate);
+
+ this.params = params;
+ }
+
+ public ECDomainParameters getParameters()
+ {
+ return params;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECPrivateKeyParameters.java
new file mode 100644
index 00000000..3e49983e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/ECPrivateKeyParameters.java
@@ -0,0 +1,22 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class ECPrivateKeyParameters
+ extends ECKeyParameters
+{
+ BigInteger d;
+
+ public ECPrivateKeyParameters(
+ BigInteger d,
+ ECDomainParameters params)
+ {
+ super(true, params);
+ this.d = d;
+ }
+
+ public BigInteger getD()
+ {
+ return d;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java
new file mode 100644
index 00000000..5fbea19e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java
@@ -0,0 +1,22 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.math.ec.ECPoint;
+
+public class ECPublicKeyParameters
+ extends ECKeyParameters
+{
+ ECPoint Q;
+
+ public ECPublicKeyParameters(
+ ECPoint Q,
+ ECDomainParameters params)
+ {
+ super(false, params);
+ this.Q = Q;
+ }
+
+ public ECPoint getQ()
+ {
+ return Q;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ElGamalKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ElGamalKeyGenerationParameters.java
new file mode 100644
index 00000000..f5fbabd5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/ElGamalKeyGenerationParameters.java
@@ -0,0 +1,30 @@
+package org.bouncycastle.crypto.params;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.KeyGenerationParameters;
+
+public class ElGamalKeyGenerationParameters
+ extends KeyGenerationParameters
+{
+ private ElGamalParameters params;
+
+ public ElGamalKeyGenerationParameters(
+ SecureRandom random,
+ ElGamalParameters params)
+ {
+ super(random, getStrength(params));
+
+ this.params = params;
+ }
+
+ public ElGamalParameters getParameters()
+ {
+ return params;
+ }
+
+ static int getStrength(ElGamalParameters params)
+ {
+ return params.getL() != 0 ? params.getL() : params.getP().bitLength();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ElGamalKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ElGamalKeyParameters.java
new file mode 100644
index 00000000..72506931
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/ElGamalKeyParameters.java
@@ -0,0 +1,47 @@
+package org.bouncycastle.crypto.params;
+
+
+public class ElGamalKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private ElGamalParameters params;
+
+ protected ElGamalKeyParameters(
+ boolean isPrivate,
+ ElGamalParameters params)
+ {
+ super(isPrivate);
+
+ this.params = params;
+ }
+
+ public ElGamalParameters getParameters()
+ {
+ return params;
+ }
+
+ public int hashCode()
+ {
+ return (params != null) ? params.hashCode() : 0;
+ }
+
+ public boolean equals(
+ Object obj)
+ {
+ if (!(obj instanceof ElGamalKeyParameters))
+ {
+ return false;
+ }
+
+ ElGamalKeyParameters dhKey = (ElGamalKeyParameters)obj;
+
+ if (params == null)
+ {
+ return dhKey.getParameters() == null;
+ }
+ else
+ {
+ return params.equals(dhKey.getParameters());
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ElGamalParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ElGamalParameters.java
new file mode 100644
index 00000000..166eff35
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/ElGamalParameters.java
@@ -0,0 +1,69 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public class ElGamalParameters
+ implements CipherParameters
+{
+ private BigInteger g;
+ private BigInteger p;
+ private int l;
+
+ public ElGamalParameters(
+ BigInteger p,
+ BigInteger g)
+ {
+ this(p, g, 0);
+ }
+
+ public ElGamalParameters(
+ BigInteger p,
+ BigInteger g,
+ int l)
+ {
+ this.g = g;
+ this.p = p;
+ this.l = l;
+ }
+
+ public BigInteger getP()
+ {
+ return p;
+ }
+
+ /**
+ * return the generator - g
+ */
+ public BigInteger getG()
+ {
+ return g;
+ }
+
+ /**
+ * return private value limit - l
+ */
+ public int getL()
+ {
+ return l;
+ }
+
+ public boolean equals(
+ Object obj)
+ {
+ if (!(obj instanceof ElGamalParameters))
+ {
+ return false;
+ }
+
+ ElGamalParameters pm = (ElGamalParameters)obj;
+
+ return pm.getP().equals(p) && pm.getG().equals(g) && pm.getL() == l;
+ }
+
+ public int hashCode()
+ {
+ return (getP().hashCode() ^ getG().hashCode()) + l;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ElGamalPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ElGamalPrivateKeyParameters.java
new file mode 100644
index 00000000..b8fb529f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/ElGamalPrivateKeyParameters.java
@@ -0,0 +1,46 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class ElGamalPrivateKeyParameters
+ extends ElGamalKeyParameters
+{
+ private BigInteger x;
+
+ public ElGamalPrivateKeyParameters(
+ BigInteger x,
+ ElGamalParameters params)
+ {
+ super(true, params);
+
+ this.x = x;
+ }
+
+ public BigInteger getX()
+ {
+ return x;
+ }
+
+ public boolean equals(
+ Object obj)
+ {
+ if (!(obj instanceof ElGamalPrivateKeyParameters))
+ {
+ return false;
+ }
+
+ ElGamalPrivateKeyParameters pKey = (ElGamalPrivateKeyParameters)obj;
+
+ if (!pKey.getX().equals(x))
+ {
+ return false;
+ }
+
+ return super.equals(obj);
+ }
+
+ public int hashCode()
+ {
+ return getX().hashCode();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ElGamalPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ElGamalPublicKeyParameters.java
new file mode 100644
index 00000000..d7da7a93
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/ElGamalPublicKeyParameters.java
@@ -0,0 +1,41 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class ElGamalPublicKeyParameters
+ extends ElGamalKeyParameters
+{
+ private BigInteger y;
+
+ public ElGamalPublicKeyParameters(
+ BigInteger y,
+ ElGamalParameters params)
+ {
+ super(false, params);
+
+ this.y = y;
+ }
+
+ public BigInteger getY()
+ {
+ return y;
+ }
+
+ public int hashCode()
+ {
+ return y.hashCode() ^ super.hashCode();
+ }
+
+ public boolean equals(
+ Object obj)
+ {
+ if (!(obj instanceof ElGamalPublicKeyParameters))
+ {
+ return false;
+ }
+
+ ElGamalPublicKeyParameters other = (ElGamalPublicKeyParameters)obj;
+
+ return other.getY().equals(y) && super.equals(obj);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/GOST3410KeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/GOST3410KeyGenerationParameters.java
new file mode 100644
index 00000000..74e05a96
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/GOST3410KeyGenerationParameters.java
@@ -0,0 +1,25 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.KeyGenerationParameters;
+
+import java.security.SecureRandom;
+
+public class GOST3410KeyGenerationParameters
+ extends KeyGenerationParameters
+{
+ private GOST3410Parameters params;
+
+ public GOST3410KeyGenerationParameters(
+ SecureRandom random,
+ GOST3410Parameters params)
+ {
+ super(random, params.getP().bitLength() - 1);
+
+ this.params = params;
+ }
+
+ public GOST3410Parameters getParameters()
+ {
+ return params;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/GOST3410KeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/GOST3410KeyParameters.java
new file mode 100644
index 00000000..6716924a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/GOST3410KeyParameters.java
@@ -0,0 +1,21 @@
+package org.bouncycastle.crypto.params;
+
+public class GOST3410KeyParameters
+ extends AsymmetricKeyParameter
+{
+ private GOST3410Parameters params;
+
+ public GOST3410KeyParameters(
+ boolean isPrivate,
+ GOST3410Parameters params)
+ {
+ super(isPrivate);
+
+ this.params = params;
+ }
+
+ public GOST3410Parameters getParameters()
+ {
+ return params;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/GOST3410Parameters.java b/core/src/main/java/org/bouncycastle/crypto/params/GOST3410Parameters.java
new file mode 100644
index 00000000..07450f61
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/GOST3410Parameters.java
@@ -0,0 +1,74 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+import java.math.BigInteger;
+
+public class GOST3410Parameters
+ implements CipherParameters
+{
+ private BigInteger p;
+ private BigInteger q;
+ private BigInteger a;
+ private GOST3410ValidationParameters validation;
+
+ public GOST3410Parameters(
+ BigInteger p,
+ BigInteger q,
+ BigInteger a)
+ {
+ this.p = p;
+ this.q = q;
+ this.a = a;
+ }
+
+ public GOST3410Parameters(
+ BigInteger p,
+ BigInteger q,
+ BigInteger a,
+ GOST3410ValidationParameters params)
+ {
+ this.a = a;
+ this.p = p;
+ this.q = q;
+ this.validation = params;
+ }
+
+ public BigInteger getP()
+ {
+ return p;
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public BigInteger getA()
+ {
+ return a;
+ }
+
+ public GOST3410ValidationParameters getValidationParameters()
+ {
+ return validation;
+ }
+
+ public int hashCode()
+ {
+ return p.hashCode() ^ q.hashCode() ^ a.hashCode();
+ }
+
+ public boolean equals(
+ Object obj)
+ {
+ if (!(obj instanceof GOST3410Parameters))
+ {
+ return false;
+ }
+
+ GOST3410Parameters pm = (GOST3410Parameters)obj;
+
+ return (pm.getP().equals(p) && pm.getQ().equals(q) && pm.getA().equals(a));
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/GOST3410PrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/GOST3410PrivateKeyParameters.java
new file mode 100644
index 00000000..408e0657
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/GOST3410PrivateKeyParameters.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class GOST3410PrivateKeyParameters
+ extends GOST3410KeyParameters
+{
+ private BigInteger x;
+
+ public GOST3410PrivateKeyParameters(
+ BigInteger x,
+ GOST3410Parameters params)
+ {
+ super(true, params);
+
+ this.x = x;
+ }
+
+ public BigInteger getX()
+ {
+ return x;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/GOST3410PublicKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/GOST3410PublicKeyParameters.java
new file mode 100644
index 00000000..9dfd2d98
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/GOST3410PublicKeyParameters.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class GOST3410PublicKeyParameters
+ extends GOST3410KeyParameters
+{
+ private BigInteger y;
+
+ public GOST3410PublicKeyParameters(
+ BigInteger y,
+ GOST3410Parameters params)
+ {
+ super(false, params);
+
+ this.y = y;
+ }
+
+ public BigInteger getY()
+ {
+ return y;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/GOST3410ValidationParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/GOST3410ValidationParameters.java
new file mode 100644
index 00000000..c2a4fb57
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/GOST3410ValidationParameters.java
@@ -0,0 +1,84 @@
+package org.bouncycastle.crypto.params;
+
+public class GOST3410ValidationParameters
+{
+ private int x0;
+ private int c;
+ private long x0L;
+ private long cL;
+
+
+ public GOST3410ValidationParameters(
+ int x0,
+ int c)
+ {
+ this.x0 = x0;
+ this.c = c;
+ }
+
+ public GOST3410ValidationParameters(
+ long x0L,
+ long cL)
+ {
+ this.x0L = x0L;
+ this.cL = cL;
+ }
+
+ public int getC()
+ {
+ return c;
+ }
+
+ public int getX0()
+ {
+ return x0;
+ }
+
+ public long getCL()
+ {
+ return cL;
+ }
+
+ public long getX0L()
+ {
+ return x0L;
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof GOST3410ValidationParameters))
+ {
+ return false;
+ }
+
+ GOST3410ValidationParameters other = (GOST3410ValidationParameters)o;
+
+ if (other.c != this.c)
+ {
+ return false;
+ }
+
+ if (other.x0 != this.x0)
+ {
+ return false;
+ }
+
+ if (other.cL != this.cL)
+ {
+ return false;
+ }
+
+ if (other.x0L != this.x0L)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ public int hashCode()
+ {
+ return x0 ^ c ^ (int) x0L ^ (int)(x0L >> 32) ^ (int) cL ^ (int)(cL >> 32);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/HKDFParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/HKDFParameters.java
new file mode 100644
index 00000000..2db3ce61
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/HKDFParameters.java
@@ -0,0 +1,123 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.DerivationParameters;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Parameter class for the HKDFBytesGenerator class.
+ */
+public class HKDFParameters
+ implements DerivationParameters
+{
+ private final byte[] ikm;
+ private final boolean skipExpand;
+ private final byte[] salt;
+ private final byte[] info;
+
+ private HKDFParameters(final byte[] ikm, final boolean skip,
+ final byte[] salt, final byte[] info)
+ {
+ if (ikm == null)
+ {
+ throw new IllegalArgumentException(
+ "IKM (input keying material) should not be null");
+ }
+
+ this.ikm = Arrays.clone(ikm);
+
+ this.skipExpand = skip;
+
+ if (salt == null || salt.length == 0)
+ {
+ this.salt = null;
+ }
+ else
+ {
+ this.salt = Arrays.clone(salt);
+ }
+
+ if (info == null)
+ {
+ this.info = new byte[0];
+ }
+ else
+ {
+ this.info = Arrays.clone(info);
+ }
+ }
+
+ /**
+ * Generates parameters for HKDF, specifying both the optional salt and
+ * optional info. Step 1: Extract won't be skipped.
+ *
+ * @param ikm the input keying material or seed
+ * @param salt the salt to use, may be null for a salt for hashLen zeros
+ * @param info the info to use, may be null for an info field of zero bytes
+ */
+ public HKDFParameters(final byte[] ikm, final byte[] salt, final byte[] info)
+ {
+ this(ikm, false, salt, info);
+ }
+
+ /**
+ * Factory method that makes the HKDF skip the extract part of the key
+ * derivation function.
+ *
+ * @param ikm the input keying material or seed, directly used for step 2:
+ * Expand
+ * @param info the info to use, may be null for an info field of zero bytes
+ * @return HKDFParameters that makes the implementation skip step 1
+ */
+ public static HKDFParameters skipExtractParameters(final byte[] ikm,
+ final byte[] info)
+ {
+
+ return new HKDFParameters(ikm, true, null, info);
+ }
+
+ public static HKDFParameters defaultParameters(final byte[] ikm)
+ {
+ return new HKDFParameters(ikm, false, null, null);
+ }
+
+ /**
+ * Returns the input keying material or seed.
+ *
+ * @return the keying material
+ */
+ public byte[] getIKM()
+ {
+ return Arrays.clone(ikm);
+ }
+
+ /**
+ * Returns if step 1: extract has to be skipped or not
+ *
+ * @return true for skipping, false for no skipping of step 1
+ */
+ public boolean skipExtract()
+ {
+ return skipExpand;
+ }
+
+ /**
+ * Returns the salt, or null if the salt should be generated as a byte array
+ * of HashLen zeros.
+ *
+ * @return the salt, or null
+ */
+ public byte[] getSalt()
+ {
+ return Arrays.clone(salt);
+ }
+
+ /**
+ * Returns the info field, which may be empty (null is converted to empty).
+ *
+ * @return the info field, never null
+ */
+ public byte[] getInfo()
+ {
+ return Arrays.clone(info);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/IESParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/IESParameters.java
new file mode 100644
index 00000000..0600b346
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/IESParameters.java
@@ -0,0 +1,44 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+/**
+ * parameters for using an integrated cipher in stream mode.
+ */
+public class IESParameters
+ implements CipherParameters
+{
+ private byte[] derivation;
+ private byte[] encoding;
+ private int macKeySize;
+
+ /**
+ * @param derivation the derivation parameter for the KDF function.
+ * @param encoding the encoding parameter for the KDF function.
+ * @param macKeySize the size of the MAC key (in bits).
+ */
+ public IESParameters(
+ byte[] derivation,
+ byte[] encoding,
+ int macKeySize)
+ {
+ this.derivation = derivation;
+ this.encoding = encoding;
+ this.macKeySize = macKeySize;
+ }
+
+ public byte[] getDerivationV()
+ {
+ return derivation;
+ }
+
+ public byte[] getEncodingV()
+ {
+ return encoding;
+ }
+
+ public int getMacKeySize()
+ {
+ return macKeySize;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/IESWithCipherParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/IESWithCipherParameters.java
new file mode 100644
index 00000000..ef61b2cc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/IESWithCipherParameters.java
@@ -0,0 +1,30 @@
+package org.bouncycastle.crypto.params;
+
+
+public class IESWithCipherParameters
+ extends IESParameters
+{
+ private int cipherKeySize;
+
+ /**
+ * @param derivation the derivation parameter for the KDF function.
+ * @param encoding the encoding parameter for the KDF function.
+ * @param macKeySize the size of the MAC key (in bits).
+ * @param cipherKeySize the size of the associated Cipher key (in bits).
+ */
+ public IESWithCipherParameters(
+ byte[] derivation,
+ byte[] encoding,
+ int macKeySize,
+ int cipherKeySize)
+ {
+ super(derivation, encoding, macKeySize);
+
+ this.cipherKeySize = cipherKeySize;
+ }
+
+ public int getCipherKeySize()
+ {
+ return cipherKeySize;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ISO18033KDFParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ISO18033KDFParameters.java
new file mode 100644
index 00000000..8dffe2ee
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/ISO18033KDFParameters.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.DerivationParameters;
+
+/**
+ * parameters for Key derivation functions for ISO-18033
+ */
+public class ISO18033KDFParameters
+ implements DerivationParameters
+{
+ byte[] seed;
+
+ public ISO18033KDFParameters(
+ byte[] seed)
+ {
+ this.seed = seed;
+ }
+
+ public byte[] getSeed()
+ {
+ return seed;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/KDFParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/KDFParameters.java
new file mode 100644
index 00000000..f3bac647
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/KDFParameters.java
@@ -0,0 +1,31 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.DerivationParameters;
+
+/**
+ * parameters for Key derivation functions for IEEE P1363a
+ */
+public class KDFParameters
+ implements DerivationParameters
+{
+ byte[] iv;
+ byte[] shared;
+
+ public KDFParameters(
+ byte[] shared,
+ byte[] iv)
+ {
+ this.shared = shared;
+ this.iv = iv;
+ }
+
+ public byte[] getSharedSecret()
+ {
+ return shared;
+ }
+
+ public byte[] getIV()
+ {
+ return iv;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/KeyParameter.java b/core/src/main/java/org/bouncycastle/crypto/params/KeyParameter.java
new file mode 100644
index 00000000..5c4fe0e0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/KeyParameter.java
@@ -0,0 +1,30 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public class KeyParameter
+ implements CipherParameters
+{
+ private byte[] key;
+
+ public KeyParameter(
+ byte[] key)
+ {
+ this(key, 0, key.length);
+ }
+
+ public KeyParameter(
+ byte[] key,
+ int keyOff,
+ int keyLen)
+ {
+ this.key = new byte[keyLen];
+
+ System.arraycopy(key, keyOff, this.key, 0, keyLen);
+ }
+
+ public byte[] getKey()
+ {
+ return key;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/MGFParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/MGFParameters.java
new file mode 100644
index 00000000..847bd989
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/MGFParameters.java
@@ -0,0 +1,32 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.DerivationParameters;
+
+/**
+ * parameters for mask derivation functions.
+ */
+public class MGFParameters
+ implements DerivationParameters
+{
+ byte[] seed;
+
+ public MGFParameters(
+ byte[] seed)
+ {
+ this(seed, 0, seed.length);
+ }
+
+ public MGFParameters(
+ byte[] seed,
+ int off,
+ int len)
+ {
+ this.seed = new byte[len];
+ System.arraycopy(seed, off, this.seed, 0, len);
+ }
+
+ public byte[] getSeed()
+ {
+ return seed;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/MQVPrivateParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/MQVPrivateParameters.java
new file mode 100644
index 00000000..832c07fd
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/MQVPrivateParameters.java
@@ -0,0 +1,43 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public class MQVPrivateParameters
+ implements CipherParameters
+{
+ private ECPrivateKeyParameters staticPrivateKey;
+ private ECPrivateKeyParameters ephemeralPrivateKey;
+ private ECPublicKeyParameters ephemeralPublicKey;
+
+ public MQVPrivateParameters(
+ ECPrivateKeyParameters staticPrivateKey,
+ ECPrivateKeyParameters ephemeralPrivateKey)
+ {
+ this(staticPrivateKey, ephemeralPrivateKey, null);
+ }
+
+ public MQVPrivateParameters(
+ ECPrivateKeyParameters staticPrivateKey,
+ ECPrivateKeyParameters ephemeralPrivateKey,
+ ECPublicKeyParameters ephemeralPublicKey)
+ {
+ this.staticPrivateKey = staticPrivateKey;
+ this.ephemeralPrivateKey = ephemeralPrivateKey;
+ this.ephemeralPublicKey = ephemeralPublicKey;
+ }
+
+ public ECPrivateKeyParameters getStaticPrivateKey()
+ {
+ return staticPrivateKey;
+ }
+
+ public ECPrivateKeyParameters getEphemeralPrivateKey()
+ {
+ return ephemeralPrivateKey;
+ }
+
+ public ECPublicKeyParameters getEphemeralPublicKey()
+ {
+ return ephemeralPublicKey;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/MQVPublicParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/MQVPublicParameters.java
new file mode 100644
index 00000000..b3b24671
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/MQVPublicParameters.java
@@ -0,0 +1,28 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public class MQVPublicParameters
+ implements CipherParameters
+{
+ private ECPublicKeyParameters staticPublicKey;
+ private ECPublicKeyParameters ephemeralPublicKey;
+
+ public MQVPublicParameters(
+ ECPublicKeyParameters staticPublicKey,
+ ECPublicKeyParameters ephemeralPublicKey)
+ {
+ this.staticPublicKey = staticPublicKey;
+ this.ephemeralPublicKey = ephemeralPublicKey;
+ }
+
+ public ECPublicKeyParameters getStaticPublicKey()
+ {
+ return staticPublicKey;
+ }
+
+ public ECPublicKeyParameters getEphemeralPublicKey()
+ {
+ return ephemeralPublicKey;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/NaccacheSternKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/NaccacheSternKeyGenerationParameters.java
new file mode 100644
index 00000000..758fcd79
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/NaccacheSternKeyGenerationParameters.java
@@ -0,0 +1,97 @@
+package org.bouncycastle.crypto.params;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.KeyGenerationParameters;
+
+/**
+ * Parameters for NaccacheStern public private key generation. For details on
+ * this cipher, please see
+ *
+ * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf
+ */
+public class NaccacheSternKeyGenerationParameters extends KeyGenerationParameters
+{
+
+ // private BigInteger publicExponent;
+ private int certainty;
+
+ private int cntSmallPrimes;
+
+ private boolean debug = false;
+
+ /**
+ * Parameters for generating a NaccacheStern KeyPair.
+ *
+ * @param random
+ * The source of randomness
+ * @param strength
+ * The desired strength of the Key in Bits
+ * @param certainty
+ * the probability that the generated primes are not really prime
+ * as integer: 2^(-certainty) is then the probability
+ * @param cntSmallPrimes
+ * How many small key factors are desired
+ */
+ public NaccacheSternKeyGenerationParameters(SecureRandom random, int strength, int certainty, int cntSmallPrimes)
+ {
+ this(random, strength, certainty, cntSmallPrimes, false);
+ }
+
+ /**
+ * Parameters for a NaccacheStern KeyPair.
+ *
+ * @param random
+ * The source of randomness
+ * @param strength
+ * The desired strength of the Key in Bits
+ * @param certainty
+ * the probability that the generated primes are not really prime
+ * as integer: 2^(-certainty) is then the probability
+ * @param cntSmallPrimes
+ * How many small key factors are desired
+ * @param debug
+ * Turn debugging on or off (reveals secret information, use with
+ * caution)
+ */
+ public NaccacheSternKeyGenerationParameters(SecureRandom random,
+ int strength, int certainty, int cntSmallPrimes, boolean debug)
+ {
+ super(random, strength);
+
+ this.certainty = certainty;
+ if (cntSmallPrimes % 2 == 1)
+ {
+ throw new IllegalArgumentException("cntSmallPrimes must be a multiple of 2");
+ }
+ if (cntSmallPrimes < 30)
+ {
+ throw new IllegalArgumentException("cntSmallPrimes must be >= 30 for security reasons");
+ }
+ this.cntSmallPrimes = cntSmallPrimes;
+
+ this.debug = debug;
+ }
+
+ /**
+ * @return Returns the certainty.
+ */
+ public int getCertainty()
+ {
+ return certainty;
+ }
+
+ /**
+ * @return Returns the cntSmallPrimes.
+ */
+ public int getCntSmallPrimes()
+ {
+ return cntSmallPrimes;
+ }
+
+ public boolean isDebug()
+ {
+ return debug;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/NaccacheSternKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/NaccacheSternKeyParameters.java
new file mode 100644
index 00000000..21b6a286
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/NaccacheSternKeyParameters.java
@@ -0,0 +1,53 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+
+/**
+ * Public key parameters for NaccacheStern cipher. For details on this cipher,
+ * please see
+ *
+ * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf
+ */
+public class NaccacheSternKeyParameters extends AsymmetricKeyParameter
+{
+
+ private BigInteger g, n;
+
+ int lowerSigmaBound;
+
+ /**
+ * @param privateKey
+ */
+ public NaccacheSternKeyParameters(boolean privateKey, BigInteger g, BigInteger n, int lowerSigmaBound)
+ {
+ super(privateKey);
+ this.g = g;
+ this.n = n;
+ this.lowerSigmaBound = lowerSigmaBound;
+ }
+
+ /**
+ * @return Returns the g.
+ */
+ public BigInteger getG()
+ {
+ return g;
+ }
+
+ /**
+ * @return Returns the lowerSigmaBound.
+ */
+ public int getLowerSigmaBound()
+ {
+ return lowerSigmaBound;
+ }
+
+ /**
+ * @return Returns the n.
+ */
+ public BigInteger getModulus()
+ {
+ return n;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/NaccacheSternPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/NaccacheSternPrivateKeyParameters.java
new file mode 100644
index 00000000..6d0ec486
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/NaccacheSternPrivateKeyParameters.java
@@ -0,0 +1,50 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+import java.util.Vector;
+
+/**
+ * Private key parameters for NaccacheStern cipher. For details on this cipher,
+ * please see
+ *
+ * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf
+ */
+public class NaccacheSternPrivateKeyParameters extends NaccacheSternKeyParameters
+{
+ private BigInteger phi_n;
+ private Vector smallPrimes;
+
+ /**
+ * Constructs a NaccacheSternPrivateKey
+ *
+ * @param g
+ * the public enryption parameter g
+ * @param n
+ * the public modulus n = p*q
+ * @param lowerSigmaBound
+ * the public lower sigma bound up to which data can be encrypted
+ * @param smallPrimes
+ * the small primes, of which sigma is constructed in the right
+ * order
+ * @param phi_n
+ * the private modulus phi(n) = (p-1)(q-1)
+ */
+ public NaccacheSternPrivateKeyParameters(BigInteger g, BigInteger n,
+ int lowerSigmaBound, Vector smallPrimes,
+ BigInteger phi_n)
+ {
+ super(true, g, n, lowerSigmaBound);
+ this.smallPrimes = smallPrimes;
+ this.phi_n = phi_n;
+ }
+
+ public BigInteger getPhi_n()
+ {
+ return phi_n;
+ }
+
+ public Vector getSmallPrimes()
+ {
+ return smallPrimes;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ParametersWithIV.java b/core/src/main/java/org/bouncycastle/crypto/params/ParametersWithIV.java
new file mode 100644
index 00000000..4a1e6e9a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/ParametersWithIV.java
@@ -0,0 +1,39 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public class ParametersWithIV
+ implements CipherParameters
+{
+ private byte[] iv;
+ private CipherParameters parameters;
+
+ public ParametersWithIV(
+ CipherParameters parameters,
+ byte[] iv)
+ {
+ this(parameters, iv, 0, iv.length);
+ }
+
+ public ParametersWithIV(
+ CipherParameters parameters,
+ byte[] iv,
+ int ivOff,
+ int ivLen)
+ {
+ this.iv = new byte[ivLen];
+ this.parameters = parameters;
+
+ System.arraycopy(iv, ivOff, this.iv, 0, ivLen);
+ }
+
+ public byte[] getIV()
+ {
+ return iv;
+ }
+
+ public CipherParameters getParameters()
+ {
+ return parameters;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ParametersWithRandom.java b/core/src/main/java/org/bouncycastle/crypto/params/ParametersWithRandom.java
new file mode 100644
index 00000000..a7b18e51
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/ParametersWithRandom.java
@@ -0,0 +1,36 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+import java.security.SecureRandom;
+
+public class ParametersWithRandom
+ implements CipherParameters
+{
+ private SecureRandom random;
+ private CipherParameters parameters;
+
+ public ParametersWithRandom(
+ CipherParameters parameters,
+ SecureRandom random)
+ {
+ this.random = random;
+ this.parameters = parameters;
+ }
+
+ public ParametersWithRandom(
+ CipherParameters parameters)
+ {
+ this(parameters, new SecureRandom());
+ }
+
+ public SecureRandom getRandom()
+ {
+ return random;
+ }
+
+ public CipherParameters getParameters()
+ {
+ return parameters;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ParametersWithSBox.java b/core/src/main/java/org/bouncycastle/crypto/params/ParametersWithSBox.java
new file mode 100644
index 00000000..b226a9d9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/ParametersWithSBox.java
@@ -0,0 +1,28 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public class ParametersWithSBox
+ implements CipherParameters
+{
+ private CipherParameters parameters;
+ private byte[] sBox;
+
+ public ParametersWithSBox(
+ CipherParameters parameters,
+ byte[] sBox)
+ {
+ this.parameters = parameters;
+ this.sBox = sBox;
+ }
+
+ public byte[] getSBox()
+ {
+ return sBox;
+ }
+
+ public CipherParameters getParameters()
+ {
+ return parameters;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ParametersWithSalt.java b/core/src/main/java/org/bouncycastle/crypto/params/ParametersWithSalt.java
new file mode 100644
index 00000000..73765dd5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/ParametersWithSalt.java
@@ -0,0 +1,42 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+/**
+ * Cipher parameters with a fixed salt value associated with them.
+ */
+public class ParametersWithSalt
+ implements CipherParameters
+{
+ private byte[] salt;
+ private CipherParameters parameters;
+
+ public ParametersWithSalt(
+ CipherParameters parameters,
+ byte[] salt)
+ {
+ this(parameters, salt, 0, salt.length);
+ }
+
+ public ParametersWithSalt(
+ CipherParameters parameters,
+ byte[] salt,
+ int saltOff,
+ int saltLen)
+ {
+ this.salt = new byte[saltLen];
+ this.parameters = parameters;
+
+ System.arraycopy(salt, saltOff, this.salt, 0, saltLen);
+ }
+
+ public byte[] getSalt()
+ {
+ return salt;
+ }
+
+ public CipherParameters getParameters()
+ {
+ return parameters;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/RC2Parameters.java b/core/src/main/java/org/bouncycastle/crypto/params/RC2Parameters.java
new file mode 100644
index 00000000..dc33ec5b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/RC2Parameters.java
@@ -0,0 +1,36 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public class RC2Parameters
+ implements CipherParameters
+{
+ private byte[] key;
+ private int bits;
+
+ public RC2Parameters(
+ byte[] key)
+ {
+ this(key, (key.length > 128) ? 1024 : (key.length * 8));
+ }
+
+ public RC2Parameters(
+ byte[] key,
+ int bits)
+ {
+ this.key = new byte[key.length];
+ this.bits = bits;
+
+ System.arraycopy(key, 0, this.key, 0, key.length);
+ }
+
+ public byte[] getKey()
+ {
+ return key;
+ }
+
+ public int getEffectiveKeyBits()
+ {
+ return bits;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/RC5Parameters.java b/core/src/main/java/org/bouncycastle/crypto/params/RC5Parameters.java
new file mode 100644
index 00000000..6cbd57f1
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/RC5Parameters.java
@@ -0,0 +1,35 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public class RC5Parameters
+ implements CipherParameters
+{
+ private byte[] key;
+ private int rounds;
+
+ public RC5Parameters(
+ byte[] key,
+ int rounds)
+ {
+ if (key.length > 255)
+ {
+ throw new IllegalArgumentException("RC5 key length can be no greater than 255");
+ }
+
+ this.key = new byte[key.length];
+ this.rounds = rounds;
+
+ System.arraycopy(key, 0, this.key, 0, key.length);
+ }
+
+ public byte[] getKey()
+ {
+ return key;
+ }
+
+ public int getRounds()
+ {
+ return rounds;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/RSABlindingParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/RSABlindingParameters.java
new file mode 100644
index 00000000..c7fa6ba6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/RSABlindingParameters.java
@@ -0,0 +1,35 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+import java.math.BigInteger;
+
+public class RSABlindingParameters
+ implements CipherParameters
+{
+ private RSAKeyParameters publicKey;
+ private BigInteger blindingFactor;
+
+ public RSABlindingParameters(
+ RSAKeyParameters publicKey,
+ BigInteger blindingFactor)
+ {
+ if (publicKey instanceof RSAPrivateCrtKeyParameters)
+ {
+ throw new IllegalArgumentException("RSA parameters should be for a public key");
+ }
+
+ this.publicKey = publicKey;
+ this.blindingFactor = blindingFactor;
+ }
+
+ public RSAKeyParameters getPublicKey()
+ {
+ return publicKey;
+ }
+
+ public BigInteger getBlindingFactor()
+ {
+ return blindingFactor;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/RSAKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/RSAKeyGenerationParameters.java
new file mode 100644
index 00000000..38b55fcd
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/RSAKeyGenerationParameters.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.KeyGenerationParameters;
+
+public class RSAKeyGenerationParameters
+ extends KeyGenerationParameters
+{
+ private BigInteger publicExponent;
+ private int certainty;
+
+ public RSAKeyGenerationParameters(
+ BigInteger publicExponent,
+ SecureRandom random,
+ int strength,
+ int certainty)
+ {
+ super(random, strength);
+
+ if (strength < 12)
+ {
+ throw new IllegalArgumentException("key strength too small");
+ }
+
+ //
+ // public exponent cannot be even
+ //
+ if (!publicExponent.testBit(0))
+ {
+ throw new IllegalArgumentException("public exponent cannot be even");
+ }
+
+ this.publicExponent = publicExponent;
+ this.certainty = certainty;
+ }
+
+ public BigInteger getPublicExponent()
+ {
+ return publicExponent;
+ }
+
+ public int getCertainty()
+ {
+ return certainty;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/RSAKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/RSAKeyParameters.java
new file mode 100644
index 00000000..4a2d9354
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/RSAKeyParameters.java
@@ -0,0 +1,31 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class RSAKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private BigInteger modulus;
+ private BigInteger exponent;
+
+ public RSAKeyParameters(
+ boolean isPrivate,
+ BigInteger modulus,
+ BigInteger exponent)
+ {
+ super(isPrivate);
+
+ this.modulus = modulus;
+ this.exponent = exponent;
+ }
+
+ public BigInteger getModulus()
+ {
+ return modulus;
+ }
+
+ public BigInteger getExponent()
+ {
+ return exponent;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java
new file mode 100644
index 00000000..b61cb5c4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java
@@ -0,0 +1,67 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class RSAPrivateCrtKeyParameters
+ extends RSAKeyParameters
+{
+ private BigInteger e;
+ private BigInteger p;
+ private BigInteger q;
+ private BigInteger dP;
+ private BigInteger dQ;
+ private BigInteger qInv;
+
+ /**
+ *
+ */
+ public RSAPrivateCrtKeyParameters(
+ BigInteger modulus,
+ BigInteger publicExponent,
+ BigInteger privateExponent,
+ BigInteger p,
+ BigInteger q,
+ BigInteger dP,
+ BigInteger dQ,
+ BigInteger qInv)
+ {
+ super(true, modulus, privateExponent);
+
+ this.e = publicExponent;
+ this.p = p;
+ this.q = q;
+ this.dP = dP;
+ this.dQ = dQ;
+ this.qInv = qInv;
+ }
+
+ public BigInteger getPublicExponent()
+ {
+ return e;
+ }
+
+ public BigInteger getP()
+ {
+ return p;
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public BigInteger getDP()
+ {
+ return dP;
+ }
+
+ public BigInteger getDQ()
+ {
+ return dQ;
+ }
+
+ public BigInteger getQInv()
+ {
+ return qInv;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/parsers/DHIESPublicKeyParser.java b/core/src/main/java/org/bouncycastle/crypto/parsers/DHIESPublicKeyParser.java
new file mode 100644
index 00000000..44f5b571
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/parsers/DHIESPublicKeyParser.java
@@ -0,0 +1,31 @@
+package org.bouncycastle.crypto.parsers;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.KeyParser;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+
+public class DHIESPublicKeyParser
+ implements KeyParser
+{
+ private DHParameters dhParams;
+
+ public DHIESPublicKeyParser(DHParameters dhParams)
+ {
+ this.dhParams = dhParams;
+ }
+
+ public AsymmetricKeyParameter readKey(InputStream stream)
+ throws IOException
+ {
+ byte[] V = new byte[(dhParams.getP().bitLength() + 7) / 8];
+
+ stream.read(V, 0, V.length);
+
+ return new DHPublicKeyParameters(new BigInteger(1, V), dhParams);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/parsers/ECIESPublicKeyParser.java b/core/src/main/java/org/bouncycastle/crypto/parsers/ECIESPublicKeyParser.java
new file mode 100644
index 00000000..1880a506
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/parsers/ECIESPublicKeyParser.java
@@ -0,0 +1,53 @@
+package org.bouncycastle.crypto.parsers;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.crypto.KeyParser;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+
+public class ECIESPublicKeyParser
+ implements KeyParser
+{
+ private ECDomainParameters ecParams;
+
+ public ECIESPublicKeyParser(ECDomainParameters ecParams)
+ {
+ this.ecParams = ecParams;
+ }
+
+ public AsymmetricKeyParameter readKey(InputStream stream)
+ throws IOException
+ {
+ byte[] V;
+ int first = stream.read();
+
+ // Decode the public ephemeral key
+ switch (first)
+ {
+ case 0x00: // infinity
+ throw new IOException("Sender's public key invalid.");
+
+ case 0x02: // compressed
+ case 0x03: // Byte length calculated as in ECPoint.getEncoded();
+ V = new byte[1 + (ecParams.getCurve().getFieldSize()+7)/8];
+ break;
+
+ case 0x04: // uncompressed or
+ case 0x06: // hybrid
+ case 0x07: // Byte length calculated as in ECPoint.getEncoded();
+ V = new byte[1 + 2*((ecParams.getCurve().getFieldSize()+7)/8)];
+ break;
+
+ default:
+ throw new IOException("Sender's public key has invalid point encoding 0x" + Integer.toString(first, 16));
+ }
+
+ V[0] = (byte)first;
+ stream.read(V, 1, V.length - 1);
+
+ return new ECPublicKeyParameters(ecParams.getCurve().decodePoint(V), ecParams);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/prng/BasicEntropySourceProvider.java b/core/src/main/java/org/bouncycastle/crypto/prng/BasicEntropySourceProvider.java
new file mode 100644
index 00000000..9f1d0427
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/prng/BasicEntropySourceProvider.java
@@ -0,0 +1,53 @@
+package org.bouncycastle.crypto.prng;
+
+import java.security.SecureRandom;
+
+/**
+ * An EntropySourceProvider where entropy generation is based on a SecureRandom output using SecureRandom.generateSeed().
+ */
+public class BasicEntropySourceProvider
+ implements EntropySourceProvider
+{
+ private final SecureRandom _sr;
+ private final boolean _predictionResistant;
+
+ /**
+ * Create a entropy source provider based on the passed in SecureRandom.
+ *
+ * @param random the SecureRandom to base EntropySource construction on.
+ * @param isPredictionResistant boolean indicating if the SecureRandom is based on prediction resistant entropy or not (true if it is).
+ */
+ public BasicEntropySourceProvider(SecureRandom random, boolean isPredictionResistant)
+ {
+ _sr = random;
+ _predictionResistant = isPredictionResistant;
+ }
+
+ /**
+ * Return an entropy source that will create bitsRequired bits of entropy on
+ * each invocation of getEntropy().
+ *
+ * @param bitsRequired size (in bits) of entropy to be created by the provided source.
+ * @return an EntropySource that generates bitsRequired bits of entropy on each call to its getEntropy() method.
+ */
+ public EntropySource get(final int bitsRequired)
+ {
+ return new EntropySource()
+ {
+ public boolean isPredictionResistant()
+ {
+ return _predictionResistant;
+ }
+
+ public byte[] getEntropy()
+ {
+ return _sr.generateSeed((bitsRequired + 7) / 8);
+ }
+
+ public int entropySize()
+ {
+ return bitsRequired;
+ }
+ };
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/prng/DRBGProvider.java b/core/src/main/java/org/bouncycastle/crypto/prng/DRBGProvider.java
new file mode 100644
index 00000000..c39760c9
--- /dev/null
+++ b/core/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/core/src/main/java/org/bouncycastle/crypto/prng/DigestRandomGenerator.java b/core/src/main/java/org/bouncycastle/crypto/prng/DigestRandomGenerator.java
new file mode 100644
index 00000000..f36b62c2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/prng/DigestRandomGenerator.java
@@ -0,0 +1,123 @@
+package org.bouncycastle.crypto.prng;
+
+import org.bouncycastle.crypto.Digest;
+
+/**
+ * Random generation based on the digest with counter. Calling addSeedMaterial will
+ * always increase the entropy of the hash.
+ * <p>
+ * Internal access to the digest is synchronized so a single one of these can be shared.
+ * </p>
+ */
+public class DigestRandomGenerator
+ implements RandomGenerator
+{
+ private static long CYCLE_COUNT = 10;
+
+ private long stateCounter;
+ private long seedCounter;
+ private Digest digest;
+ private byte[] state;
+ private byte[] seed;
+
+ // public constructors
+ public DigestRandomGenerator(
+ Digest digest)
+ {
+ this.digest = digest;
+
+ this.seed = new byte[digest.getDigestSize()];
+ this.seedCounter = 1;
+
+ this.state = new byte[digest.getDigestSize()];
+ this.stateCounter = 1;
+ }
+
+ public void addSeedMaterial(byte[] inSeed)
+ {
+ synchronized (this)
+ {
+ digestUpdate(inSeed);
+ digestUpdate(seed);
+ digestDoFinal(seed);
+ }
+ }
+
+ public void addSeedMaterial(long rSeed)
+ {
+ synchronized (this)
+ {
+ digestAddCounter(rSeed);
+ digestUpdate(seed);
+
+ digestDoFinal(seed);
+ }
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ nextBytes(bytes, 0, bytes.length);
+ }
+
+ public void nextBytes(byte[] bytes, int start, int len)
+ {
+ synchronized (this)
+ {
+ int stateOff = 0;
+
+ generateState();
+
+ int end = start + len;
+ for (int i = start; i != end; i++)
+ {
+ if (stateOff == state.length)
+ {
+ generateState();
+ stateOff = 0;
+ }
+ bytes[i] = state[stateOff++];
+ }
+ }
+ }
+
+ private void cycleSeed()
+ {
+ digestUpdate(seed);
+ digestAddCounter(seedCounter++);
+
+ digestDoFinal(seed);
+ }
+
+ private void generateState()
+ {
+ digestAddCounter(stateCounter++);
+ digestUpdate(state);
+ digestUpdate(seed);
+
+ digestDoFinal(state);
+
+ if ((stateCounter % CYCLE_COUNT) == 0)
+ {
+ cycleSeed();
+ }
+ }
+
+ private void digestAddCounter(long seed)
+ {
+ for (int i = 0; i != 8; i++)
+ {
+ digest.update((byte)seed);
+ seed >>>= 8;
+ }
+ }
+
+ private void digestUpdate(byte[] inSeed)
+ {
+ digest.update(inSeed, 0, inSeed.length);
+ }
+
+ private void digestDoFinal(byte[] result)
+ {
+ digest.doFinal(result, 0);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/prng/EntropySource.java b/core/src/main/java/org/bouncycastle/crypto/prng/EntropySource.java
new file mode 100644
index 00000000..53bc549d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/prng/EntropySource.java
@@ -0,0 +1,25 @@
+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/core/src/main/java/org/bouncycastle/crypto/prng/EntropySourceProvider.java b/core/src/main/java/org/bouncycastle/crypto/prng/EntropySourceProvider.java
new file mode 100644
index 00000000..190bf624
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/prng/EntropySourceProvider.java
@@ -0,0 +1,6 @@
+package org.bouncycastle.crypto.prng;
+
+public interface EntropySourceProvider
+{
+ EntropySource get(final int bitsRequired);
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/prng/FixedSecureRandom.java b/core/src/main/java/org/bouncycastle/crypto/prng/FixedSecureRandom.java
new file mode 100644
index 00000000..209b5e21
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/prng/FixedSecureRandom.java
@@ -0,0 +1,135 @@
+package org.bouncycastle.crypto.prng;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.SecureRandom;
+
+public class FixedSecureRandom
+ extends SecureRandom
+{
+ private byte[] _data;
+
+ private int _index;
+ private int _intPad;
+
+ public FixedSecureRandom(byte[] value)
+ {
+ this(false, new byte[][] { value });
+ }
+
+ public FixedSecureRandom(
+ byte[][] values)
+ {
+ this(false, values);
+ }
+
+ /**
+ * Pad the data on integer boundaries. This is necessary for the classpath project's BigInteger
+ * implementation.
+ */
+ public FixedSecureRandom(
+ boolean intPad,
+ byte[] value)
+ {
+ this(intPad, new byte[][] { value });
+ }
+
+ /**
+ * Pad the data on integer boundaries. This is necessary for the classpath project's BigInteger
+ * implementation.
+ */
+ public FixedSecureRandom(
+ boolean intPad,
+ byte[][] values)
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ for (int i = 0; i != values.length; i++)
+ {
+ try
+ {
+ bOut.write(values[i]);
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("can't save value array.");
+ }
+ }
+
+ _data = bOut.toByteArray();
+
+ if (intPad)
+ {
+ _intPad = _data.length % 4;
+ }
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ System.arraycopy(_data, _index, bytes, 0, bytes.length);
+
+ _index += bytes.length;
+ }
+
+ //
+ // classpath's implementation of SecureRandom doesn't currently go back to nextBytes
+ // when next is called. We can't override next as it's a final method.
+ //
+ public int nextInt()
+ {
+ int val = 0;
+
+ val |= nextValue() << 24;
+ val |= nextValue() << 16;
+
+ if (_intPad == 2)
+ {
+ _intPad--;
+ }
+ else
+ {
+ val |= nextValue() << 8;
+ }
+
+ if (_intPad == 1)
+ {
+ _intPad--;
+ }
+ else
+ {
+ val |= nextValue();
+ }
+
+ return val;
+ }
+
+ //
+ // classpath's implementation of SecureRandom doesn't currently go back to nextBytes
+ // when next is called. We can't override next as it's a final method.
+ //
+ public long nextLong()
+ {
+ long val = 0;
+
+ val |= (long)nextValue() << 56;
+ val |= (long)nextValue() << 48;
+ val |= (long)nextValue() << 40;
+ val |= (long)nextValue() << 32;
+ val |= (long)nextValue() << 24;
+ val |= (long)nextValue() << 16;
+ val |= (long)nextValue() << 8;
+ val |= (long)nextValue();
+
+ return val;
+ }
+
+ public boolean isExhausted()
+ {
+ return _index == _data.length;
+ }
+
+ private int nextValue()
+ {
+ return _data[_index++] & 0xff;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/prng/RandomGenerator.java b/core/src/main/java/org/bouncycastle/crypto/prng/RandomGenerator.java
new file mode 100644
index 00000000..47ff68e3
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/prng/RandomGenerator.java
@@ -0,0 +1,38 @@
+package org.bouncycastle.crypto.prng;
+
+/**
+ * Generic interface for objects generating random bytes.
+ */
+public interface RandomGenerator
+{
+ /**
+ * Add more seed material to the generator.
+ *
+ * @param seed a byte array to be mixed into the generator's state.
+ */
+ void addSeedMaterial(byte[] seed);
+
+ /**
+ * Add more seed material to the generator.
+ *
+ * @param seed a long value to be mixed into the generator's state.
+ */
+ void addSeedMaterial(long seed);
+
+ /**
+ * Fill bytes with random values.
+ *
+ * @param bytes byte array to be filled.
+ */
+ void nextBytes(byte[] bytes);
+
+ /**
+ * Fill part of bytes with random values.
+ *
+ * @param bytes byte array to be filled.
+ * @param start index to start filling at.
+ * @param len length of segment to fill.
+ */
+ void nextBytes(byte[] bytes, int start, int len);
+
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/prng/ReversedWindowGenerator.java b/core/src/main/java/org/bouncycastle/crypto/prng/ReversedWindowGenerator.java
new file mode 100644
index 00000000..fbb2639c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/prng/ReversedWindowGenerator.java
@@ -0,0 +1,111 @@
+package org.bouncycastle.crypto.prng;
+
+/**
+ * Takes bytes generated by an underling RandomGenerator and reverses the order in
+ * each small window (of configurable size).
+ * <p>
+ * Access to internals is synchronized so a single one of these can be shared.
+ * </p>
+ */
+public class ReversedWindowGenerator
+ implements RandomGenerator
+{
+ private final RandomGenerator generator;
+
+ private byte[] window;
+ private int windowCount;
+
+ public ReversedWindowGenerator(
+ RandomGenerator generator,
+ int windowSize)
+ {
+ if (generator == null)
+ {
+ throw new IllegalArgumentException("generator cannot be null");
+ }
+ if (windowSize < 2)
+ {
+ throw new IllegalArgumentException("windowSize must be at least 2");
+ }
+
+ this.generator = generator;
+ this.window = new byte[windowSize];
+ }
+
+ /**
+ * Add more seed material to the generator.
+ *
+ * @param seed a byte array to be mixed into the generator's state.
+ */
+ public void addSeedMaterial(
+ byte[] seed)
+ {
+ synchronized (this)
+ {
+ windowCount = 0;
+ generator.addSeedMaterial(seed);
+ }
+ }
+
+ /**
+ * Add more seed material to the generator.
+ *
+ * @param seed a long value to be mixed into the generator's state.
+ */
+ public void addSeedMaterial(
+ long seed)
+ {
+ synchronized (this)
+ {
+ windowCount = 0;
+ generator.addSeedMaterial(seed);
+ }
+ }
+
+ /**
+ * Fill bytes with random values.
+ *
+ * @param bytes byte array to be filled.
+ */
+ public void nextBytes(
+ byte[] bytes)
+ {
+ doNextBytes(bytes, 0, bytes.length);
+ }
+
+ /**
+ * Fill part of bytes with random values.
+ *
+ * @param bytes byte array to be filled.
+ * @param start index to start filling at.
+ * @param len length of segment to fill.
+ */
+ public void nextBytes(
+ byte[] bytes,
+ int start,
+ int len)
+ {
+ doNextBytes(bytes, start, len);
+ }
+
+ private void doNextBytes(
+ byte[] bytes,
+ int start,
+ int len)
+ {
+ synchronized (this)
+ {
+ int done = 0;
+ while (done < len)
+ {
+ if (windowCount < 1)
+ {
+ generator.nextBytes(window, 0, window.length);
+ windowCount = window.length;
+ }
+
+ bytes[start + done++] = window[--windowCount];
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandom.java b/core/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandom.java
new file mode 100644
index 00000000..e1ec6c28
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandom.java
@@ -0,0 +1,74 @@
+package org.bouncycastle.crypto.prng;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.prng.drbg.SP80090DRBG;
+
+public class SP800SecureRandom
+ extends SecureRandom
+{
+ private final DRBGProvider drbgProvider;
+ private final boolean predictionResistant;
+ private final SecureRandom randomSource;
+ private final EntropySource entropySource;
+
+ private SP80090DRBG drbg;
+
+ SP800SecureRandom(SecureRandom randomSource, EntropySource entropySource, DRBGProvider drbgProvider, boolean predictionResistant)
+ {
+ this.randomSource = randomSource;
+ this.entropySource = entropySource;
+ this.drbgProvider = drbgProvider;
+ this.predictionResistant = predictionResistant;
+ }
+
+ public void setSeed(byte[] seed)
+ {
+ synchronized (this)
+ {
+ if (randomSource != null)
+ {
+ this.randomSource.setSeed(seed);
+ }
+ }
+ }
+
+ public void setSeed(long seed)
+ {
+ synchronized (this)
+ {
+ // this will happen when SecureRandom() is created
+ if (randomSource != null)
+ {
+ this.randomSource.setSeed(seed);
+ }
+ }
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ synchronized (this)
+ {
+ if (drbg == null)
+ {
+ drbg = drbgProvider.get(entropySource);
+ }
+
+ // check if a reseed is required...
+ if (drbg.generate(bytes, null, predictionResistant) < 0)
+ {
+ drbg.reseed(entropySource.getEntropy());
+ drbg.generate(bytes, null, predictionResistant);
+ }
+ }
+ }
+
+ public byte[] generateSeed(int numBytes)
+ {
+ byte[] bytes = new byte[numBytes];
+
+ this.nextBytes(bytes);
+
+ return bytes;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java b/core/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java
new file mode 100644
index 00000000..66f05c5f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java
@@ -0,0 +1,249 @@
+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.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).
+ */
+public class SP800SecureRandomBuilder
+{
+ private final SecureRandom random;
+ private final EntropySourceProvider entropySourceProvider;
+
+ private byte[] personalizationString;
+ private int securityStrength = 256;
+ private int entropyBitsRequired = 256;
+
+ /**
+ * Basic constructor, creates a builder using an EntropySourceProvider based on the default SecureRandom with
+ * 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 default SecureRandom does for its generateSeed() call.
+ * </p>
+ */
+ public SP800SecureRandomBuilder()
+ {
+ this(new SecureRandom(), false);
+ }
+
+ /**
+ * Construct a builder with an EntropySourceProvider based on the passed in SecureRandom and the passed in value
+ * 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 for its generateSeed() call.
+ * </p>
+ * @param entropySource
+ * @param predictionResistant
+ */
+ public SP800SecureRandomBuilder(SecureRandom entropySource, boolean predictionResistant)
+ {
+ this.random = entropySource;
+ this.entropySourceProvider = new BasicEntropySourceProvider(random, predictionResistant);
+ }
+
+ /**
+ * Create a builder which makes creates the SecureRandom objects from a specified entropy source provider.
+ * <p>
+ * <b>Note:</b> If this constructor is used any calls to setSeed() in the resulting SecureRandom will be ignored.
+ * </p>
+ * @param entropySourceProvider a provider of EntropySource objects.
+ */
+ public SP800SecureRandomBuilder(EntropySourceProvider entropySourceProvider)
+ {
+ this.random = null;
+ this.entropySourceProvider = entropySourceProvider;
+ }
+
+ /**
+ * Set the personalization string for DRBG SecureRandoms created by this builder
+ * @param personalizationString the personalisation string for the underlying DRBG.
+ * @return the current builder.
+ */
+ public SP800SecureRandomBuilder setPersonalizationString(byte[] personalizationString)
+ {
+ this.personalizationString = personalizationString;
+
+ return this;
+ }
+
+ /**
+ * Set the security strength required for DRBGs used in building SecureRandom objects.
+ *
+ * @param securityStrength the security strength (in bits)
+ * @return the current builder.
+ */
+ public SP800SecureRandomBuilder setSecurityStrength(int securityStrength)
+ {
+ this.securityStrength = securityStrength;
+
+ return this;
+ }
+
+ /**
+ * Set the amount of entropy bits required for seeding and reseeding DRBGs used in building SecureRandom objects.
+ *
+ * @param entropyBitsRequired the number of bits of entropy to be requested from the entropy source on each seed/reseed.
+ * @return the current builder.
+ */
+ public SP800SecureRandomBuilder setEntropyBitsRequired(int entropyBitsRequired)
+ {
+ this.entropyBitsRequired = entropyBitsRequired;
+
+ return this;
+ }
+
+ /**
+ * Build a SecureRandom based on a SP 800-90A Hash DRBG.
+ *
+ * @param digest digest algorithm to use in the DRBG underneath the SecureRandom.
+ * @param nonce nonce value to use in DRBG construction.
+ * @param predictionResistant specify whether the underlying DRBG in the resulting SecureRandom should reseed on each request for bytes.
+ * @return a SecureRandom supported by a Hash DRBG.
+ */
+ public SP800SecureRandom buildHash(Digest digest, byte[] nonce, boolean predictionResistant)
+ {
+ return new SP800SecureRandom(random, entropySourceProvider.get(entropyBitsRequired), new HashDRBGProvider(digest, nonce, personalizationString, securityStrength), predictionResistant);
+ }
+
+ /**
+ * Build a SecureRandom based on a SP 800-90A CTR DRBG.
+ *
+ * @param cipher the block cipher to base the DRBG on.
+ * @param keySizeInBits key size in bits to be used with the block cipher.
+ * @param nonce nonce value to use in DRBG construction.
+ * @param predictionResistant specify whether the underlying DRBG in the resulting SecureRandom should reseed on each request for bytes.
+ * @return a SecureRandom supported by a CTR DRBG.
+ */
+ public SP800SecureRandom buildCTR(BlockCipher cipher, int keySizeInBits, byte[] nonce, boolean predictionResistant)
+ {
+ return new SP800SecureRandom(random, entropySourceProvider.get(entropyBitsRequired), new CTRDRBGProvider(cipher, keySizeInBits, nonce, personalizationString, securityStrength), predictionResistant);
+ }
+
+ /**
+ * Build a SecureRandom based on a SP 800-90A HMAC DRBG.
+ *
+ * @param hMac HMAC algorithm to use in the DRBG underneath the SecureRandom.
+ * @param nonce nonce value to use in DRBG construction.
+ * @param predictionResistant specify whether the underlying DRBG in the resulting SecureRandom should reseed on each request for bytes.
+ * @return a SecureRandom supported by a HMAC DRBG.
+ */
+ public SP800SecureRandom buildHMAC(Mac hMac, byte[] nonce, boolean predictionResistant)
+ {
+ return new SP800SecureRandom(random, entropySourceProvider.get(entropyBitsRequired), new HMacDRBGProvider(hMac, nonce, personalizationString, securityStrength), predictionResistant);
+ }
+
+ /**
+ * Build a SecureRandom based on a SP 800-90A Dual EC DRBG.
+ *
+ * @param digest digest algorithm to use in the DRBG underneath the SecureRandom.
+ * @param nonce nonce value to use in DRBG construction.
+ * @param predictionResistant specify whether the underlying DRBG in the resulting SecureRandom should reseed on each request for bytes.
+ * @return a SecureRandom supported by a Dual EC DRBG.
+ */
+ public SP800SecureRandom buildDualEC(Digest digest, byte[] nonce, boolean predictionResistant)
+ {
+ return new SP800SecureRandom(random, entropySourceProvider.get(entropyBitsRequired), new DualECDRBGProvider(digest, nonce, personalizationString, securityStrength), predictionResistant);
+ }
+
+ private static class HashDRBGProvider
+ implements DRBGProvider
+ {
+ private final Digest digest;
+ private final byte[] nonce;
+ private final byte[] personalizationString;
+ private final int securityStrength;
+
+ public HashDRBGProvider(Digest digest, byte[] nonce, byte[] personalizationString, int securityStrength)
+ {
+ this.digest = digest;
+ this.nonce = nonce;
+ this.personalizationString = personalizationString;
+ this.securityStrength = securityStrength;
+ }
+
+ public SP80090DRBG get(EntropySource entropySource)
+ {
+ return new HashSP800DRBG(digest, securityStrength, entropySource, personalizationString, nonce);
+ }
+ }
+
+ private static class DualECDRBGProvider
+ implements DRBGProvider
+ {
+ private final Digest digest;
+ private final byte[] nonce;
+ private final byte[] personalizationString;
+ private final int securityStrength;
+
+ public DualECDRBGProvider(Digest digest, byte[] nonce, byte[] personalizationString, int securityStrength)
+ {
+ this.digest = digest;
+ this.nonce = nonce;
+ this.personalizationString = personalizationString;
+ this.securityStrength = securityStrength;
+ }
+
+ public SP80090DRBG get(EntropySource entropySource)
+ {
+ return new DualECSP800DRBG(digest, securityStrength, entropySource, personalizationString, nonce);
+ }
+ }
+
+ private static class HMacDRBGProvider
+ implements DRBGProvider
+ {
+ private final Mac hMac;
+ private final byte[] nonce;
+ private final byte[] personalizationString;
+ private final int securityStrength;
+
+ public HMacDRBGProvider(Mac hMac, byte[] nonce, byte[] personalizationString, int securityStrength)
+ {
+ this.hMac = hMac;
+ this.nonce = nonce;
+ this.personalizationString = personalizationString;
+ this.securityStrength = securityStrength;
+ }
+
+ public SP80090DRBG get(EntropySource entropySource)
+ {
+ return new HMacSP800DRBG(hMac, securityStrength, entropySource, personalizationString, nonce);
+ }
+ }
+
+ private static class CTRDRBGProvider
+ implements DRBGProvider
+ {
+
+ private final BlockCipher blockCipher;
+ private final int keySizeInBits;
+ private final byte[] nonce;
+ private final byte[] personalizationString;
+ private final int securityStrength;
+
+ public CTRDRBGProvider(BlockCipher blockCipher, int keySizeInBits, byte[] nonce, byte[] personalizationString, int securityStrength)
+ {
+ this.blockCipher = blockCipher;
+ this.keySizeInBits = keySizeInBits;
+ this.nonce = nonce;
+ this.personalizationString = personalizationString;
+ this.securityStrength = securityStrength;
+ }
+
+ public SP80090DRBG get(EntropySource entropySource)
+ {
+ return new CTRSP800DRBG(blockCipher, keySizeInBits, securityStrength, entropySource, personalizationString, nonce);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/prng/ThreadedSeedGenerator.java b/core/src/main/java/org/bouncycastle/crypto/prng/ThreadedSeedGenerator.java
new file mode 100644
index 00000000..6b2d5ec2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/prng/ThreadedSeedGenerator.java
@@ -0,0 +1,95 @@
+package org.bouncycastle.crypto.prng;
+
+/**
+ * A thread based seed generator - one source of randomness.
+ * <p>
+ * Based on an idea from Marcus Lippert.
+ * </p>
+ */
+public class ThreadedSeedGenerator
+{
+ private class SeedGenerator
+ implements Runnable
+ {
+ private volatile int counter = 0;
+ private volatile boolean stop = false;
+
+ public void run()
+ {
+ while (!this.stop)
+ {
+ this.counter++;
+ }
+
+ }
+
+ public byte[] generateSeed(
+ int numbytes,
+ boolean fast)
+ {
+ Thread t = new Thread(this);
+ byte[] result = new byte[numbytes];
+ this.counter = 0;
+ this.stop = false;
+ int last = 0;
+ int end;
+
+ t.start();
+ if(fast)
+ {
+ end = numbytes;
+ }
+ else
+ {
+ end = numbytes * 8;
+ }
+ for (int i = 0; i < end; i++)
+ {
+ while (this.counter == last)
+ {
+ try
+ {
+ Thread.sleep(1);
+ }
+ catch (InterruptedException e)
+ {
+ // ignore
+ }
+ }
+ last = this.counter;
+ if (fast)
+ {
+ result[i] = (byte) (last & 0xff);
+ }
+ else
+ {
+ int bytepos = i/8;
+ result[bytepos] = (byte) ((result[bytepos] << 1) | (last & 1));
+ }
+
+ }
+ stop = true;
+ return result;
+ }
+ }
+
+ /**
+ * Generate seed bytes. Set fast to false for best quality.
+ * <p>
+ * If fast is set to true, the code should be round about 8 times faster when
+ * generating a long sequence of random bytes. 20 bytes of random values using
+ * the fast mode take less than half a second on a Nokia e70. If fast is set to false,
+ * it takes round about 2500 ms.
+ * </p>
+ * @param numBytes the number of bytes to generate
+ * @param fast true if fast mode should be used
+ */
+ public byte[] generateSeed(
+ int numBytes,
+ boolean fast)
+ {
+ SeedGenerator gen = new SeedGenerator();
+
+ return gen.generateSeed(numBytes, fast);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/prng/VMPCRandomGenerator.java b/core/src/main/java/org/bouncycastle/crypto/prng/VMPCRandomGenerator.java
new file mode 100644
index 00000000..2146af7f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/prng/VMPCRandomGenerator.java
@@ -0,0 +1,127 @@
+package org.bouncycastle.crypto.prng;
+
+import org.bouncycastle.crypto.util.Pack;
+
+public class VMPCRandomGenerator implements RandomGenerator
+{
+ private byte n = 0;
+
+ /**
+ * Permutation generated by code: <code>
+ * // First 1850 fractional digit of Pi number.
+ * byte[] key = new BigInteger("14159265358979323846...5068006422512520511").toByteArray();
+ * s = 0;
+ * P = new byte[256];
+ * for (int i = 0; i < 256; i++) {
+ * P[i] = (byte) i;
+ * }
+ * for (int m = 0; m < 768; m++) {
+ * s = P[(s + P[m & 0xff] + key[m % key.length]) & 0xff];
+ * byte temp = P[m & 0xff];
+ * P[m & 0xff] = P[s & 0xff];
+ * P[s & 0xff] = temp;
+ * } </code>
+ */
+ private byte[] P =
+ {
+ (byte) 0xbb, (byte) 0x2c, (byte) 0x62, (byte) 0x7f,
+ (byte) 0xb5, (byte) 0xaa, (byte) 0xd4, (byte) 0x0d, (byte) 0x81,
+ (byte) 0xfe, (byte) 0xb2, (byte) 0x82, (byte) 0xcb, (byte) 0xa0,
+ (byte) 0xa1, (byte) 0x08, (byte) 0x18, (byte) 0x71, (byte) 0x56,
+ (byte) 0xe8, (byte) 0x49, (byte) 0x02, (byte) 0x10, (byte) 0xc4,
+ (byte) 0xde, (byte) 0x35, (byte) 0xa5, (byte) 0xec, (byte) 0x80,
+ (byte) 0x12, (byte) 0xb8, (byte) 0x69, (byte) 0xda, (byte) 0x2f,
+ (byte) 0x75, (byte) 0xcc, (byte) 0xa2, (byte) 0x09, (byte) 0x36,
+ (byte) 0x03, (byte) 0x61, (byte) 0x2d, (byte) 0xfd, (byte) 0xe0,
+ (byte) 0xdd, (byte) 0x05, (byte) 0x43, (byte) 0x90, (byte) 0xad,
+ (byte) 0xc8, (byte) 0xe1, (byte) 0xaf, (byte) 0x57, (byte) 0x9b,
+ (byte) 0x4c, (byte) 0xd8, (byte) 0x51, (byte) 0xae, (byte) 0x50,
+ (byte) 0x85, (byte) 0x3c, (byte) 0x0a, (byte) 0xe4, (byte) 0xf3,
+ (byte) 0x9c, (byte) 0x26, (byte) 0x23, (byte) 0x53, (byte) 0xc9,
+ (byte) 0x83, (byte) 0x97, (byte) 0x46, (byte) 0xb1, (byte) 0x99,
+ (byte) 0x64, (byte) 0x31, (byte) 0x77, (byte) 0xd5, (byte) 0x1d,
+ (byte) 0xd6, (byte) 0x78, (byte) 0xbd, (byte) 0x5e, (byte) 0xb0,
+ (byte) 0x8a, (byte) 0x22, (byte) 0x38, (byte) 0xf8, (byte) 0x68,
+ (byte) 0x2b, (byte) 0x2a, (byte) 0xc5, (byte) 0xd3, (byte) 0xf7,
+ (byte) 0xbc, (byte) 0x6f, (byte) 0xdf, (byte) 0x04, (byte) 0xe5,
+ (byte) 0x95, (byte) 0x3e, (byte) 0x25, (byte) 0x86, (byte) 0xa6,
+ (byte) 0x0b, (byte) 0x8f, (byte) 0xf1, (byte) 0x24, (byte) 0x0e,
+ (byte) 0xd7, (byte) 0x40, (byte) 0xb3, (byte) 0xcf, (byte) 0x7e,
+ (byte) 0x06, (byte) 0x15, (byte) 0x9a, (byte) 0x4d, (byte) 0x1c,
+ (byte) 0xa3, (byte) 0xdb, (byte) 0x32, (byte) 0x92, (byte) 0x58,
+ (byte) 0x11, (byte) 0x27, (byte) 0xf4, (byte) 0x59, (byte) 0xd0,
+ (byte) 0x4e, (byte) 0x6a, (byte) 0x17, (byte) 0x5b, (byte) 0xac,
+ (byte) 0xff, (byte) 0x07, (byte) 0xc0, (byte) 0x65, (byte) 0x79,
+ (byte) 0xfc, (byte) 0xc7, (byte) 0xcd, (byte) 0x76, (byte) 0x42,
+ (byte) 0x5d, (byte) 0xe7, (byte) 0x3a, (byte) 0x34, (byte) 0x7a,
+ (byte) 0x30, (byte) 0x28, (byte) 0x0f, (byte) 0x73, (byte) 0x01,
+ (byte) 0xf9, (byte) 0xd1, (byte) 0xd2, (byte) 0x19, (byte) 0xe9,
+ (byte) 0x91, (byte) 0xb9, (byte) 0x5a, (byte) 0xed, (byte) 0x41,
+ (byte) 0x6d, (byte) 0xb4, (byte) 0xc3, (byte) 0x9e, (byte) 0xbf,
+ (byte) 0x63, (byte) 0xfa, (byte) 0x1f, (byte) 0x33, (byte) 0x60,
+ (byte) 0x47, (byte) 0x89, (byte) 0xf0, (byte) 0x96, (byte) 0x1a,
+ (byte) 0x5f, (byte) 0x93, (byte) 0x3d, (byte) 0x37, (byte) 0x4b,
+ (byte) 0xd9, (byte) 0xa8, (byte) 0xc1, (byte) 0x1b, (byte) 0xf6,
+ (byte) 0x39, (byte) 0x8b, (byte) 0xb7, (byte) 0x0c, (byte) 0x20,
+ (byte) 0xce, (byte) 0x88, (byte) 0x6e, (byte) 0xb6, (byte) 0x74,
+ (byte) 0x8e, (byte) 0x8d, (byte) 0x16, (byte) 0x29, (byte) 0xf2,
+ (byte) 0x87, (byte) 0xf5, (byte) 0xeb, (byte) 0x70, (byte) 0xe3,
+ (byte) 0xfb, (byte) 0x55, (byte) 0x9f, (byte) 0xc6, (byte) 0x44,
+ (byte) 0x4a, (byte) 0x45, (byte) 0x7d, (byte) 0xe2, (byte) 0x6b,
+ (byte) 0x5c, (byte) 0x6c, (byte) 0x66, (byte) 0xa9, (byte) 0x8c,
+ (byte) 0xee, (byte) 0x84, (byte) 0x13, (byte) 0xa7, (byte) 0x1e,
+ (byte) 0x9d, (byte) 0xdc, (byte) 0x67, (byte) 0x48, (byte) 0xba,
+ (byte) 0x2e, (byte) 0xe6, (byte) 0xa4, (byte) 0xab, (byte) 0x7c,
+ (byte) 0x94, (byte) 0x00, (byte) 0x21, (byte) 0xef, (byte) 0xea,
+ (byte) 0xbe, (byte) 0xca, (byte) 0x72, (byte) 0x4f, (byte) 0x52,
+ (byte) 0x98, (byte) 0x3f, (byte) 0xc2, (byte) 0x14, (byte) 0x7b,
+ (byte) 0x3b, (byte) 0x54 };
+
+ /**
+ * Value generated in the same way as {@link VMPCRandomGenerator#P};
+ */
+ private byte s = (byte) 0xbe;
+
+ public VMPCRandomGenerator()
+ {
+ }
+
+ public void addSeedMaterial(byte[] seed)
+ {
+ for (int m = 0; m < seed.length; m++)
+ {
+ s = P[(s + P[n & 0xff] + seed[m]) & 0xff];
+ byte temp = P[n & 0xff];
+ P[n & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ n = (byte) ((n + 1) & 0xff);
+ }
+ }
+
+ public void addSeedMaterial(long seed)
+ {
+ addSeedMaterial(Pack.longToBigEndian(seed));
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ nextBytes(bytes, 0, bytes.length);
+ }
+
+ public void nextBytes(byte[] bytes, int start, int len)
+ {
+ synchronized (P)
+ {
+ int end = start + len;
+ for (int i = start; i != end; i++)
+ {
+ s = P[(s + P[n & 0xff]) & 0xff];
+ bytes[i] = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff];
+ byte temp = P[n & 0xff];
+ P[n & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ n = (byte) ((n + 1) & 0xff);
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/prng/drbg/CTRSP800DRBG.java b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/CTRSP800DRBG.java
new file mode 100644
index 00000000..84fe4a40
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/CTRSP800DRBG.java
@@ -0,0 +1,468 @@
+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 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;
+
+ // internal state
+ private byte[] _Key;
+ private byte[] _V;
+ 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);
+
+ if (securityStrength > 256)
+ {
+ 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");
+ }
+
+ if (entropySource.entropySize() < securityStrength)
+ {
+ throw new IllegalArgumentException("Not enough entropy for security strength required");
+ }
+
+ 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)
+ {
+ byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalisationString);
+ byte[] seed = Block_Cipher_df(seedMaterial, _seedLength);
+
+ 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);
+
+ _reseedCounter = 1;
+ }
+
+ private void CTR_DRBG_Update(byte[] seed, byte[] key, byte[] v)
+ {
+ byte[] temp = new byte[seed.length];
+ byte[] outputBlock = new byte[_engine.getBlockSize()];
+
+ int i=0;
+ int outLen = _engine.getBlockSize();
+
+ _engine.init(true, new KeyParameter(expandKey(key)));
+ while (i*outLen < seed.length)
+ {
+ addOneTo(v);
+ _engine.processBlock(v, 0, outputBlock, 0);
+
+ int bytesToCopy = ((temp.length - i * outLen) > outLen)
+ ? outLen : (temp.length - i * outLen);
+
+ System.arraycopy(outputBlock, 0, temp, i * outLen, bytesToCopy);
+ ++i;
+ }
+
+ XOR(temp, seed, temp, 0);
+
+ 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)
+ {
+ for (int i=0; i< out.length; i++)
+ {
+ out[i] = (byte)(a[i] ^ b[i+bOff]);
+ }
+ }
+
+ private void addOneTo(byte[] longer)
+ {
+ int carry = 1;
+ for (int i = 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;
+ }
+ }
+
+ // -- Internal state migration ---
+
+ private static final byte[] K_BITS = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+
+ // 1. If (number_of_bits_to_return > max_number_of_bits), then return an
+ // ERROR_FLAG.
+ // 2. L = len (input_string)/8.
+ // 3. N = number_of_bits_to_return/8.
+ // Comment: L is the bitstring represention of
+ // the integer resulting from len (input_string)/8.
+ // L shall be represented as a 32-bit integer.
+ //
+ // Comment : N is the bitstring represention of
+ // the integer resulting from
+ // number_of_bits_to_return/8. N shall be
+ // represented as a 32-bit integer.
+ //
+ // 4. S = L || N || input_string || 0x80.
+ // 5. While (len (S) mod outlen)
+ // Comment : Pad S with zeros, if necessary.
+ // 0, S = S || 0x00.
+ //
+ // Comment : Compute the starting value.
+ // 6. temp = the Null string.
+ // 7. i = 0.
+ // 8. K = Leftmost keylen bits of 0x00010203...1D1E1F.
+ // 9. While len (temp) < keylen + outlen, do
+ //
+ // IV = i || 0outlen - len (i).
+ //
+ // 9.1
+ //
+ // temp = temp || BCC (K, (IV || S)).
+ //
+ // 9.2
+ //
+ // i = i + 1.
+ //
+ // 9.3
+ //
+ // Comment : i shall be represented as a 32-bit
+ // integer, i.e., len (i) = 32.
+ //
+ // Comment: The 32-bit integer represenation of
+ // i is padded with zeros to outlen bits.
+ //
+ // Comment: Compute the requested number of
+ // bits.
+ //
+ // 10. K = Leftmost keylen bits of temp.
+ //
+ // 11. X = Next outlen bits of temp.
+ //
+ // 12. temp = the Null string.
+ //
+ // 13. While len (temp) < number_of_bits_to_return, do
+ //
+ // 13.1 X = Block_Encrypt (K, X).
+ //
+ // 13.2 temp = temp || X.
+ //
+ // 14. requested_bits = Leftmost number_of_bits_to_return of temp.
+ //
+ // 15. Return SUCCESS and requested_bits.
+ private byte[] Block_Cipher_df(byte[] inputString, int bitLength)
+ {
+ int outLen = _engine.getBlockSize();
+ int L = inputString.length; // already in bytes
+ int N = bitLength / 8;
+ // 4 S = L || N || inputstring || 0x80
+ int sLen = 4 + 4 + L + 1;
+ int blockLen = ((sLen + outLen - 1) / outLen) * outLen;
+ byte[] S = new byte[blockLen];
+ copyIntToByteArray(S, L, 0);
+ copyIntToByteArray(S, N, 4);
+ System.arraycopy(inputString, 0, S, 8, L);
+ S[8 + L] = (byte)0x80;
+ // S already padded with zeros
+
+ byte[] temp = new byte[_keySizeInBits / 8 + outLen];
+ byte[] bccOut = new byte[outLen];
+
+ byte[] IV = new byte[outLen];
+
+ int i = 0;
+ byte[] K = new byte[_keySizeInBits / 8];
+ System.arraycopy(K_BITS, 0, K, 0, K.length);
+
+ while (i*outLen*8 < _keySizeInBits + outLen *8)
+ {
+ copyIntToByteArray(IV, i, 0);
+ BCC(bccOut, K, IV, S);
+
+ int bytesToCopy = ((temp.length - i * outLen) > outLen)
+ ? outLen
+ : (temp.length - i * outLen);
+
+ System.arraycopy(bccOut, 0, temp, i * outLen, bytesToCopy);
+ ++i;
+ }
+
+ byte[] X = new byte[outLen];
+ System.arraycopy(temp, 0, K, 0, K.length);
+ System.arraycopy(temp, K.length, X, 0, X.length);
+
+ temp = new byte[bitLength / 2];
+
+ i = 0;
+ _engine.init(true, new KeyParameter(expandKey(K)));
+
+ while (i * outLen < temp.length)
+ {
+ _engine.processBlock(X, 0, X, 0);
+
+ int bytesToCopy = ((temp.length - i * outLen) > outLen)
+ ? outLen
+ : (temp.length - i * outLen);
+
+ System.arraycopy(X, 0, temp, i * outLen, bytesToCopy);
+ i++;
+ }
+
+ return temp;
+ }
+
+ /*
+ * 1. chaining_value = 0^outlen
+ * . Comment: Set the first chaining value to outlen zeros.
+ * 2. n = len (data)/outlen.
+ * 3. Starting with the leftmost bits of data, split the data into n blocks of outlen bits
+ * each, forming block(1) to block(n).
+ * 4. For i = 1 to n do
+ * 4.1 input_block = chaining_value ^ block(i) .
+ * 4.2 chaining_value = Block_Encrypt (Key, input_block).
+ * 5. output_block = chaining_value.
+ * 6. Return output_block.
+ */
+ private void BCC(byte[] bccOut, byte[] k, byte[] iV, byte[] data)
+ {
+ int outlen = _engine.getBlockSize();
+ byte[] chainingValue = new byte[outlen]; // initial values = 0
+ int n = data.length / outlen;
+
+ byte[] inputBlock = new byte[outlen];
+
+ _engine.init(true, new KeyParameter(expandKey(k)));
+
+ _engine.processBlock(iV, 0, chainingValue, 0);
+
+ for (int i = 0; i < n; i++)
+ {
+ XOR(inputBlock, chainingValue, data, i*outlen);
+ _engine.processBlock(inputBlock, 0, chainingValue, 0);
+ }
+
+ System.arraycopy(chainingValue, 0, bccOut, 0, bccOut.length);
+ }
+
+ private void copyIntToByteArray(byte[] buf, int value, int offSet)
+ {
+ buf[offSet + 0] = ((byte)(value >> 24));
+ buf[offSet + 1] = ((byte)(value >> 16));
+ buf[offSet + 2] = ((byte)(value >> 8));
+ 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)
+ {
+ additionalInput = Block_Cipher_df(additionalInput, _seedLength);
+ CTR_DRBG_Update(additionalInput, _Key, _V);
+ }
+ else
+ {
+ additionalInput = new byte[_seedLength];
+ }
+
+ byte[] out = new byte[_V.length];
+
+ _engine.init(true, new KeyParameter(expandKey(_Key)));
+
+ for (int i = 0; i < output.length / out.length; i++)
+ {
+ addOneTo(_V);
+
+ _engine.processBlock(_V, 0, out, 0);
+
+ int bytesToCopy = ((output.length - i * out.length) > out.length)
+ ? out.length
+ : (output.length - i * _V.length);
+
+ System.arraycopy(out, 0, output, i * out.length, bytesToCopy);
+ }
+
+ CTR_DRBG_Update(additionalInput, _Key, _V);
+
+ _reseedCounter++;
+
+ 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/core/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java
new file mode 100644
index 00000000..3cee39cb
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java
@@ -0,0 +1,267 @@
+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
+{
+ /*
+ * Default P, Q values for each curve
+ */
+ private static final BigInteger p256_Px = new BigInteger("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16);
+ private static final BigInteger p256_Py = new BigInteger("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16);
+ private static final BigInteger p256_Qx = new BigInteger("c97445f45cdef9f0d3e05e1e585fc297235b82b5be8ff3efca67c59852018192", 16);
+ private static final BigInteger p256_Qy = new BigInteger("b28ef557ba31dfcbdd21ac46e2a91e3c304f44cb87058ada2cb815151e610046", 16);
+
+ private static final BigInteger p384_Px = new BigInteger("aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7", 16);
+ private static final BigInteger p384_Py = new BigInteger("3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f", 16);
+ private static final BigInteger p384_Qx = new BigInteger("8e722de3125bddb05580164bfe20b8b432216a62926c57502ceede31c47816edd1e89769124179d0b695106428815065", 16);
+ private static final BigInteger p384_Qy = new BigInteger("023b1660dd701d0839fd45eec36f9ee7b32e13b315dc02610aa1b636e346df671f790f84c5e09b05674dbb7e45c803dd", 16);
+
+ private static final BigInteger p521_Px = new BigInteger("c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66", 16);
+ private static final BigInteger p521_Py = new BigInteger("11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650", 16);
+ private static final BigInteger p521_Qx = new BigInteger("1b9fa3e518d683c6b65763694ac8efbaec6fab44f2276171a42726507dd08add4c3b3f4c1ebc5b1222ddba077f722943b24c3edfa0f85fe24d0c8c01591f0be6f63", 16);
+ private static final BigInteger p521_Qy = new BigInteger("1f3bdba585295d9a1110d1df1f9430ef8442c5018976ff3437ef91b81dc0b8132c8d5c39c32d0e004a3092b7d327c0e7a4d26d2c7b69b58f9066652911e457779de", 16);
+
+ 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 long _reseedCounter;
+ private EntropySource _entropySource;
+ private int _securityStrength;
+ private int _seedlen;
+ private int _outlen;
+ private ECCurve.Fp _curve;
+ private ECPoint _P;
+ private ECPoint _Q;
+ private byte[] _s;
+ 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)
+ {
+ _digest = digest;
+ _entropySource = entropySource;
+ _securityStrength = securityStrength;
+
+ 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, 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();
+ _P = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p256_Px), new ECFieldElement.Fp(_curve.getQ(), p256_Py));
+ _Q = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p256_Qx), new ECFieldElement.Fp(_curve.getQ(), p256_Qy));
+ }
+ 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();
+ _P = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p384_Px), new ECFieldElement.Fp(_curve.getQ(), p384_Py));
+ _Q = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p384_Qx), new ECFieldElement.Fp(_curve.getQ(), p384_Qy));
+ }
+ 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();
+ _P = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p521_Px), new ECFieldElement.Fp(_curve.getQ(), p521_Py));
+ _Q = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p521_Qx), new ECFieldElement.Fp(_curve.getQ(), p521_Qy));
+ }
+ else
+ {
+ throw new IllegalArgumentException("security strength cannot be greater than 256 bits");
+ }
+
+ _s = Utils.hash_df(_digest, seedMaterial, _seedlen);
+ _sLength = _s.length;
+
+ _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;
+ 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;
+ }
+
+ if (additionalInput != null)
+ {
+ // Note: we ignore the use of pad8 on the additional input as we mandate byte arrays for it.
+ additionalInput = Utils.hash_df(_digest, additionalInput, _seedlen);
+ }
+
+ for (int i = 0; i < m; i++)
+ {
+ BigInteger t = new BigInteger(1, xor(_s, additionalInput));
+
+ _s = _P.multiply(t).getX().toBigInteger().toByteArray();
+
+ //System.err.println("S: " + new String(Hex.encode(_s)));
+
+ byte[] r = _Q.multiply(new BigInteger(1, _s)).getX().toBigInteger().toByteArray();
+
+ if (r.length > _outlen)
+ {
+ System.arraycopy(r, r.length - _outlen, output, i * _outlen, _outlen);
+ }
+ else
+ {
+ System.arraycopy(r, 0, output, i * _outlen + (_outlen - r.length), r.length);
+ }
+
+ //System.err.println("R: " + new String(Hex.encode(r)));
+ additionalInput = null;
+
+ _reseedCounter++;
+ }
+
+ if (m * _outlen < output.length)
+ {
+ BigInteger t = new BigInteger(1, xor(_s, additionalInput));
+
+ _s = _P.multiply(t).getX().toBigInteger().toByteArray();
+
+ byte[] r = _Q.multiply(new BigInteger(1, _s)).getX().toBigInteger().toByteArray();
+
+ System.arraycopy(r, 0, output, m * _outlen, output.length - (m * _outlen));
+ }
+
+ // Need to preserve length of S as unsigned int.
+ _s = BigIntegers.asUnsignedByteArray(_sLength, _P.multiply(new BigInteger(1, _s)).getX().toBigInteger());
+
+ return numberOfBits;
+ }
+
+ /**
+ * Reseed the DRBG.
+ *
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ */
+ public void reseed(byte[] additionalInput)
+ {
+ if (Utils.isTooLarge(additionalInput, MAX_ADDITIONAL_INPUT / 8))
+ {
+ throw new IllegalArgumentException("Additional input string too large");
+ }
+
+ byte[] entropy = _entropySource.getEntropy();
+ byte[] seedMaterial = Arrays.concatenate(pad8(_s, _seedlen), entropy, additionalInput);
+
+ _s = Utils.hash_df(_digest, seedMaterial, _seedlen);
+
+ _reseedCounter = 0;
+ }
+
+ private byte[] xor(byte[] a, byte[] b)
+ {
+ if (b == null)
+ {
+ return a;
+ }
+
+ byte[] rv = new byte[a.length];
+
+ for (int i = 0; i != rv.length; i++)
+ {
+ rv[i] = (byte)(a[i] ^ b[i]);
+ }
+
+ return rv;
+ }
+
+ // Note: works in place
+ private byte[] pad8(byte[] s, int seedlen)
+ {
+ if (seedlen % 8 == 0)
+ {
+ return s;
+ }
+
+ int shift = 8 - (seedlen % 8);
+ int carry = 0;
+
+ for (int i = s.length - 1; i >= 0; i--)
+ {
+ int b = s[i] & 0xff;
+ s[i] = (byte)((b << shift) | (carry >> (8 - shift)));
+ carry = b;
+ }
+
+ return s;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/prng/drbg/HMacSP800DRBG.java b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/HMacSP800DRBG.java
new file mode 100644
index 00000000..3ddeaac6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/HMacSP800DRBG.java
@@ -0,0 +1,171 @@
+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 long _reseedCounter;
+ private EntropySource _entropySource;
+ private Mac _hMac;
+
+ /**
+ * 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)
+ {
+ 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;
+
+ byte[] entropy = entropySource.getEntropy();
+ byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalizationString);
+
+ _K = new byte[hMac.getMacSize()];
+ _V = new byte[_K.length];
+ Arrays.fill(_V, (byte)1);
+
+ hmac_DRBG_Update(seedMaterial);
+
+ _reseedCounter = 1;
+ }
+
+ private void hmac_DRBG_Update(byte[] seedMaterial)
+ {
+ hmac_DRBG_Update_Func(seedMaterial, (byte)0x00);
+ if (seedMaterial != null)
+ {
+ hmac_DRBG_Update_Func(seedMaterial, (byte)0x01);
+ }
+ }
+
+ private void hmac_DRBG_Update_Func(byte[] seedMaterial, byte vValue)
+ {
+ _hMac.init(new KeyParameter(_K));
+
+ _hMac.update(_V, 0, _V.length);
+ _hMac.update(vValue);
+
+ if (seedMaterial != null)
+ {
+ _hMac.update(seedMaterial, 0, seedMaterial.length);
+ }
+
+ _hMac.doFinal(_K, 0);
+
+ _hMac.init(new KeyParameter(_K));
+ _hMac.update(_V, 0, _V.length);
+
+ _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)
+ {
+ 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)
+ {
+ hmac_DRBG_Update(additionalInput);
+ }
+
+ // 3.
+ byte[] rv = new byte[output.length];
+
+ int m = output.length / _V.length;
+
+ _hMac.init(new KeyParameter(_K));
+
+ for (int i = 0; i < m; i++)
+ {
+ _hMac.update(_V, 0, _V.length);
+ _hMac.doFinal(_V, 0);
+
+ System.arraycopy(_V, 0, rv, i * _V.length, _V.length);
+ }
+
+ if (m * _V.length < rv.length)
+ {
+ _hMac.update(_V, 0, _V.length);
+ _hMac.doFinal(_V, 0);
+
+ System.arraycopy(_V, 0, rv, m * _V.length, rv.length - (m * _V.length));
+ }
+
+ hmac_DRBG_Update(additionalInput);
+
+ _reseedCounter++;
+
+ System.arraycopy(rv, 0, output, 0, output.length);
+
+ 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();
+ byte[] seedMaterial = Arrays.concatenate(entropy, additionalInput);
+
+ hmac_DRBG_Update(seedMaterial);
+
+ _reseedCounter = 1;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/prng/drbg/HashSP800DRBG.java b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/HashSP800DRBG.java
new file mode 100644
index 00000000..4ed57163
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/HashSP800DRBG.java
@@ -0,0 +1,269 @@
+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/core/src/main/java/org/bouncycastle/crypto/prng/drbg/SP80090DRBG.java b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/SP80090DRBG.java
new file mode 100644
index 00000000..93bc8945
--- /dev/null
+++ b/core/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/core/src/main/java/org/bouncycastle/crypto/prng/drbg/Utils.java b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/Utils.java
new file mode 100644
index 00000000..f7a41176
--- /dev/null
+++ b/core/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/core/src/main/java/org/bouncycastle/crypto/signers/DSADigestSigner.java b/core/src/main/java/org/bouncycastle/crypto/signers/DSADigestSigner.java
new file mode 100644
index 00000000..2e4c48d3
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/signers/DSADigestSigner.java
@@ -0,0 +1,163 @@
+package org.bouncycastle.crypto.signers;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+
+public class DSADigestSigner
+ implements Signer
+{
+ private final Digest digest;
+ private final DSA dsaSigner;
+ private boolean forSigning;
+
+ public DSADigestSigner(
+ DSA signer,
+ Digest digest)
+ {
+ this.digest = digest;
+ this.dsaSigner = signer;
+ }
+
+ public void init(
+ boolean forSigning,
+ CipherParameters parameters)
+ {
+ this.forSigning = forSigning;
+
+ AsymmetricKeyParameter k;
+
+ if (parameters instanceof ParametersWithRandom)
+ {
+ k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).getParameters();
+ }
+ else
+ {
+ k = (AsymmetricKeyParameter)parameters;
+ }
+
+ if (forSigning && !k.isPrivate())
+ {
+ throw new IllegalArgumentException("Signing Requires Private Key.");
+ }
+
+ if (!forSigning && k.isPrivate())
+ {
+ throw new IllegalArgumentException("Verification Requires Public Key.");
+ }
+
+ reset();
+
+ dsaSigner.init(forSigning, parameters);
+ }
+
+ /**
+ * update the internal digest with the byte b
+ */
+ public void update(
+ byte input)
+ {
+ digest.update(input);
+ }
+
+ /**
+ * update the internal digest with the byte array in
+ */
+ public void update(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ digest.update(input, inOff, length);
+ }
+
+ /**
+ * Generate a signature for the message we've been loaded with using
+ * the key we were initialised with.
+ */
+ public byte[] generateSignature()
+ {
+ if (!forSigning)
+ {
+ throw new IllegalStateException("DSADigestSigner not initialised for signature generation.");
+ }
+
+ byte[] hash = new byte[digest.getDigestSize()];
+ digest.doFinal(hash, 0);
+
+ BigInteger[] sig = dsaSigner.generateSignature(hash);
+
+ try
+ {
+ return derEncode(sig[0], sig[1]);
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("unable to encode signature");
+ }
+ }
+
+ public boolean verifySignature(
+ byte[] signature)
+ {
+ if (forSigning)
+ {
+ throw new IllegalStateException("DSADigestSigner not initialised for verification");
+ }
+
+ byte[] hash = new byte[digest.getDigestSize()];
+ digest.doFinal(hash, 0);
+
+ try
+ {
+ BigInteger[] sig = derDecode(signature);
+ return dsaSigner.verifySignature(hash, sig[0], sig[1]);
+ }
+ catch (IOException e)
+ {
+ return false;
+ }
+ }
+
+ public void reset()
+ {
+ digest.reset();
+ }
+
+ private byte[] derEncode(
+ BigInteger r,
+ BigInteger s)
+ throws IOException
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(new DERInteger(r));
+ v.add(new DERInteger(s));
+
+ return new DERSequence(v).getEncoded(ASN1Encoding.DER);
+ }
+
+ private BigInteger[] derDecode(
+ byte[] encoding)
+ throws IOException
+ {
+ ASN1Sequence s = (ASN1Sequence)ASN1Primitive.fromByteArray(encoding);
+
+ return new BigInteger[]
+ {
+ ((DERInteger)s.getObjectAt(0)).getValue(),
+ ((DERInteger)s.getObjectAt(1)).getValue()
+ };
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java b/core/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java
new file mode 100644
index 00000000..a96cef07
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java
@@ -0,0 +1,138 @@
+package org.bouncycastle.crypto.signers;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.params.DSAKeyParameters;
+import org.bouncycastle.crypto.params.DSAParameters;
+import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
+import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/**
+ * The Digital Signature Algorithm - as described in "Handbook of Applied
+ * Cryptography", pages 452 - 453.
+ */
+public class DSASigner
+ implements DSA
+{
+ DSAKeyParameters key;
+
+ SecureRandom random;
+
+ public void init(
+ boolean forSigning,
+ CipherParameters param)
+ {
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.random = rParam.getRandom();
+ this.key = (DSAPrivateKeyParameters)rParam.getParameters();
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ this.key = (DSAPrivateKeyParameters)param;
+ }
+ }
+ else
+ {
+ this.key = (DSAPublicKeyParameters)param;
+ }
+ }
+
+ /**
+ * generate a signature for the given message using the key we were
+ * initialised with. For conventional DSA the message should be a SHA-1
+ * hash of the message of interest.
+ *
+ * @param message the message that will be verified later.
+ */
+ public BigInteger[] generateSignature(
+ byte[] message)
+ {
+ DSAParameters params = key.getParameters();
+ BigInteger m = calculateE(params.getQ(), message);
+ BigInteger k;
+ int qBitLength = params.getQ().bitLength();
+
+ do
+ {
+ k = new BigInteger(qBitLength, random);
+ }
+ while (k.compareTo(params.getQ()) >= 0);
+
+ BigInteger r = params.getG().modPow(k, params.getP()).mod(params.getQ());
+
+ k = k.modInverse(params.getQ()).multiply(
+ m.add(((DSAPrivateKeyParameters)key).getX().multiply(r)));
+
+ BigInteger s = k.mod(params.getQ());
+
+ BigInteger[] res = new BigInteger[2];
+
+ res[0] = r;
+ res[1] = s;
+
+ return res;
+ }
+
+ /**
+ * return true if the value r and s represent a DSA signature for
+ * the passed in message for standard DSA the message should be a
+ * SHA-1 hash of the real message to be verified.
+ */
+ public boolean verifySignature(
+ byte[] message,
+ BigInteger r,
+ BigInteger s)
+ {
+ DSAParameters params = key.getParameters();
+ BigInteger m = calculateE(params.getQ(), message);
+ BigInteger zero = BigInteger.valueOf(0);
+
+ if (zero.compareTo(r) >= 0 || params.getQ().compareTo(r) <= 0)
+ {
+ return false;
+ }
+
+ if (zero.compareTo(s) >= 0 || params.getQ().compareTo(s) <= 0)
+ {
+ return false;
+ }
+
+ BigInteger w = s.modInverse(params.getQ());
+
+ BigInteger u1 = m.multiply(w).mod(params.getQ());
+ BigInteger u2 = r.multiply(w).mod(params.getQ());
+
+ u1 = params.getG().modPow(u1, params.getP());
+ u2 = ((DSAPublicKeyParameters)key).getY().modPow(u2, params.getP());
+
+ BigInteger v = u1.multiply(u2).mod(params.getP()).mod(params.getQ());
+
+ return v.equals(r);
+ }
+
+ private BigInteger calculateE(BigInteger n, byte[] message)
+ {
+ if (n.bitLength() >= message.length * 8)
+ {
+ return new BigInteger(1, message);
+ }
+ else
+ {
+ byte[] trunc = new byte[n.bitLength() / 8];
+
+ System.arraycopy(message, 0, trunc, 0, trunc.length);
+
+ return new BigInteger(1, trunc);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java b/core/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java
new file mode 100644
index 00000000..a8fc194e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java
@@ -0,0 +1,163 @@
+package org.bouncycastle.crypto.signers;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.params.ECKeyParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.math.ec.ECAlgorithms;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * DSTU 4145-2002
+ * <p>
+ * National Ukrainian standard of digital signature based on elliptic curves (DSTU 4145-2002).
+ * </p>
+ */
+public class DSTU4145Signer
+ implements DSA
+{
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ private ECKeyParameters key;
+ private SecureRandom random;
+
+ public void init(boolean forSigning, CipherParameters param)
+ {
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.random = rParam.getRandom();
+ param = rParam.getParameters();
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ }
+
+ this.key = (ECPrivateKeyParameters)param;
+ }
+ else
+ {
+ this.key = (ECPublicKeyParameters)param;
+ }
+
+ }
+
+ public BigInteger[] generateSignature(byte[] message)
+ {
+ ECFieldElement h = hash2FieldElement(key.getParameters().getCurve(), message);
+ if (h.toBigInteger().signum() == 0)
+ {
+ h = key.getParameters().getCurve().fromBigInteger(ONE);
+ }
+
+ BigInteger e, r, s;
+ ECFieldElement Fe, y;
+
+ do
+ {
+ do
+ {
+ do
+ {
+ e = generateRandomInteger(key.getParameters().getN(), random);
+ Fe = key.getParameters().getG().multiply(e).getX();
+ }
+ while (Fe.toBigInteger().signum() == 0);
+
+ y = h.multiply(Fe);
+ r = fieldElement2Integer(key.getParameters().getN(), y);
+ }
+ while (r.signum() == 0);
+
+ s = r.multiply(((ECPrivateKeyParameters)key).getD()).add(e).mod(key.getParameters().getN());
+ }
+ while (s.signum() == 0);
+
+ return new BigInteger[]{r, s};
+ }
+
+ public boolean verifySignature(byte[] message, BigInteger r, BigInteger s)
+ {
+ if (r.signum() == 0 || s.signum() == 0)
+ {
+ return false;
+ }
+ if (r.compareTo(key.getParameters().getN()) >= 0 || s.compareTo(key.getParameters().getN()) >= 0)
+ {
+ return false;
+ }
+
+ ECFieldElement h = hash2FieldElement(key.getParameters().getCurve(), message);
+ if (h.toBigInteger().signum() == 0)
+ {
+ h = key.getParameters().getCurve().fromBigInteger(ONE);
+ }
+
+ ECPoint R = ECAlgorithms.sumOfTwoMultiplies(key.getParameters().getG(), s, ((ECPublicKeyParameters)key).getQ(), r);
+
+ // components must be bogus.
+ if (R.isInfinity())
+ {
+ return false;
+ }
+
+ ECFieldElement y = h.multiply(R.getX());
+ return fieldElement2Integer(key.getParameters().getN(), y).compareTo(r) == 0;
+ }
+
+ /**
+ * Generates random integer such, than its bit length is less than that of n
+ */
+ private static BigInteger generateRandomInteger(BigInteger n, SecureRandom random)
+ {
+ return new BigInteger(n.bitLength() - 1, random);
+ }
+
+ private static void reverseBytes(byte[] bytes)
+ {
+ byte tmp;
+
+ for (int i=0; i<bytes.length/2; i++)
+ {
+ tmp=bytes[i];
+ bytes[i]=bytes[bytes.length-1-i];
+ bytes[bytes.length-1-i]=tmp;
+ }
+ }
+
+ private static ECFieldElement hash2FieldElement(ECCurve curve, byte[] hash)
+ {
+ byte[] data = Arrays.clone(hash);
+ reverseBytes(data);
+ BigInteger num = new BigInteger(1, data);
+ while (num.bitLength() >= curve.getFieldSize())
+ {
+ num = num.clearBit(num.bitLength() - 1);
+ }
+
+ return curve.fromBigInteger(num);
+ }
+
+ private static BigInteger fieldElement2Integer(BigInteger n, ECFieldElement fieldElement)
+ {
+ BigInteger num = fieldElement.toBigInteger();
+ while (num.bitLength() >= n.bitLength())
+ {
+ num = num.clearBit(num.bitLength() - 1);
+ }
+
+ return num;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java b/core/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java
new file mode 100644
index 00000000..a80c574b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java
@@ -0,0 +1,169 @@
+package org.bouncycastle.crypto.signers;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.params.ECKeyParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.math.ec.ECAlgorithms;
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECPoint;
+
+/**
+ * EC-DSA as described in X9.62
+ */
+public class ECDSASigner
+ implements ECConstants, DSA
+{
+ ECKeyParameters key;
+
+ SecureRandom random;
+
+ public void init(
+ boolean forSigning,
+ CipherParameters param)
+ {
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.random = rParam.getRandom();
+ this.key = (ECPrivateKeyParameters)rParam.getParameters();
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ this.key = (ECPrivateKeyParameters)param;
+ }
+ }
+ else
+ {
+ this.key = (ECPublicKeyParameters)param;
+ }
+ }
+
+ // 5.3 pg 28
+ /**
+ * generate a signature for the given message using the key we were
+ * initialised with. For conventional DSA the message should be a SHA-1
+ * hash of the message of interest.
+ *
+ * @param message the message that will be verified later.
+ */
+ public BigInteger[] generateSignature(
+ byte[] message)
+ {
+ BigInteger n = key.getParameters().getN();
+ BigInteger e = calculateE(n, message);
+ BigInteger r = null;
+ BigInteger s = null;
+
+ // 5.3.2
+ do // generate s
+ {
+ BigInteger k = null;
+ int nBitLength = n.bitLength();
+
+ do // generate r
+ {
+ do
+ {
+ k = new BigInteger(nBitLength, random);
+ }
+ while (k.equals(ZERO) || k.compareTo(n) >= 0);
+
+ ECPoint p = key.getParameters().getG().multiply(k);
+
+ // 5.3.3
+ BigInteger x = p.getX().toBigInteger();
+
+ r = x.mod(n);
+ }
+ while (r.equals(ZERO));
+
+ BigInteger d = ((ECPrivateKeyParameters)key).getD();
+
+ s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n);
+ }
+ while (s.equals(ZERO));
+
+ BigInteger[] res = new BigInteger[2];
+
+ res[0] = r;
+ res[1] = s;
+
+ return res;
+ }
+
+ // 5.4 pg 29
+ /**
+ * return true if the value r and s represent a DSA signature for
+ * the passed in message (for standard DSA the message should be
+ * a SHA-1 hash of the real message to be verified).
+ */
+ public boolean verifySignature(
+ byte[] message,
+ BigInteger r,
+ BigInteger s)
+ {
+ BigInteger n = key.getParameters().getN();
+ BigInteger e = calculateE(n, message);
+
+ // r in the range [1,n-1]
+ if (r.compareTo(ONE) < 0 || r.compareTo(n) >= 0)
+ {
+ return false;
+ }
+
+ // s in the range [1,n-1]
+ if (s.compareTo(ONE) < 0 || s.compareTo(n) >= 0)
+ {
+ return false;
+ }
+
+ BigInteger c = s.modInverse(n);
+
+ BigInteger u1 = e.multiply(c).mod(n);
+ BigInteger u2 = r.multiply(c).mod(n);
+
+ ECPoint G = key.getParameters().getG();
+ ECPoint Q = ((ECPublicKeyParameters)key).getQ();
+
+ ECPoint point = ECAlgorithms.sumOfTwoMultiplies(G, u1, Q, u2);
+
+ // components must be bogus.
+ if (point.isInfinity())
+ {
+ return false;
+ }
+
+ BigInteger v = point.getX().toBigInteger().mod(n);
+
+ return v.equals(r);
+ }
+
+ private BigInteger calculateE(BigInteger n, byte[] message)
+ {
+ int log2n = n.bitLength();
+ int messageBitLength = message.length * 8;
+
+ if (log2n >= messageBitLength)
+ {
+ return new BigInteger(1, message);
+ }
+ else
+ {
+ BigInteger trunc = new BigInteger(1, message);
+
+ trunc = trunc.shiftRight(messageBitLength - log2n);
+
+ return trunc;
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java b/core/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java
new file mode 100644
index 00000000..7256d353
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java
@@ -0,0 +1,158 @@
+package org.bouncycastle.crypto.signers;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.params.ECKeyParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.math.ec.ECAlgorithms;
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECPoint;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/**
+ * GOST R 34.10-2001 Signature Algorithm
+ */
+public class ECGOST3410Signer
+ implements DSA
+{
+ ECKeyParameters key;
+
+ SecureRandom random;
+
+ public void init(
+ boolean forSigning,
+ CipherParameters param)
+ {
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.random = rParam.getRandom();
+ this.key = (ECPrivateKeyParameters)rParam.getParameters();
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ this.key = (ECPrivateKeyParameters)param;
+ }
+ }
+ else
+ {
+ this.key = (ECPublicKeyParameters)param;
+ }
+ }
+
+ /**
+ * generate a signature for the given message using the key we were
+ * initialised with. For conventional GOST3410 the message should be a GOST3411
+ * hash of the message of interest.
+ *
+ * @param message the message that will be verified later.
+ */
+ public BigInteger[] generateSignature(
+ byte[] message)
+ {
+ byte[] mRev = new byte[message.length]; // conversion is little-endian
+ for (int i = 0; i != mRev.length; i++)
+ {
+ mRev[i] = message[mRev.length - 1 - i];
+ }
+
+ BigInteger e = new BigInteger(1, mRev);
+ BigInteger n = key.getParameters().getN();
+
+ BigInteger r = null;
+ BigInteger s = null;
+
+ do // generate s
+ {
+ BigInteger k = null;
+
+ do // generate r
+ {
+ do
+ {
+ k = new BigInteger(n.bitLength(), random);
+ }
+ while (k.equals(ECConstants.ZERO));
+
+ ECPoint p = key.getParameters().getG().multiply(k);
+
+ BigInteger x = p.getX().toBigInteger();
+
+ r = x.mod(n);
+ }
+ while (r.equals(ECConstants.ZERO));
+
+ BigInteger d = ((ECPrivateKeyParameters)key).getD();
+
+ s = (k.multiply(e)).add(d.multiply(r)).mod(n);
+ }
+ while (s.equals(ECConstants.ZERO));
+
+ BigInteger[] res = new BigInteger[2];
+
+ res[0] = r;
+ res[1] = s;
+
+ return res;
+ }
+
+ /**
+ * return true if the value r and s represent a GOST3410 signature for
+ * the passed in message (for standard GOST3410 the message should be
+ * a GOST3411 hash of the real message to be verified).
+ */
+ public boolean verifySignature(
+ byte[] message,
+ BigInteger r,
+ BigInteger s)
+ {
+ byte[] mRev = new byte[message.length]; // conversion is little-endian
+ for (int i = 0; i != mRev.length; i++)
+ {
+ mRev[i] = message[mRev.length - 1 - i];
+ }
+
+ BigInteger e = new BigInteger(1, mRev);
+ BigInteger n = key.getParameters().getN();
+
+ // r in the range [1,n-1]
+ if (r.compareTo(ECConstants.ONE) < 0 || r.compareTo(n) >= 0)
+ {
+ return false;
+ }
+
+ // s in the range [1,n-1]
+ if (s.compareTo(ECConstants.ONE) < 0 || s.compareTo(n) >= 0)
+ {
+ return false;
+ }
+
+ BigInteger v = e.modInverse(n);
+
+ BigInteger z1 = s.multiply(v).mod(n);
+ BigInteger z2 = (n.subtract(r)).multiply(v).mod(n);
+
+ ECPoint G = key.getParameters().getG(); // P
+ ECPoint Q = ((ECPublicKeyParameters)key).getQ();
+
+ ECPoint point = ECAlgorithms.sumOfTwoMultiplies(G, z1, Q, z2);
+
+ // components must be bogus.
+ if (point.isInfinity())
+ {
+ return false;
+ }
+
+ BigInteger R = point.getX().toBigInteger().mod(n);
+
+ return R.equals(r);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java b/core/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java
new file mode 100644
index 00000000..07e8ca7b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java
@@ -0,0 +1,188 @@
+package org.bouncycastle.crypto.signers;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
+import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import org.bouncycastle.crypto.params.ECKeyParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.math.ec.ECAlgorithms;
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECPoint;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/**
+ * EC-NR as described in IEEE 1363-2000
+ */
+public class ECNRSigner
+ implements DSA
+{
+ private boolean forSigning;
+ private ECKeyParameters key;
+ private SecureRandom random;
+
+ public void init(
+ boolean forSigning,
+ CipherParameters param)
+ {
+ this.forSigning = forSigning;
+
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.random = rParam.getRandom();
+ this.key = (ECPrivateKeyParameters)rParam.getParameters();
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ this.key = (ECPrivateKeyParameters)param;
+ }
+ }
+ else
+ {
+ this.key = (ECPublicKeyParameters)param;
+ }
+ }
+
+ // Section 7.2.5 ECSP-NR, pg 34
+ /**
+ * generate a signature for the given message using the key we were
+ * initialised with. Generally, the order of the curve should be at
+ * least as long as the hash of the message of interest, and with
+ * ECNR it *must* be at least as long.
+ *
+ * @param digest the digest to be signed.
+ * @exception DataLengthException if the digest is longer than the key allows
+ */
+ public BigInteger[] generateSignature(
+ byte[] digest)
+ {
+ if (! this.forSigning)
+ {
+ throw new IllegalStateException("not initialised for signing");
+ }
+
+ BigInteger n = ((ECPrivateKeyParameters)this.key).getParameters().getN();
+ int nBitLength = n.bitLength();
+
+ BigInteger e = new BigInteger(1, digest);
+ int eBitLength = e.bitLength();
+
+ ECPrivateKeyParameters privKey = (ECPrivateKeyParameters)key;
+
+ if (eBitLength > nBitLength)
+ {
+ throw new DataLengthException("input too large for ECNR key.");
+ }
+
+ BigInteger r = null;
+ BigInteger s = null;
+
+ AsymmetricCipherKeyPair tempPair;
+ do // generate r
+ {
+ // generate another, but very temporary, key pair using
+ // the same EC parameters
+ ECKeyPairGenerator keyGen = new ECKeyPairGenerator();
+
+ keyGen.init(new ECKeyGenerationParameters(privKey.getParameters(), this.random));
+
+ tempPair = keyGen.generateKeyPair();
+
+ // BigInteger Vx = tempPair.getPublic().getW().getAffineX();
+ ECPublicKeyParameters V = (ECPublicKeyParameters)tempPair.getPublic(); // get temp's public key
+ BigInteger Vx = V.getQ().getX().toBigInteger(); // get the point's x coordinate
+
+ r = Vx.add(e).mod(n);
+ }
+ while (r.equals(ECConstants.ZERO));
+
+ // generate s
+ BigInteger x = privKey.getD(); // private key value
+ BigInteger u = ((ECPrivateKeyParameters)tempPair.getPrivate()).getD(); // temp's private key value
+ s = u.subtract(r.multiply(x)).mod(n);
+
+ BigInteger[] res = new BigInteger[2];
+ res[0] = r;
+ res[1] = s;
+
+ return res;
+ }
+
+ // Section 7.2.6 ECVP-NR, pg 35
+ /**
+ * return true if the value r and s represent a signature for the
+ * message passed in. Generally, the order of the curve should be at
+ * least as long as the hash of the message of interest, and with
+ * ECNR, it *must* be at least as long. But just in case the signer
+ * applied mod(n) to the longer digest, this implementation will
+ * apply mod(n) during verification.
+ *
+ * @param digest the digest to be verified.
+ * @param r the r value of the signature.
+ * @param s the s value of the signature.
+ * @exception DataLengthException if the digest is longer than the key allows
+ */
+ public boolean verifySignature(
+ byte[] digest,
+ BigInteger r,
+ BigInteger s)
+ {
+ if (this.forSigning)
+ {
+ throw new IllegalStateException("not initialised for verifying");
+ }
+
+ ECPublicKeyParameters pubKey = (ECPublicKeyParameters)key;
+ BigInteger n = pubKey.getParameters().getN();
+ int nBitLength = n.bitLength();
+
+ BigInteger e = new BigInteger(1, digest);
+ int eBitLength = e.bitLength();
+
+ if (eBitLength > nBitLength)
+ {
+ throw new DataLengthException("input too large for ECNR key.");
+ }
+
+ // r in the range [1,n-1]
+ if (r.compareTo(ECConstants.ONE) < 0 || r.compareTo(n) >= 0)
+ {
+ return false;
+ }
+
+ // s in the range [0,n-1] NB: ECNR spec says 0
+ if (s.compareTo(ECConstants.ZERO) < 0 || s.compareTo(n) >= 0)
+ {
+ return false;
+ }
+
+ // compute P = sG + rW
+
+ ECPoint G = pubKey.getParameters().getG();
+ ECPoint W = pubKey.getQ();
+ // calculate P using Bouncy math
+ ECPoint P = ECAlgorithms.sumOfTwoMultiplies(G, s, W, r);
+
+ // components must be bogus.
+ if (P.isInfinity())
+ {
+ return false;
+ }
+
+ BigInteger x = P.getX().toBigInteger();
+ BigInteger t = r.subtract(x).mod(n);
+
+ return t.equals(e);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/GOST3410Signer.java b/core/src/main/java/org/bouncycastle/crypto/signers/GOST3410Signer.java
new file mode 100644
index 00000000..9fcc41b9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/signers/GOST3410Signer.java
@@ -0,0 +1,127 @@
+package org.bouncycastle.crypto.signers;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.params.*;
+
+import java.security.SecureRandom;
+import java.math.BigInteger;
+
+/**
+ * GOST R 34.10-94 Signature Algorithm
+ */
+public class GOST3410Signer
+ implements DSA
+{
+ GOST3410KeyParameters key;
+
+ SecureRandom random;
+
+ public void init(
+ boolean forSigning,
+ CipherParameters param)
+ {
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.random = rParam.getRandom();
+ this.key = (GOST3410PrivateKeyParameters)rParam.getParameters();
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ this.key = (GOST3410PrivateKeyParameters)param;
+ }
+ }
+ else
+ {
+ this.key = (GOST3410PublicKeyParameters)param;
+ }
+ }
+
+ /**
+ * generate a signature for the given message using the key we were
+ * initialised with. For conventional GOST3410 the message should be a GOST3411
+ * hash of the message of interest.
+ *
+ * @param message the message that will be verified later.
+ */
+ public BigInteger[] generateSignature(
+ byte[] message)
+ {
+ byte[] mRev = new byte[message.length]; // conversion is little-endian
+ for (int i = 0; i != mRev.length; i++)
+ {
+ mRev[i] = message[mRev.length - 1 - i];
+ }
+
+ BigInteger m = new BigInteger(1, mRev);
+ GOST3410Parameters params = key.getParameters();
+ BigInteger k;
+
+ do
+ {
+ k = new BigInteger(params.getQ().bitLength(), random);
+ }
+ while (k.compareTo(params.getQ()) >= 0);
+
+ BigInteger r = params.getA().modPow(k, params.getP()).mod(params.getQ());
+
+ BigInteger s = k.multiply(m).
+ add(((GOST3410PrivateKeyParameters)key).getX().multiply(r)).
+ mod(params.getQ());
+
+ BigInteger[] res = new BigInteger[2];
+
+ res[0] = r;
+ res[1] = s;
+
+ return res;
+ }
+
+ /**
+ * return true if the value r and s represent a GOST3410 signature for
+ * the passed in message for standard GOST3410 the message should be a
+ * GOST3411 hash of the real message to be verified.
+ */
+ public boolean verifySignature(
+ byte[] message,
+ BigInteger r,
+ BigInteger s)
+ {
+ byte[] mRev = new byte[message.length]; // conversion is little-endian
+ for (int i = 0; i != mRev.length; i++)
+ {
+ mRev[i] = message[mRev.length - 1 - i];
+ }
+
+ BigInteger m = new BigInteger(1, mRev);
+ GOST3410Parameters params = key.getParameters();
+ BigInteger zero = BigInteger.valueOf(0);
+
+ if (zero.compareTo(r) >= 0 || params.getQ().compareTo(r) <= 0)
+ {
+ return false;
+ }
+
+ if (zero.compareTo(s) >= 0 || params.getQ().compareTo(s) <= 0)
+ {
+ return false;
+ }
+
+ BigInteger v = m.modPow(params.getQ().subtract(new BigInteger("2")),params.getQ());
+
+ BigInteger z1 = s.multiply(v).mod(params.getQ());
+ BigInteger z2 = (params.getQ().subtract(r)).multiply(v).mod(params.getQ());
+
+ z1 = params.getA().modPow(z1, params.getP());
+ z2 = ((GOST3410PublicKeyParameters)key).getY().modPow(z2, params.getP());
+
+ BigInteger u = z1.multiply(z2).mod(params.getP()).mod(params.getQ());
+
+ return u.equals(r);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/GenericSigner.java b/core/src/main/java/org/bouncycastle/crypto/signers/GenericSigner.java
new file mode 100644
index 00000000..6819e141
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/signers/GenericSigner.java
@@ -0,0 +1,136 @@
+package org.bouncycastle.crypto.signers;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.util.Arrays;
+
+public class GenericSigner
+ implements Signer
+{
+ private final AsymmetricBlockCipher engine;
+ private final Digest digest;
+ private boolean forSigning;
+
+ public GenericSigner(
+ AsymmetricBlockCipher engine,
+ Digest digest)
+ {
+ this.engine = engine;
+ this.digest = digest;
+ }
+
+ /**
+ * initialise the signer for signing or verification.
+ *
+ * @param forSigning
+ * true if for signing, false otherwise
+ * @param parameters
+ * necessary parameters.
+ */
+ public void init(
+ boolean forSigning,
+ CipherParameters parameters)
+ {
+ this.forSigning = forSigning;
+ AsymmetricKeyParameter k;
+
+ if (parameters instanceof ParametersWithRandom)
+ {
+ k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).getParameters();
+ }
+ else
+ {
+ k = (AsymmetricKeyParameter)parameters;
+ }
+
+ if (forSigning && !k.isPrivate())
+ {
+ throw new IllegalArgumentException("signing requires private key");
+ }
+
+ if (!forSigning && k.isPrivate())
+ {
+ throw new IllegalArgumentException("verification requires public key");
+ }
+
+ reset();
+
+ engine.init(forSigning, parameters);
+ }
+
+ /**
+ * update the internal digest with the byte b
+ */
+ public void update(
+ byte input)
+ {
+ digest.update(input);
+ }
+
+ /**
+ * update the internal digest with the byte array in
+ */
+ public void update(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ digest.update(input, inOff, length);
+ }
+
+ /**
+ * Generate a signature for the message we've been loaded with using the key
+ * we were initialised with.
+ */
+ public byte[] generateSignature()
+ throws CryptoException, DataLengthException
+ {
+ if (!forSigning)
+ {
+ throw new IllegalStateException("GenericSigner not initialised for signature generation.");
+ }
+
+ byte[] hash = new byte[digest.getDigestSize()];
+ digest.doFinal(hash, 0);
+
+ return engine.processBlock(hash, 0, hash.length);
+ }
+
+ /**
+ * return true if the internal state represents the signature described in
+ * the passed in array.
+ */
+ public boolean verifySignature(
+ byte[] signature)
+ {
+ if (forSigning)
+ {
+ throw new IllegalStateException("GenericSigner not initialised for verification");
+ }
+
+ byte[] hash = new byte[digest.getDigestSize()];
+ digest.doFinal(hash, 0);
+
+ try
+ {
+ byte[] sig = engine.processBlock(signature, 0, signature.length);
+
+ return Arrays.constantTimeAreEqual(sig, hash);
+ }
+ catch (Exception e)
+ {
+ return false;
+ }
+ }
+
+ public void reset()
+ {
+ digest.reset();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2PSSSigner.java b/core/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2PSSSigner.java
new file mode 100644
index 00000000..e3dcc08e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2PSSSigner.java
@@ -0,0 +1,668 @@
+package org.bouncycastle.crypto.signers;
+
+import java.security.SecureRandom;
+import java.util.Hashtable;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.SignerWithRecovery;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.ParametersWithSalt;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Integers;
+
+/**
+ * ISO9796-2 - mechanism using a hash function with recovery (scheme 2 and 3).
+ * <p/>
+ * Note: the usual length for the salt is the length of the hash
+ * function used in bytes.
+ */
+public class ISO9796d2PSSSigner
+ implements SignerWithRecovery
+{
+ static final public int TRAILER_IMPLICIT = 0xBC;
+ static final public int TRAILER_RIPEMD160 = 0x31CC;
+ static final public int TRAILER_RIPEMD128 = 0x32CC;
+ static final public int TRAILER_SHA1 = 0x33CC;
+ static final public int TRAILER_SHA256 = 0x34CC;
+ static final public int TRAILER_SHA512 = 0x35CC;
+ static final public int TRAILER_SHA384 = 0x36CC;
+ static final public int TRAILER_WHIRLPOOL = 0x37CC;
+
+ private static Hashtable trailerMap = new Hashtable();
+
+ static
+ {
+ trailerMap.put("RIPEMD128", Integers.valueOf(TRAILER_RIPEMD128));
+ trailerMap.put("RIPEMD160", Integers.valueOf(TRAILER_RIPEMD160));
+
+ trailerMap.put("SHA-1", Integers.valueOf(TRAILER_SHA1));
+ trailerMap.put("SHA-256", Integers.valueOf(TRAILER_SHA256));
+ trailerMap.put("SHA-384", Integers.valueOf(TRAILER_SHA384));
+ trailerMap.put("SHA-512", Integers.valueOf(TRAILER_SHA512));
+
+ trailerMap.put("Whirlpool", Integers.valueOf(TRAILER_WHIRLPOOL));
+ }
+
+ private Digest digest;
+ private AsymmetricBlockCipher cipher;
+
+ private SecureRandom random;
+ private byte[] standardSalt;
+
+ private int hLen;
+ private int trailer;
+ private int keyBits;
+ private byte[] block;
+ private byte[] mBuf;
+ private int messageLength;
+ private int saltLength;
+ private boolean fullMessage;
+ private byte[] recoveredMessage;
+
+ private byte[] preSig;
+ private byte[] preBlock;
+ private int preMStart;
+ private int preTLength;
+
+ /**
+ * Generate a signer for the with either implicit or explicit trailers
+ * for ISO9796-2, scheme 2 or 3.
+ *
+ * @param cipher base cipher to use for signature creation/verification
+ * @param digest digest to use.
+ * @param saltLength length of salt in bytes.
+ * @param implicit whether or not the trailer is implicit or gives the hash.
+ */
+ public ISO9796d2PSSSigner(
+ AsymmetricBlockCipher cipher,
+ Digest digest,
+ int saltLength,
+ boolean implicit)
+ {
+ this.cipher = cipher;
+ this.digest = digest;
+ this.hLen = digest.getDigestSize();
+ this.saltLength = saltLength;
+
+ if (implicit)
+ {
+ trailer = TRAILER_IMPLICIT;
+ }
+ else
+ {
+ Integer trailerObj = (Integer)trailerMap.get(digest.getAlgorithmName());
+
+ if (trailerObj != null)
+ {
+ trailer = trailerObj.intValue();
+ }
+ else
+ {
+ throw new IllegalArgumentException("no valid trailer for digest");
+ }
+ }
+ }
+
+ /**
+ * Constructor for a signer with an explicit digest trailer.
+ *
+ * @param cipher cipher to use.
+ * @param digest digest to sign with.
+ * @param saltLength length of salt in bytes.
+ */
+ public ISO9796d2PSSSigner(
+ AsymmetricBlockCipher cipher,
+ Digest digest,
+ int saltLength)
+ {
+ this(cipher, digest, saltLength, false);
+ }
+
+ /**
+ * Initialise the signer.
+ *
+ * @param forSigning true if for signing, false if for verification.
+ * @param param parameters for signature generation/verification. If the
+ * parameters are for generation they should be a ParametersWithRandom,
+ * a ParametersWithSalt, or just an RSAKeyParameters object. If RSAKeyParameters
+ * are passed in a SecureRandom will be created.
+ * @throws IllegalArgumentException if wrong parameter type or a fixed
+ * salt is passed in which is the wrong length.
+ */
+ public void init(
+ boolean forSigning,
+ CipherParameters param)
+ {
+ RSAKeyParameters kParam;
+ int lengthOfSalt = saltLength;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom p = (ParametersWithRandom)param;
+
+ kParam = (RSAKeyParameters)p.getParameters();
+ if (forSigning)
+ {
+ random = p.getRandom();
+ }
+ }
+ else if (param instanceof ParametersWithSalt)
+ {
+ ParametersWithSalt p = (ParametersWithSalt)param;
+
+ kParam = (RSAKeyParameters)p.getParameters();
+ standardSalt = p.getSalt();
+ lengthOfSalt = standardSalt.length;
+ if (standardSalt.length != saltLength)
+ {
+ throw new IllegalArgumentException("Fixed salt is of wrong length");
+ }
+ }
+ else
+ {
+ kParam = (RSAKeyParameters)param;
+ if (forSigning)
+ {
+ random = new SecureRandom();
+ }
+ }
+
+ cipher.init(forSigning, kParam);
+
+ keyBits = kParam.getModulus().bitLength();
+
+ block = new byte[(keyBits + 7) / 8];
+
+ if (trailer == TRAILER_IMPLICIT)
+ {
+ mBuf = new byte[block.length - digest.getDigestSize() - lengthOfSalt - 1 - 1];
+ }
+ else
+ {
+ mBuf = new byte[block.length - digest.getDigestSize() - lengthOfSalt - 1 - 2];
+ }
+
+ reset();
+ }
+
+ /**
+ * compare two byte arrays - constant time
+ */
+ private boolean isSameAs(
+ byte[] a,
+ byte[] b)
+ {
+ boolean isOkay = true;
+
+ if (messageLength != b.length)
+ {
+ isOkay = false;
+ }
+
+ for (int i = 0; i != b.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ isOkay = false;
+ }
+ }
+
+ return isOkay;
+ }
+
+ /**
+ * clear possible sensitive data
+ */
+ private void clearBlock(
+ byte[] block)
+ {
+ for (int i = 0; i != block.length; i++)
+ {
+ block[i] = 0;
+ }
+ }
+
+ public void updateWithRecoveredMessage(byte[] signature)
+ throws InvalidCipherTextException
+ {
+ byte[] block = cipher.processBlock(signature, 0, signature.length);
+
+ //
+ // adjust block size for leading zeroes if necessary
+ //
+ if (block.length < (keyBits + 7) / 8)
+ {
+ byte[] tmp = new byte[(keyBits + 7) / 8];
+
+ System.arraycopy(block, 0, tmp, tmp.length - block.length, block.length);
+ clearBlock(block);
+ block = tmp;
+ }
+
+ int tLength;
+
+ if (((block[block.length - 1] & 0xFF) ^ 0xBC) == 0)
+ {
+ tLength = 1;
+ }
+ else
+ {
+ int sigTrail = ((block[block.length - 2] & 0xFF) << 8) | (block[block.length - 1] & 0xFF);
+
+ Integer trailerObj = (Integer)trailerMap.get(digest.getAlgorithmName());
+
+ if (trailerObj != null)
+ {
+ if (sigTrail != trailerObj.intValue())
+ {
+ throw new IllegalStateException("signer initialised with wrong digest for trailer " + sigTrail);
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("unrecognised hash in signature");
+ }
+
+ tLength = 2;
+ }
+
+ //
+ // calculate H(m2)
+ //
+ byte[] m2Hash = new byte[hLen];
+ digest.doFinal(m2Hash, 0);
+
+ //
+ // remove the mask
+ //
+ byte[] dbMask = maskGeneratorFunction1(block, block.length - hLen - tLength, hLen, block.length - hLen - tLength);
+ for (int i = 0; i != dbMask.length; i++)
+ {
+ block[i] ^= dbMask[i];
+ }
+
+ block[0] &= 0x7f;
+
+ //
+ // find out how much padding we've got
+ //
+ int mStart = 0;
+ for (; mStart != block.length; mStart++)
+ {
+ if (block[mStart] == 0x01)
+ {
+ break;
+ }
+ }
+
+ mStart++;
+
+ if (mStart >= block.length)
+ {
+ clearBlock(block);
+ }
+
+ fullMessage = (mStart > 1);
+
+ recoveredMessage = new byte[dbMask.length - mStart - saltLength];
+
+ System.arraycopy(block, mStart, recoveredMessage, 0, recoveredMessage.length);
+ System.arraycopy(recoveredMessage, 0, mBuf, 0, recoveredMessage.length);
+
+ preSig = signature;
+ preBlock = block;
+ preMStart = mStart;
+ preTLength = tLength;
+ }
+
+ /**
+ * update the internal digest with the byte b
+ */
+ public void update(
+ byte b)
+ {
+ if (preSig == null && messageLength < mBuf.length)
+ {
+ mBuf[messageLength++] = b;
+ }
+ else
+ {
+ digest.update(b);
+ }
+ }
+
+ /**
+ * update the internal digest with the byte array in
+ */
+ public void update(
+ byte[] in,
+ int off,
+ int len)
+ {
+ if (preSig == null)
+ {
+ while (len > 0 && messageLength < mBuf.length)
+ {
+ this.update(in[off]);
+ off++;
+ len--;
+ }
+ }
+
+ if (len > 0)
+ {
+ digest.update(in, off, len);
+ }
+ }
+
+ /**
+ * reset the internal state
+ */
+ public void reset()
+ {
+ digest.reset();
+ messageLength = 0;
+ if (mBuf != null)
+ {
+ clearBlock(mBuf);
+ }
+ if (recoveredMessage != null)
+ {
+ clearBlock(recoveredMessage);
+ recoveredMessage = null;
+ }
+ fullMessage = false;
+ if (preSig != null)
+ {
+ preSig = null;
+ clearBlock(preBlock);
+ preBlock = null;
+ }
+ }
+
+ /**
+ * generate a signature for the loaded message using the key we were
+ * initialised with.
+ */
+ public byte[] generateSignature()
+ throws CryptoException
+ {
+ int digSize = digest.getDigestSize();
+
+ byte[] m2Hash = new byte[digSize];
+
+ digest.doFinal(m2Hash, 0);
+
+ byte[] C = new byte[8];
+ LtoOSP(messageLength * 8, C);
+
+ digest.update(C, 0, C.length);
+
+ digest.update(mBuf, 0, messageLength);
+
+ digest.update(m2Hash, 0, m2Hash.length);
+
+ byte[] salt;
+
+ if (standardSalt != null)
+ {
+ salt = standardSalt;
+ }
+ else
+ {
+ salt = new byte[saltLength];
+ random.nextBytes(salt);
+ }
+
+ digest.update(salt, 0, salt.length);
+
+ byte[] hash = new byte[digest.getDigestSize()];
+
+ digest.doFinal(hash, 0);
+
+ int tLength = 2;
+ if (trailer == TRAILER_IMPLICIT)
+ {
+ tLength = 1;
+ }
+
+ int off = block.length - messageLength - salt.length - hLen - tLength - 1;
+
+ block[off] = 0x01;
+
+ System.arraycopy(mBuf, 0, block, off + 1, messageLength);
+ System.arraycopy(salt, 0, block, off + 1 + messageLength, salt.length);
+
+ byte[] dbMask = maskGeneratorFunction1(hash, 0, hash.length, block.length - hLen - tLength);
+ for (int i = 0; i != dbMask.length; i++)
+ {
+ block[i] ^= dbMask[i];
+ }
+
+ System.arraycopy(hash, 0, block, block.length - hLen - tLength, hLen);
+
+ if (trailer == TRAILER_IMPLICIT)
+ {
+ block[block.length - 1] = (byte)TRAILER_IMPLICIT;
+ }
+ else
+ {
+ block[block.length - 2] = (byte)(trailer >>> 8);
+ block[block.length - 1] = (byte)trailer;
+ }
+
+ block[0] &= 0x7f;
+
+ byte[] b = cipher.processBlock(block, 0, block.length);
+
+ clearBlock(mBuf);
+ clearBlock(block);
+ messageLength = 0;
+
+ return b;
+ }
+
+ /**
+ * return true if the signature represents a ISO9796-2 signature
+ * for the passed in message.
+ */
+ public boolean verifySignature(
+ byte[] signature)
+ {
+ //
+ // calculate H(m2)
+ //
+ byte[] m2Hash = new byte[hLen];
+ digest.doFinal(m2Hash, 0);
+
+ byte[] block;
+ int tLength;
+ int mStart = 0;
+
+ if (preSig == null)
+ {
+ try
+ {
+ updateWithRecoveredMessage(signature);
+ }
+ catch (Exception e)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (!Arrays.areEqual(preSig, signature))
+ {
+ throw new IllegalStateException("updateWithRecoveredMessage called on different signature");
+ }
+ }
+
+ block = preBlock;
+ mStart = preMStart;
+ tLength = preTLength;
+
+ preSig = null;
+ preBlock = null;
+
+ //
+ // check the hashes
+ //
+ byte[] C = new byte[8];
+ LtoOSP(recoveredMessage.length * 8, C);
+
+ digest.update(C, 0, C.length);
+
+ if (recoveredMessage.length != 0)
+ {
+ digest.update(recoveredMessage, 0, recoveredMessage.length);
+ }
+
+ digest.update(m2Hash, 0, m2Hash.length);
+
+ // Update for the salt
+ digest.update(block, mStart + recoveredMessage.length, saltLength);
+
+ byte[] hash = new byte[digest.getDigestSize()];
+ digest.doFinal(hash, 0);
+
+ int off = block.length - tLength - hash.length;
+
+ boolean isOkay = true;
+
+ for (int i = 0; i != hash.length; i++)
+ {
+ if (hash[i] != block[off + i])
+ {
+ isOkay = false;
+ }
+ }
+
+ clearBlock(block);
+ clearBlock(hash);
+
+ if (!isOkay)
+ {
+ fullMessage = false;
+ clearBlock(recoveredMessage);
+ return false;
+ }
+
+ //
+ // if they've input a message check what we've recovered against
+ // what was input.
+ //
+ if (messageLength != 0)
+ {
+ if (!isSameAs(mBuf, recoveredMessage))
+ {
+ clearBlock(mBuf);
+ return false;
+ }
+ messageLength = 0;
+ }
+
+ clearBlock(mBuf);
+ return true;
+ }
+
+ /**
+ * Return true if the full message was recoveredMessage.
+ *
+ * @return true on full message recovery, false otherwise, or if not sure.
+ * @see org.bouncycastle.crypto.SignerWithRecovery#hasFullMessage()
+ */
+ public boolean hasFullMessage()
+ {
+ return fullMessage;
+ }
+
+ /**
+ * Return a reference to the recoveredMessage message.
+ *
+ * @return the full/partial recoveredMessage message.
+ * @see org.bouncycastle.crypto.SignerWithRecovery#getRecoveredMessage()
+ */
+ public byte[] getRecoveredMessage()
+ {
+ return recoveredMessage;
+ }
+
+ /**
+ * int to octet string.
+ */
+ private void ItoOSP(
+ int i,
+ byte[] sp)
+ {
+ sp[0] = (byte)(i >>> 24);
+ sp[1] = (byte)(i >>> 16);
+ sp[2] = (byte)(i >>> 8);
+ sp[3] = (byte)(i >>> 0);
+ }
+
+ /**
+ * long to octet string.
+ */
+ private void LtoOSP(
+ long l,
+ byte[] sp)
+ {
+ sp[0] = (byte)(l >>> 56);
+ sp[1] = (byte)(l >>> 48);
+ sp[2] = (byte)(l >>> 40);
+ sp[3] = (byte)(l >>> 32);
+ sp[4] = (byte)(l >>> 24);
+ sp[5] = (byte)(l >>> 16);
+ sp[6] = (byte)(l >>> 8);
+ sp[7] = (byte)(l >>> 0);
+ }
+
+ /**
+ * mask generator function, as described in PKCS1v2.
+ */
+ private byte[] maskGeneratorFunction1(
+ byte[] Z,
+ int zOff,
+ int zLen,
+ int length)
+ {
+ byte[] mask = new byte[length];
+ byte[] hashBuf = new byte[hLen];
+ byte[] C = new byte[4];
+ int counter = 0;
+
+ digest.reset();
+
+ while (counter < (length / hLen))
+ {
+ ItoOSP(counter, C);
+
+ digest.update(Z, zOff, zLen);
+ digest.update(C, 0, C.length);
+ digest.doFinal(hashBuf, 0);
+
+ System.arraycopy(hashBuf, 0, mask, counter * hLen, hLen);
+
+ counter++;
+ }
+
+ if ((counter * hLen) < length)
+ {
+ ItoOSP(counter, C);
+
+ digest.update(Z, zOff, zLen);
+ digest.update(C, 0, C.length);
+ digest.doFinal(hashBuf, 0);
+
+ System.arraycopy(hashBuf, 0, mask, counter * hLen, mask.length - (counter * hLen));
+ }
+
+ return mask;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2Signer.java b/core/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2Signer.java
new file mode 100644
index 00000000..563fae63
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2Signer.java
@@ -0,0 +1,618 @@
+package org.bouncycastle.crypto.signers;
+
+import java.util.Hashtable;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.SignerWithRecovery;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Integers;
+
+/**
+ * ISO9796-2 - mechanism using a hash function with recovery (scheme 1)
+ */
+public class ISO9796d2Signer
+ implements SignerWithRecovery
+{
+ static final public int TRAILER_IMPLICIT = 0xBC;
+ static final public int TRAILER_RIPEMD160 = 0x31CC;
+ static final public int TRAILER_RIPEMD128 = 0x32CC;
+ static final public int TRAILER_SHA1 = 0x33CC;
+ static final public int TRAILER_SHA256 = 0x34CC;
+ static final public int TRAILER_SHA512 = 0x35CC;
+ static final public int TRAILER_SHA384 = 0x36CC;
+ static final public int TRAILER_WHIRLPOOL = 0x37CC;
+
+ private static Hashtable trailerMap = new Hashtable();
+
+ static
+ {
+ trailerMap.put("RIPEMD128", Integers.valueOf(TRAILER_RIPEMD128));
+ trailerMap.put("RIPEMD160", Integers.valueOf(TRAILER_RIPEMD160));
+
+ trailerMap.put("SHA-1", Integers.valueOf(TRAILER_SHA1));
+ trailerMap.put("SHA-256", Integers.valueOf(TRAILER_SHA256));
+ trailerMap.put("SHA-384", Integers.valueOf(TRAILER_SHA384));
+ trailerMap.put("SHA-512", Integers.valueOf(TRAILER_SHA512));
+
+ trailerMap.put("Whirlpool", Integers.valueOf(TRAILER_WHIRLPOOL));
+ }
+
+ private Digest digest;
+ private AsymmetricBlockCipher cipher;
+
+ private int trailer;
+ private int keyBits;
+ private byte[] block;
+ private byte[] mBuf;
+ private int messageLength;
+ private boolean fullMessage;
+ private byte[] recoveredMessage;
+
+ private byte[] preSig;
+ private byte[] preBlock;
+
+ /**
+ * Generate a signer for the with either implicit or explicit trailers
+ * for ISO9796-2.
+ *
+ * @param cipher base cipher to use for signature creation/verification
+ * @param digest digest to use.
+ * @param implicit whether or not the trailer is implicit or gives the hash.
+ */
+ public ISO9796d2Signer(
+ AsymmetricBlockCipher cipher,
+ Digest digest,
+ boolean implicit)
+ {
+ this.cipher = cipher;
+ this.digest = digest;
+
+ if (implicit)
+ {
+ trailer = TRAILER_IMPLICIT;
+ }
+ else
+ {
+ Integer trailerObj = (Integer)trailerMap.get(digest.getAlgorithmName());
+
+ if (trailerObj != null)
+ {
+ trailer = trailerObj.intValue();
+ }
+ else
+ {
+ throw new IllegalArgumentException("no valid trailer for digest");
+ }
+ }
+ }
+
+ /**
+ * Constructor for a signer with an explicit digest trailer.
+ *
+ * @param cipher cipher to use.
+ * @param digest digest to sign with.
+ */
+ public ISO9796d2Signer(
+ AsymmetricBlockCipher cipher,
+ Digest digest)
+ {
+ this(cipher, digest, false);
+ }
+
+ public void init(
+ boolean forSigning,
+ CipherParameters param)
+ {
+ RSAKeyParameters kParam = (RSAKeyParameters)param;
+
+ cipher.init(forSigning, kParam);
+
+ keyBits = kParam.getModulus().bitLength();
+
+ block = new byte[(keyBits + 7) / 8];
+
+ if (trailer == TRAILER_IMPLICIT)
+ {
+ mBuf = new byte[block.length - digest.getDigestSize() - 2];
+ }
+ else
+ {
+ mBuf = new byte[block.length - digest.getDigestSize() - 3];
+ }
+
+ reset();
+ }
+
+ /**
+ * compare two byte arrays - constant time
+ */
+ private boolean isSameAs(
+ byte[] a,
+ byte[] b)
+ {
+ boolean isOkay = true;
+
+ if (messageLength > mBuf.length)
+ {
+ if (mBuf.length > b.length)
+ {
+ isOkay = false;
+ }
+
+ for (int i = 0; i != mBuf.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ isOkay = false;
+ }
+ }
+ }
+ else
+ {
+ if (messageLength != b.length)
+ {
+ isOkay = false;
+ }
+
+ for (int i = 0; i != b.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ isOkay = false;
+ }
+ }
+ }
+
+ return isOkay;
+ }
+
+ /**
+ * clear possible sensitive data
+ */
+ private void clearBlock(
+ byte[] block)
+ {
+ for (int i = 0; i != block.length; i++)
+ {
+ block[i] = 0;
+ }
+ }
+
+ public void updateWithRecoveredMessage(byte[] signature)
+ throws InvalidCipherTextException
+ {
+ byte[] block = cipher.processBlock(signature, 0, signature.length);
+
+ if (((block[0] & 0xC0) ^ 0x40) != 0)
+ {
+ throw new InvalidCipherTextException("malformed signature");
+ }
+
+ if (((block[block.length - 1] & 0xF) ^ 0xC) != 0)
+ {
+ throw new InvalidCipherTextException("malformed signature");
+ }
+
+ int delta = 0;
+
+ if (((block[block.length - 1] & 0xFF) ^ 0xBC) == 0)
+ {
+ delta = 1;
+ }
+ else
+ {
+ int sigTrail = ((block[block.length - 2] & 0xFF) << 8) | (block[block.length - 1] & 0xFF);
+ Integer trailerObj = (Integer)trailerMap.get(digest.getAlgorithmName());
+
+ if (trailerObj != null)
+ {
+ if (sigTrail != trailerObj.intValue())
+ {
+ throw new IllegalStateException("signer initialised with wrong digest for trailer " + sigTrail);
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("unrecognised hash in signature");
+ }
+
+ delta = 2;
+ }
+
+ //
+ // find out how much padding we've got
+ //
+ int mStart = 0;
+
+ for (mStart = 0; mStart != block.length; mStart++)
+ {
+ if (((block[mStart] & 0x0f) ^ 0x0a) == 0)
+ {
+ break;
+ }
+ }
+
+ mStart++;
+
+ int off = block.length - delta - digest.getDigestSize();
+
+ //
+ // there must be at least one byte of message string
+ //
+ if ((off - mStart) <= 0)
+ {
+ throw new InvalidCipherTextException("malformed block");
+ }
+
+ //
+ // if we contain the whole message as well, check the hash of that.
+ //
+ if ((block[0] & 0x20) == 0)
+ {
+ fullMessage = true;
+
+ recoveredMessage = new byte[off - mStart];
+ System.arraycopy(block, mStart, recoveredMessage, 0, recoveredMessage.length);
+ }
+ else
+ {
+ fullMessage = false;
+
+ recoveredMessage = new byte[off - mStart];
+ System.arraycopy(block, mStart, recoveredMessage, 0, recoveredMessage.length);
+ }
+
+ preSig = signature;
+ preBlock = block;
+
+ digest.update(recoveredMessage, 0, recoveredMessage.length);
+ messageLength = recoveredMessage.length;
+ System.arraycopy(recoveredMessage, 0, mBuf, 0, recoveredMessage.length);
+ }
+
+ /**
+ * update the internal digest with the byte b
+ */
+ public void update(
+ byte b)
+ {
+ digest.update(b);
+
+ if (messageLength < mBuf.length)
+ {
+ mBuf[messageLength] = b;
+ }
+
+ messageLength++;
+ }
+
+ /**
+ * update the internal digest with the byte array in
+ */
+ public void update(
+ byte[] in,
+ int off,
+ int len)
+ {
+ while (len > 0 && messageLength < mBuf.length)
+ {
+ this.update(in[off]);
+ off++;
+ len--;
+ }
+
+ digest.update(in, off, len);
+ messageLength += len;
+ }
+
+ /**
+ * reset the internal state
+ */
+ public void reset()
+ {
+ digest.reset();
+ messageLength = 0;
+ clearBlock(mBuf);
+
+ if (recoveredMessage != null)
+ {
+ clearBlock(recoveredMessage);
+ }
+
+ recoveredMessage = null;
+ fullMessage = false;
+
+ if (preSig != null)
+ {
+ preSig = null;
+ clearBlock(preBlock);
+ preBlock = null;
+ }
+ }
+
+ /**
+ * generate a signature for the loaded message using the key we were
+ * initialised with.
+ */
+ public byte[] generateSignature()
+ throws CryptoException
+ {
+ int digSize = digest.getDigestSize();
+
+ int t = 0;
+ int delta = 0;
+
+ if (trailer == TRAILER_IMPLICIT)
+ {
+ t = 8;
+ delta = block.length - digSize - 1;
+ digest.doFinal(block, delta);
+ block[block.length - 1] = (byte)TRAILER_IMPLICIT;
+ }
+ else
+ {
+ t = 16;
+ delta = block.length - digSize - 2;
+ digest.doFinal(block, delta);
+ block[block.length - 2] = (byte)(trailer >>> 8);
+ block[block.length - 1] = (byte)trailer;
+ }
+
+ byte header = 0;
+ int x = (digSize + messageLength) * 8 + t + 4 - keyBits;
+
+ if (x > 0)
+ {
+ int mR = messageLength - ((x + 7) / 8);
+ header = 0x60;
+
+ delta -= mR;
+
+ System.arraycopy(mBuf, 0, block, delta, mR);
+ }
+ else
+ {
+ header = 0x40;
+ delta -= messageLength;
+
+ System.arraycopy(mBuf, 0, block, delta, messageLength);
+ }
+
+ if ((delta - 1) > 0)
+ {
+ for (int i = delta - 1; i != 0; i--)
+ {
+ block[i] = (byte)0xbb;
+ }
+ block[delta - 1] ^= (byte)0x01;
+ block[0] = (byte)0x0b;
+ block[0] |= header;
+ }
+ else
+ {
+ block[0] = (byte)0x0a;
+ block[0] |= header;
+ }
+
+ byte[] b = cipher.processBlock(block, 0, block.length);
+
+ clearBlock(mBuf);
+ clearBlock(block);
+
+ return b;
+ }
+
+ /**
+ * return true if the signature represents a ISO9796-2 signature
+ * for the passed in message.
+ */
+ public boolean verifySignature(
+ byte[] signature)
+ {
+ byte[] block = null;
+
+ if (preSig == null)
+ {
+ try
+ {
+ block = cipher.processBlock(signature, 0, signature.length);
+ }
+ catch (Exception e)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (!Arrays.areEqual(preSig, signature))
+ {
+ throw new IllegalStateException("updateWithRecoveredMessage called on different signature");
+ }
+
+ block = preBlock;
+
+ preSig = null;
+ preBlock = null;
+ }
+
+ if (((block[0] & 0xC0) ^ 0x40) != 0)
+ {
+ return returnFalse(block);
+ }
+
+ if (((block[block.length - 1] & 0xF) ^ 0xC) != 0)
+ {
+ return returnFalse(block);
+ }
+
+ int delta = 0;
+
+ if (((block[block.length - 1] & 0xFF) ^ 0xBC) == 0)
+ {
+ delta = 1;
+ }
+ else
+ {
+ int sigTrail = ((block[block.length - 2] & 0xFF) << 8) | (block[block.length - 1] & 0xFF);
+ Integer trailerObj = (Integer)trailerMap.get(digest.getAlgorithmName());
+
+ if (trailerObj != null)
+ {
+ if (sigTrail != trailerObj.intValue())
+ {
+ throw new IllegalStateException("signer initialised with wrong digest for trailer " + sigTrail);
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("unrecognised hash in signature");
+ }
+
+ delta = 2;
+ }
+
+ //
+ // find out how much padding we've got
+ //
+ int mStart = 0;
+
+ for (mStart = 0; mStart != block.length; mStart++)
+ {
+ if (((block[mStart] & 0x0f) ^ 0x0a) == 0)
+ {
+ break;
+ }
+ }
+
+ mStart++;
+
+ //
+ // check the hashes
+ //
+ byte[] hash = new byte[digest.getDigestSize()];
+
+ int off = block.length - delta - hash.length;
+
+ //
+ // there must be at least one byte of message string
+ //
+ if ((off - mStart) <= 0)
+ {
+ return returnFalse(block);
+ }
+
+ //
+ // if we contain the whole message as well, check the hash of that.
+ //
+ if ((block[0] & 0x20) == 0)
+ {
+ fullMessage = true;
+
+ // check right number of bytes passed in.
+ if (messageLength > off - mStart)
+ {
+ return returnFalse(block);
+ }
+
+ digest.reset();
+ digest.update(block, mStart, off - mStart);
+ digest.doFinal(hash, 0);
+
+ boolean isOkay = true;
+
+ for (int i = 0; i != hash.length; i++)
+ {
+ block[off + i] ^= hash[i];
+ if (block[off + i] != 0)
+ {
+ isOkay = false;
+ }
+ }
+
+ if (!isOkay)
+ {
+ return returnFalse(block);
+ }
+
+ recoveredMessage = new byte[off - mStart];
+ System.arraycopy(block, mStart, recoveredMessage, 0, recoveredMessage.length);
+ }
+ else
+ {
+ fullMessage = false;
+
+ digest.doFinal(hash, 0);
+
+ boolean isOkay = true;
+
+ for (int i = 0; i != hash.length; i++)
+ {
+ block[off + i] ^= hash[i];
+ if (block[off + i] != 0)
+ {
+ isOkay = false;
+ }
+ }
+
+ if (!isOkay)
+ {
+ return returnFalse(block);
+ }
+
+ recoveredMessage = new byte[off - mStart];
+ System.arraycopy(block, mStart, recoveredMessage, 0, recoveredMessage.length);
+ }
+
+ //
+ // if they've input a message check what we've recovered against
+ // what was input.
+ //
+ if (messageLength != 0)
+ {
+ if (!isSameAs(mBuf, recoveredMessage))
+ {
+ return returnFalse(block);
+ }
+ }
+
+ clearBlock(mBuf);
+ clearBlock(block);
+
+ return true;
+ }
+
+ private boolean returnFalse(byte[] block)
+ {
+ clearBlock(mBuf);
+ clearBlock(block);
+
+ return false;
+ }
+
+ /**
+ * Return true if the full message was recoveredMessage.
+ *
+ * @return true on full message recovery, false otherwise.
+ * @see org.bouncycastle.crypto.SignerWithRecovery#hasFullMessage()
+ */
+ public boolean hasFullMessage()
+ {
+ return fullMessage;
+ }
+
+ /**
+ * Return a reference to the recoveredMessage message.
+ *
+ * @return the full/partial recoveredMessage message.
+ * @see org.bouncycastle.crypto.SignerWithRecovery#getRecoveredMessage()
+ */
+ public byte[] getRecoveredMessage()
+ {
+ return recoveredMessage;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/PSSSigner.java b/core/src/main/java/org/bouncycastle/crypto/signers/PSSSigner.java
new file mode 100644
index 00000000..8c9eb940
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/signers/PSSSigner.java
@@ -0,0 +1,348 @@
+package org.bouncycastle.crypto.signers;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.RSABlindingParameters;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+
+/**
+ * RSA-PSS as described in PKCS# 1 v 2.1.
+ * <p>
+ * Note: the usual value for the salt length is the number of
+ * bytes in the hash function.
+ */
+public class PSSSigner
+ implements Signer
+{
+ static final public byte TRAILER_IMPLICIT = (byte)0xBC;
+
+ private Digest contentDigest;
+ private Digest mgfDigest;
+ private AsymmetricBlockCipher cipher;
+ private SecureRandom random;
+
+ private int hLen;
+ private int mgfhLen;
+ private int sLen;
+ private int emBits;
+ private byte[] salt;
+ private byte[] mDash;
+ private byte[] block;
+ private byte trailer;
+
+ /**
+ * basic constructor
+ *
+ * @param cipher the asymmetric cipher to use.
+ * @param digest the digest to use.
+ * @param sLen the length of the salt to use (in bytes).
+ */
+ public PSSSigner(
+ AsymmetricBlockCipher cipher,
+ Digest digest,
+ int sLen)
+ {
+ this(cipher, digest, sLen, TRAILER_IMPLICIT);
+ }
+
+ public PSSSigner(
+ AsymmetricBlockCipher cipher,
+ Digest contentDigest,
+ Digest mgfDigest,
+ int sLen)
+ {
+ this(cipher, contentDigest, mgfDigest, sLen, TRAILER_IMPLICIT);
+ }
+
+ public PSSSigner(
+ AsymmetricBlockCipher cipher,
+ Digest digest,
+ int sLen,
+ byte trailer)
+ {
+ this(cipher, digest, digest, sLen, trailer);
+ }
+
+ public PSSSigner(
+ AsymmetricBlockCipher cipher,
+ Digest contentDigest,
+ Digest mgfDigest,
+ int sLen,
+ byte trailer)
+ {
+ this.cipher = cipher;
+ this.contentDigest = contentDigest;
+ this.mgfDigest = mgfDigest;
+ this.hLen = contentDigest.getDigestSize();
+ this.mgfhLen = mgfDigest.getDigestSize();
+ this.sLen = sLen;
+ this.salt = new byte[sLen];
+ this.mDash = new byte[8 + sLen + hLen];
+ this.trailer = trailer;
+ }
+
+ public void init(
+ boolean forSigning,
+ CipherParameters param)
+ {
+ CipherParameters params;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom p = (ParametersWithRandom)param;
+
+ params = p.getParameters();
+ random = p.getRandom();
+ }
+ else
+ {
+ params = param;
+ if (forSigning)
+ {
+ random = new SecureRandom();
+ }
+ }
+
+ cipher.init(forSigning, params);
+
+ RSAKeyParameters kParam;
+
+ if (params instanceof RSABlindingParameters)
+ {
+ kParam = ((RSABlindingParameters)params).getPublicKey();
+ }
+ else
+ {
+ kParam = (RSAKeyParameters)params;
+ }
+
+ emBits = kParam.getModulus().bitLength() - 1;
+
+ if (emBits < (8 * hLen + 8 * sLen + 9))
+ {
+ throw new IllegalArgumentException("key too small for specified hash and salt lengths");
+ }
+
+ block = new byte[(emBits + 7) / 8];
+
+ reset();
+ }
+
+ /**
+ * clear possible sensitive data
+ */
+ private void clearBlock(
+ byte[] block)
+ {
+ for (int i = 0; i != block.length; i++)
+ {
+ block[i] = 0;
+ }
+ }
+
+ /**
+ * update the internal digest with the byte b
+ */
+ public void update(
+ byte b)
+ {
+ contentDigest.update(b);
+ }
+
+ /**
+ * update the internal digest with the byte array in
+ */
+ public void update(
+ byte[] in,
+ int off,
+ int len)
+ {
+ contentDigest.update(in, off, len);
+ }
+
+ /**
+ * reset the internal state
+ */
+ public void reset()
+ {
+ contentDigest.reset();
+ }
+
+ /**
+ * generate a signature for the message we've been loaded with using
+ * the key we were initialised with.
+ */
+ public byte[] generateSignature()
+ throws CryptoException, DataLengthException
+ {
+ contentDigest.doFinal(mDash, mDash.length - hLen - sLen);
+
+ if (sLen != 0)
+ {
+ random.nextBytes(salt);
+
+ System.arraycopy(salt, 0, mDash, mDash.length - sLen, sLen);
+ }
+
+ byte[] h = new byte[hLen];
+
+ contentDigest.update(mDash, 0, mDash.length);
+
+ contentDigest.doFinal(h, 0);
+
+ block[block.length - sLen - 1 - hLen - 1] = 0x01;
+ System.arraycopy(salt, 0, block, block.length - sLen - hLen - 1, sLen);
+
+ byte[] dbMask = maskGeneratorFunction1(h, 0, h.length, block.length - hLen - 1);
+ for (int i = 0; i != dbMask.length; i++)
+ {
+ block[i] ^= dbMask[i];
+ }
+
+ block[0] &= (0xff >> ((block.length * 8) - emBits));
+
+ System.arraycopy(h, 0, block, block.length - hLen - 1, hLen);
+
+ block[block.length - 1] = trailer;
+
+ byte[] b = cipher.processBlock(block, 0, block.length);
+
+ clearBlock(block);
+
+ return b;
+ }
+
+ /**
+ * return true if the internal state represents the signature described
+ * in the passed in array.
+ */
+ public boolean verifySignature(
+ byte[] signature)
+ {
+ contentDigest.doFinal(mDash, mDash.length - hLen - sLen);
+
+ try
+ {
+ byte[] b = cipher.processBlock(signature, 0, signature.length);
+ System.arraycopy(b, 0, block, block.length - b.length, b.length);
+ }
+ catch (Exception e)
+ {
+ return false;
+ }
+
+ if (block[block.length - 1] != trailer)
+ {
+ clearBlock(block);
+ return false;
+ }
+
+ byte[] dbMask = maskGeneratorFunction1(block, block.length - hLen - 1, hLen, block.length - hLen - 1);
+
+ for (int i = 0; i != dbMask.length; i++)
+ {
+ block[i] ^= dbMask[i];
+ }
+
+ block[0] &= (0xff >> ((block.length * 8) - emBits));
+
+ for (int i = 0; i != block.length - hLen - sLen - 2; i++)
+ {
+ if (block[i] != 0)
+ {
+ clearBlock(block);
+ return false;
+ }
+ }
+
+ if (block[block.length - hLen - sLen - 2] != 0x01)
+ {
+ clearBlock(block);
+ return false;
+ }
+
+ System.arraycopy(block, block.length - sLen - hLen - 1, mDash, mDash.length - sLen, sLen);
+
+ contentDigest.update(mDash, 0, mDash.length);
+ contentDigest.doFinal(mDash, mDash.length - hLen);
+
+ for (int i = block.length - hLen - 1, j = mDash.length - hLen;
+ j != mDash.length; i++, j++)
+ {
+ if ((block[i] ^ mDash[j]) != 0)
+ {
+ clearBlock(mDash);
+ clearBlock(block);
+ return false;
+ }
+ }
+
+ clearBlock(mDash);
+ clearBlock(block);
+
+ return true;
+ }
+
+ /**
+ * int to octet string.
+ */
+ private void ItoOSP(
+ int i,
+ byte[] sp)
+ {
+ sp[0] = (byte)(i >>> 24);
+ sp[1] = (byte)(i >>> 16);
+ sp[2] = (byte)(i >>> 8);
+ sp[3] = (byte)(i >>> 0);
+ }
+
+ /**
+ * mask generator function, as described in PKCS1v2.
+ */
+ private byte[] maskGeneratorFunction1(
+ byte[] Z,
+ int zOff,
+ int zLen,
+ int length)
+ {
+ byte[] mask = new byte[length];
+ byte[] hashBuf = new byte[mgfhLen];
+ byte[] C = new byte[4];
+ int counter = 0;
+
+ mgfDigest.reset();
+
+ while (counter < (length / mgfhLen))
+ {
+ ItoOSP(counter, C);
+
+ mgfDigest.update(Z, zOff, zLen);
+ mgfDigest.update(C, 0, C.length);
+ mgfDigest.doFinal(hashBuf, 0);
+
+ System.arraycopy(hashBuf, 0, mask, counter * mgfhLen, mgfhLen);
+
+ counter++;
+ }
+
+ if ((counter * mgfhLen) < length)
+ {
+ ItoOSP(counter, C);
+
+ mgfDigest.update(Z, zOff, zLen);
+ mgfDigest.update(C, 0, C.length);
+ mgfDigest.doFinal(hashBuf, 0);
+
+ System.arraycopy(hashBuf, 0, mask, counter * mgfhLen, mask.length - (counter * mgfhLen));
+ }
+
+ return mask;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java b/core/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java
new file mode 100644
index 00000000..f33ed317
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java
@@ -0,0 +1,232 @@
+package org.bouncycastle.crypto.signers;
+
+import java.io.IOException;
+import java.util.Hashtable;
+
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.DigestInfo;
+import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.encodings.PKCS1Encoding;
+import org.bouncycastle.crypto.engines.RSABlindedEngine;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.util.Arrays;
+
+public class RSADigestSigner
+ implements Signer
+{
+ private final AsymmetricBlockCipher rsaEngine = new PKCS1Encoding(new RSABlindedEngine());
+ private final AlgorithmIdentifier algId;
+ private final Digest digest;
+ private boolean forSigning;
+
+ private static final Hashtable oidMap = new Hashtable();
+
+ /*
+ * Load OID table.
+ */
+ static
+ {
+ oidMap.put("RIPEMD128", TeleTrusTObjectIdentifiers.ripemd128);
+ oidMap.put("RIPEMD160", TeleTrusTObjectIdentifiers.ripemd160);
+ oidMap.put("RIPEMD256", TeleTrusTObjectIdentifiers.ripemd256);
+
+ oidMap.put("SHA-1", X509ObjectIdentifiers.id_SHA1);
+ oidMap.put("SHA-224", NISTObjectIdentifiers.id_sha224);
+ oidMap.put("SHA-256", NISTObjectIdentifiers.id_sha256);
+ oidMap.put("SHA-384", NISTObjectIdentifiers.id_sha384);
+ oidMap.put("SHA-512", NISTObjectIdentifiers.id_sha512);
+
+ oidMap.put("MD2", PKCSObjectIdentifiers.md2);
+ oidMap.put("MD4", PKCSObjectIdentifiers.md4);
+ oidMap.put("MD5", PKCSObjectIdentifiers.md5);
+ }
+
+ public RSADigestSigner(
+ Digest digest)
+ {
+ this.digest = digest;
+
+ algId = new AlgorithmIdentifier((ASN1ObjectIdentifier)oidMap.get(digest.getAlgorithmName()), DERNull.INSTANCE);
+ }
+
+ /**
+ * @deprecated
+ */
+ public String getAlgorithmName()
+ {
+ return digest.getAlgorithmName() + "withRSA";
+ }
+
+ /**
+ * initialise the signer for signing or verification.
+ *
+ * @param forSigning
+ * true if for signing, false otherwise
+ * @param parameters
+ * necessary parameters.
+ */
+ public void init(
+ boolean forSigning,
+ CipherParameters parameters)
+ {
+ this.forSigning = forSigning;
+ AsymmetricKeyParameter k;
+
+ if (parameters instanceof ParametersWithRandom)
+ {
+ k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).getParameters();
+ }
+ else
+ {
+ k = (AsymmetricKeyParameter)parameters;
+ }
+
+ if (forSigning && !k.isPrivate())
+ {
+ throw new IllegalArgumentException("signing requires private key");
+ }
+
+ if (!forSigning && k.isPrivate())
+ {
+ throw new IllegalArgumentException("verification requires public key");
+ }
+
+ reset();
+
+ rsaEngine.init(forSigning, parameters);
+ }
+
+ /**
+ * update the internal digest with the byte b
+ */
+ public void update(
+ byte input)
+ {
+ digest.update(input);
+ }
+
+ /**
+ * update the internal digest with the byte array in
+ */
+ public void update(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ digest.update(input, inOff, length);
+ }
+
+ /**
+ * Generate a signature for the message we've been loaded with using the key
+ * we were initialised with.
+ */
+ public byte[] generateSignature()
+ throws CryptoException, DataLengthException
+ {
+ if (!forSigning)
+ {
+ throw new IllegalStateException("RSADigestSigner not initialised for signature generation.");
+ }
+
+ byte[] hash = new byte[digest.getDigestSize()];
+ digest.doFinal(hash, 0);
+
+ try
+ {
+ byte[] data = derEncode(hash);
+ return rsaEngine.processBlock(data, 0, data.length);
+ }
+ catch (IOException e)
+ {
+ throw new CryptoException("unable to encode signature: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * return true if the internal state represents the signature described in
+ * the passed in array.
+ */
+ public boolean verifySignature(
+ byte[] signature)
+ {
+ if (forSigning)
+ {
+ throw new IllegalStateException("RSADigestSigner not initialised for verification");
+ }
+
+ byte[] hash = new byte[digest.getDigestSize()];
+
+ digest.doFinal(hash, 0);
+
+ byte[] sig;
+ byte[] expected;
+
+ try
+ {
+ sig = rsaEngine.processBlock(signature, 0, signature.length);
+ expected = derEncode(hash);
+ }
+ catch (Exception e)
+ {
+ return false;
+ }
+
+ if (sig.length == expected.length)
+ {
+ return Arrays.constantTimeAreEqual(sig, expected);
+ }
+ else if (sig.length == expected.length - 2) // NULL left out
+ {
+ int sigOffset = sig.length - hash.length - 2;
+ int expectedOffset = expected.length - hash.length - 2;
+
+ expected[1] -= 2; // adjust lengths
+ expected[3] -= 2;
+
+ int nonEqual = 0;
+
+ for (int i = 0; i < hash.length; i++)
+ {
+ nonEqual |= (sig[sigOffset + i] ^ expected[expectedOffset + i]);
+ }
+
+ for (int i = 0; i < sigOffset; i++)
+ {
+ nonEqual |= (sig[i] ^ expected[i]); // check header less NULL
+ }
+
+ return nonEqual == 0;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public void reset()
+ {
+ digest.reset();
+ }
+
+ private byte[] derEncode(
+ byte[] hash)
+ throws IOException
+ {
+ DigestInfo dInfo = new DigestInfo(algId, hash);
+
+ return dInfo.getEncoded(ASN1Encoding.DER);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCipherFactory.java b/core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCipherFactory.java
new file mode 100644
index 00000000..9c2a526e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCipherFactory.java
@@ -0,0 +1,15 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+public class AbstractTlsCipherFactory
+ implements TlsCipherFactory
+{
+
+ public TlsCipher createCipher(TlsContext context, int encryptionAlgorithm, int macAlgorithm)
+ throws IOException
+ {
+
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java b/core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java
new file mode 100644
index 00000000..9e113f98
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java
@@ -0,0 +1,218 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Vector;
+
+public abstract class AbstractTlsClient
+ extends AbstractTlsPeer
+ implements TlsClient
+{
+
+ protected TlsCipherFactory cipherFactory;
+
+ protected TlsClientContext context;
+
+ protected Vector supportedSignatureAlgorithms;
+
+ protected int selectedCipherSuite;
+ protected short selectedCompressionMethod;
+
+ public AbstractTlsClient()
+ {
+ this(new DefaultTlsCipherFactory());
+ }
+
+ public AbstractTlsClient(TlsCipherFactory cipherFactory)
+ {
+ this.cipherFactory = cipherFactory;
+ }
+
+ public void init(TlsClientContext context)
+ {
+ this.context = context;
+ }
+
+ /**
+ * RFC 5246 E.1. "TLS clients that wish to negotiate with older servers MAY send any value
+ * {03,XX} as the record layer version number. Typical values would be {03,00}, the lowest
+ * version number supported by the client, and the value of ClientHello.client_version. No
+ * single value will guarantee interoperability with all old servers, but this is a complex
+ * topic beyond the scope of this document."
+ */
+ public ProtocolVersion getClientHelloRecordLayerVersion()
+ {
+ // "{03,00}"
+ // return ProtocolVersion.SSLv3;
+
+ // "the lowest version number supported by the client"
+ // return getMinimumServerVersion();
+
+ // "the value of ClientHello.client_version"
+ return getClientVersion();
+ }
+
+ public ProtocolVersion getClientVersion()
+ {
+ return ProtocolVersion.TLSv11;
+ }
+
+ public Hashtable getClientExtensions()
+ throws IOException
+ {
+
+ Hashtable clientExtensions = null;
+
+ ProtocolVersion clientVersion = context.getClientVersion();
+
+ /*
+ * RFC 5246 7.4.1.4.1. Note: this extension is not meaningful for TLS versions prior to 1.2.
+ * Clients MUST NOT offer it if they are offering prior versions.
+ */
+ if (TlsUtils.isSignatureAlgorithmsExtensionAllowed(clientVersion))
+ {
+
+ // TODO Provide a way for the user to specify the acceptable hash/signature algorithms.
+
+ short[] hashAlgorithms = new short[]{HashAlgorithm.sha512, HashAlgorithm.sha384, HashAlgorithm.sha256,
+ HashAlgorithm.sha224, HashAlgorithm.sha1};
+
+ // TODO Sort out ECDSA signatures and add them as the preferred option here
+ short[] signatureAlgorithms = new short[]{SignatureAlgorithm.rsa};
+
+ this.supportedSignatureAlgorithms = new Vector();
+ for (int i = 0; i < hashAlgorithms.length; ++i)
+ {
+ for (int j = 0; j < signatureAlgorithms.length; ++j)
+ {
+ this.supportedSignatureAlgorithms.addElement(new SignatureAndHashAlgorithm(hashAlgorithms[i],
+ signatureAlgorithms[j]));
+ }
+ }
+
+ /*
+ * RFC 5264 7.4.3. Currently, DSA [DSS] may only be used with SHA-1.
+ */
+ this.supportedSignatureAlgorithms.addElement(new SignatureAndHashAlgorithm(HashAlgorithm.sha1,
+ SignatureAlgorithm.dsa));
+
+ if (clientExtensions == null)
+ {
+ clientExtensions = new Hashtable();
+ }
+
+ TlsUtils.addSignatureAlgorithmsExtension(clientExtensions, supportedSignatureAlgorithms);
+ }
+
+ return clientExtensions;
+ }
+
+ public ProtocolVersion getMinimumVersion()
+ {
+ return ProtocolVersion.TLSv10;
+ }
+
+ public void notifyServerVersion(ProtocolVersion serverVersion)
+ throws IOException
+ {
+ if (!getMinimumVersion().isEqualOrEarlierVersionOf(serverVersion))
+ {
+ throw new TlsFatalAlert(AlertDescription.protocol_version);
+ }
+ }
+
+ public short[] getCompressionMethods()
+ {
+ return new short[]{CompressionMethod._null};
+ }
+
+ public void notifySessionID(byte[] sessionID)
+ {
+ // Currently ignored
+ }
+
+ public void notifySelectedCipherSuite(int selectedCipherSuite)
+ {
+ this.selectedCipherSuite = selectedCipherSuite;
+ }
+
+ public void notifySelectedCompressionMethod(short selectedCompressionMethod)
+ {
+ this.selectedCompressionMethod = selectedCompressionMethod;
+ }
+
+ public void notifySecureRenegotiation(boolean secureRenegotiation)
+ throws IOException
+ {
+ if (!secureRenegotiation)
+ {
+ /*
+ * RFC 5746 3.4. In this case, some clients may want to terminate the handshake instead
+ * of continuing; see Section 4.1 for discussion.
+ */
+ // throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ }
+
+ public void processServerExtensions(Hashtable serverExtensions)
+ throws IOException
+ {
+ /*
+ * TlsProtocol implementation validates that any server extensions received correspond to
+ * client extensions sent. By default, we don't send any, and this method is not called.
+ */
+ if (serverExtensions != null)
+ {
+ /*
+ * RFC 5246 7.4.1.4.1. Servers MUST NOT send this extension.
+ */
+ if (serverExtensions.containsKey(TlsUtils.EXT_signature_algorithms))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+ }
+
+ public void processServerSupplementalData(Vector serverSupplementalData)
+ throws IOException
+ {
+ if (serverSupplementalData != null)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+
+ public Vector getClientSupplementalData()
+ throws IOException
+ {
+ return null;
+ }
+
+ public TlsCompression getCompression()
+ throws IOException
+ {
+ switch (selectedCompressionMethod)
+ {
+ case CompressionMethod._null:
+ return new TlsNullCompression();
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected compression method was in the list of client-offered compression
+ * methods, so if we now can't produce an implementation, we shouldn't have offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public void notifyNewSessionTicket(NewSessionTicket newSessionTicket)
+ throws IOException
+ {
+ }
+
+ public void notifyHandshakeComplete()
+ throws IOException
+ {
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java b/core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java
new file mode 100644
index 00000000..1ff67e33
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java
@@ -0,0 +1,96 @@
+package org.bouncycastle.crypto.tls;
+
+import java.security.SecureRandom;
+
+abstract class AbstractTlsContext
+ implements TlsContext
+{
+
+ private SecureRandom secureRandom;
+ private SecurityParameters securityParameters;
+
+ private ProtocolVersion clientVersion = null;
+ private ProtocolVersion serverVersion = null;
+ private Object userObject = null;
+
+ AbstractTlsContext(SecureRandom secureRandom, SecurityParameters securityParameters)
+ {
+ this.secureRandom = secureRandom;
+ this.securityParameters = securityParameters;
+ }
+
+ public SecureRandom getSecureRandom()
+ {
+ return secureRandom;
+ }
+
+ public SecurityParameters getSecurityParameters()
+ {
+ return securityParameters;
+ }
+
+ public ProtocolVersion getClientVersion()
+ {
+ return clientVersion;
+ }
+
+ public void setClientVersion(ProtocolVersion clientVersion)
+ {
+ this.clientVersion = clientVersion;
+ }
+
+ public ProtocolVersion getServerVersion()
+ {
+ return serverVersion;
+ }
+
+ public void setServerVersion(ProtocolVersion serverVersion)
+ {
+ this.serverVersion = serverVersion;
+ }
+
+ public Object getUserObject()
+ {
+ return userObject;
+ }
+
+ public void setUserObject(Object userObject)
+ {
+ this.userObject = userObject;
+ }
+
+ public byte[] exportKeyingMaterial(String asciiLabel, byte[] context_value, int length)
+ {
+
+ SecurityParameters sp = getSecurityParameters();
+ byte[] cr = sp.getClientRandom(), sr = sp.getServerRandom();
+
+ int seedLength = cr.length + sr.length;
+ if (context_value != null)
+ {
+ seedLength += (2 + context_value.length);
+ }
+
+ byte[] seed = new byte[seedLength];
+ int seedPos = 0;
+
+ System.arraycopy(cr, 0, seed, seedPos, cr.length);
+ seedPos += cr.length;
+ System.arraycopy(sr, 0, seed, seedPos, sr.length);
+ seedPos += sr.length;
+ if (context_value != null)
+ {
+ TlsUtils.writeUint16(context_value.length, seed, seedPos);
+ seedPos += 2;
+ System.arraycopy(context_value, 0, seed, seedPos, context_value.length);
+ seedPos += context_value.length;
+ }
+
+ if (seedPos != seedLength)
+ {
+ throw new IllegalStateException("error in calculation of seed for export");
+ }
+
+ return TlsUtils.PRF(this, sp.getMasterSecret(), asciiLabel, seed, length);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsKeyExchange.java b/core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsKeyExchange.java
new file mode 100644
index 00000000..85057c13
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsKeyExchange.java
@@ -0,0 +1,164 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Vector;
+
+public abstract class AbstractTlsKeyExchange
+ implements TlsKeyExchange
+{
+
+ protected int keyExchange;
+ protected Vector supportedSignatureAlgorithms;
+
+ protected TlsContext context;
+
+ protected AbstractTlsKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms)
+ {
+ this.keyExchange = keyExchange;
+ this.supportedSignatureAlgorithms = supportedSignatureAlgorithms;
+ }
+
+ public void init(TlsContext context)
+ {
+ this.context = context;
+
+ ProtocolVersion clientVersion = context.getClientVersion();
+
+ if (TlsUtils.isSignatureAlgorithmsExtensionAllowed(clientVersion))
+ {
+
+ /*
+ * RFC 5264 7.4.1.4.1. If the client does not send the signature_algorithms extension,
+ * the server MUST do the following:
+ *
+ * - If the negotiated key exchange algorithm is one of (RSA, DHE_RSA, DH_RSA, RSA_PSK,
+ * ECDH_RSA, ECDHE_RSA), behave as if client had sent the value {sha1,rsa}.
+ *
+ * - If the negotiated key exchange algorithm is one of (DHE_DSS, DH_DSS), behave as if
+ * the client had sent the value {sha1,dsa}.
+ *
+ * - If the negotiated key exchange algorithm is one of (ECDH_ECDSA, ECDHE_ECDSA),
+ * behave as if the client had sent value {sha1,ecdsa}.
+ */
+ if (this.supportedSignatureAlgorithms == null)
+ {
+ switch (keyExchange)
+ {
+
+ case KeyExchangeAlgorithm.DH_DSS:
+ case KeyExchangeAlgorithm.DHE_DSS:
+ case KeyExchangeAlgorithm.SRP_DSS:
+ {
+ this.supportedSignatureAlgorithms = TlsUtils.getDefaultDSSSignatureAlgorithms();
+ break;
+ }
+
+ case KeyExchangeAlgorithm.ECDH_ECDSA:
+ case KeyExchangeAlgorithm.ECDHE_ECDSA:
+ {
+ this.supportedSignatureAlgorithms = TlsUtils.getDefaultECDSASignatureAlgorithms();
+ break;
+ }
+
+ case KeyExchangeAlgorithm.DH_RSA:
+ case KeyExchangeAlgorithm.DHE_RSA:
+ case KeyExchangeAlgorithm.ECDH_RSA:
+ case KeyExchangeAlgorithm.ECDHE_RSA:
+ case KeyExchangeAlgorithm.RSA:
+ case KeyExchangeAlgorithm.RSA_PSK:
+ case KeyExchangeAlgorithm.SRP_RSA:
+ {
+ this.supportedSignatureAlgorithms = TlsUtils.getDefaultRSASignatureAlgorithms();
+ break;
+ }
+
+ default:
+ throw new IllegalStateException("unsupported key exchange algorithm");
+ }
+ }
+
+ }
+ else if (this.supportedSignatureAlgorithms != null)
+ {
+ throw new IllegalStateException("supported_signature_algorithms not allowed for " + clientVersion);
+ }
+ }
+
+ public void processServerCertificate(Certificate serverCertificate)
+ throws IOException
+ {
+
+ if (supportedSignatureAlgorithms == null)
+ {
+ /*
+ * TODO RFC 2264 7.4.2. Unless otherwise specified, the signing algorithm for the
+ * certificate must be the same as the algorithm for the certificate key.
+ */
+ }
+ else
+ {
+ /*
+ * TODO RFC 5264 7.4.2. If the client provided a "signature_algorithms" extension, then
+ * all certificates provided by the server MUST be signed by a hash/signature algorithm
+ * pair that appears in that extension.
+ */
+ }
+ }
+
+ public void processServerCredentials(TlsCredentials serverCredentials)
+ throws IOException
+ {
+ processServerCertificate(serverCredentials.getCertificate());
+ }
+
+ public boolean requiresServerKeyExchange()
+ {
+ return false;
+ }
+
+ public byte[] generateServerKeyExchange()
+ throws IOException
+ {
+ if (requiresServerKeyExchange())
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ return null;
+ }
+
+ public void skipServerKeyExchange()
+ throws IOException
+ {
+ if (requiresServerKeyExchange())
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+
+ public void processServerKeyExchange(InputStream input)
+ throws IOException
+ {
+ if (!requiresServerKeyExchange())
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+
+ public void skipClientCredentials()
+ throws IOException
+ {
+ }
+
+ public void processClientCertificate(Certificate clientCertificate)
+ throws IOException
+ {
+ }
+
+ public void processClientKeyExchange(InputStream input)
+ throws IOException
+ {
+ // Key exchange implementation MUST support client key exchange
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java b/core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java
new file mode 100644
index 00000000..bdfd0d5e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java
@@ -0,0 +1,14 @@
+package org.bouncycastle.crypto.tls;
+
+public abstract class AbstractTlsPeer
+ implements TlsPeer
+{
+
+ public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Exception cause)
+ {
+ }
+
+ public void notifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsServer.java b/core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsServer.java
new file mode 100644
index 00000000..8235fd1b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsServer.java
@@ -0,0 +1,304 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Vector;
+
+public abstract class AbstractTlsServer
+ extends AbstractTlsPeer
+ implements TlsServer
+{
+
+ protected TlsCipherFactory cipherFactory;
+
+ protected TlsServerContext context;
+
+ protected ProtocolVersion clientVersion;
+ protected int[] offeredCipherSuites;
+ protected short[] offeredCompressionMethods;
+ protected Hashtable clientExtensions;
+
+ protected Vector supportedSignatureAlgorithms;
+ protected boolean eccCipherSuitesOffered;
+ protected int[] namedCurves;
+ protected short[] clientECPointFormats, serverECPointFormats;
+
+ protected ProtocolVersion serverVersion;
+ protected int selectedCipherSuite;
+ protected short selectedCompressionMethod;
+ protected Hashtable serverExtensions;
+
+ public AbstractTlsServer()
+ {
+ this(new DefaultTlsCipherFactory());
+ }
+
+ public AbstractTlsServer(TlsCipherFactory cipherFactory)
+ {
+ this.cipherFactory = cipherFactory;
+ }
+
+ protected abstract int[] getCipherSuites();
+
+ protected short[] getCompressionMethods()
+ {
+ return new short[]{CompressionMethod._null};
+ }
+
+ protected ProtocolVersion getMaximumVersion()
+ {
+ return ProtocolVersion.TLSv11;
+ }
+
+ protected ProtocolVersion getMinimumVersion()
+ {
+ return ProtocolVersion.TLSv10;
+ }
+
+ protected boolean supportsClientECCCapabilities(int[] namedCurves, short[] ecPointFormats)
+ {
+
+ // NOTE: BC supports all the current set of point formats so we don't check them here
+
+ if (namedCurves == null)
+ {
+ /*
+ * RFC 4492 4. A client that proposes ECC cipher suites may choose not to include these
+ * extensions. In this case, the server is free to choose any one of the elliptic curves
+ * or point formats [...].
+ */
+ return TlsECCUtils.hasAnySupportedNamedCurves();
+ }
+
+ for (int i = 0; i < namedCurves.length; ++i)
+ {
+ int namedCurve = namedCurves[i];
+ if (!NamedCurve.refersToASpecificNamedCurve(namedCurve) || TlsECCUtils.isSupportedNamedCurve(namedCurve))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public void init(TlsServerContext context)
+ {
+ this.context = context;
+ }
+
+ public void notifyClientVersion(ProtocolVersion clientVersion)
+ throws IOException
+ {
+ this.clientVersion = clientVersion;
+ }
+
+ public void notifyOfferedCipherSuites(int[] offeredCipherSuites)
+ throws IOException
+ {
+ this.offeredCipherSuites = offeredCipherSuites;
+ this.eccCipherSuitesOffered = TlsECCUtils.containsECCCipherSuites(this.offeredCipherSuites);
+ }
+
+ public void notifyOfferedCompressionMethods(short[] offeredCompressionMethods)
+ throws IOException
+ {
+ this.offeredCompressionMethods = offeredCompressionMethods;
+ }
+
+ public void notifySecureRenegotiation(boolean secureRenegotiation)
+ throws IOException
+ {
+ if (!secureRenegotiation)
+ {
+ /*
+ * RFC 5746 3.6. In this case, some servers may want to terminate the handshake instead
+ * of continuing; see Section 4.3 for discussion.
+ */
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ }
+
+ public void processClientExtensions(Hashtable clientExtensions)
+ throws IOException
+ {
+
+ this.clientExtensions = clientExtensions;
+
+ if (clientExtensions != null)
+ {
+
+ this.supportedSignatureAlgorithms = TlsUtils.getSignatureAlgorithmsExtension(clientExtensions);
+ if (this.supportedSignatureAlgorithms != null)
+ {
+ /*
+ * RFC 5246 7.4.1.4.1. Note: this extension is not meaningful for TLS versions prior
+ * to 1.2. Clients MUST NOT offer it if they are offering prior versions.
+ */
+ if (!TlsUtils.isSignatureAlgorithmsExtensionAllowed(clientVersion))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+
+ this.namedCurves = TlsECCUtils.getSupportedEllipticCurvesExtension(clientExtensions);
+ this.clientECPointFormats = TlsECCUtils.getSupportedPointFormatsExtension(clientExtensions);
+ }
+
+ /*
+ * RFC 4429 4. The client MUST NOT include these extensions in the ClientHello message if it
+ * does not propose any ECC cipher suites.
+ */
+ if (!this.eccCipherSuitesOffered && (this.namedCurves != null || this.clientECPointFormats != null))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+
+ public ProtocolVersion getServerVersion()
+ throws IOException
+ {
+ if (getMinimumVersion().isEqualOrEarlierVersionOf(clientVersion))
+ {
+ ProtocolVersion maximumVersion = getMaximumVersion();
+ if (clientVersion.isEqualOrEarlierVersionOf(maximumVersion))
+ {
+ return serverVersion = clientVersion;
+ }
+ if (clientVersion.isLaterVersionOf(maximumVersion))
+ {
+ return serverVersion = maximumVersion;
+ }
+ }
+ throw new TlsFatalAlert(AlertDescription.protocol_version);
+ }
+
+ public int getSelectedCipherSuite()
+ throws IOException
+ {
+
+ /*
+ * TODO RFC 5246 7.4.3. In order to negotiate correctly, the server MUST check any candidate
+ * cipher suites against the "signature_algorithms" extension before selecting them. This is
+ * somewhat inelegant but is a compromise designed to minimize changes to the original
+ * cipher suite design.
+ */
+
+ /*
+ * RFC 4429 5.1. A server that receives a ClientHello containing one or both of these
+ * extensions MUST use the client's enumerated capabilities to guide its selection of an
+ * appropriate cipher suite. One of the proposed ECC cipher suites must be negotiated only
+ * if the server can successfully complete the handshake while using the curves and point
+ * formats supported by the client [...].
+ */
+ boolean eccCipherSuitesEnabled = supportsClientECCCapabilities(this.namedCurves, this.clientECPointFormats);
+
+ int[] cipherSuites = getCipherSuites();
+ for (int i = 0; i < cipherSuites.length; ++i)
+ {
+ int cipherSuite = cipherSuites[i];
+ if (TlsProtocol.arrayContains(this.offeredCipherSuites, cipherSuite)
+ && (eccCipherSuitesEnabled || !TlsECCUtils.isECCCipherSuite(cipherSuite)))
+ {
+ return this.selectedCipherSuite = cipherSuite;
+ }
+ }
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+
+ public short getSelectedCompressionMethod()
+ throws IOException
+ {
+ short[] compressionMethods = getCompressionMethods();
+ for (int i = 0; i < compressionMethods.length; ++i)
+ {
+ if (TlsProtocol.arrayContains(offeredCompressionMethods, compressionMethods[i]))
+ {
+ return this.selectedCompressionMethod = compressionMethods[i];
+ }
+ }
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+
+ // Hashtable is (Integer -> byte[])
+ public Hashtable getServerExtensions()
+ throws IOException
+ {
+
+ if (this.clientECPointFormats != null && TlsECCUtils.isECCCipherSuite(this.selectedCipherSuite))
+ {
+ /*
+ * RFC 4492 5.2. A server that selects an ECC cipher suite in response to a ClientHello
+ * message including a Supported Point Formats Extension appends this extension (along
+ * with others) to its ServerHello message, enumerating the point formats it can parse.
+ */
+ this.serverECPointFormats = new short[]{ECPointFormat.ansiX962_compressed_char2,
+ ECPointFormat.ansiX962_compressed_prime, ECPointFormat.uncompressed};
+
+ this.serverExtensions = new Hashtable();
+ TlsECCUtils.addSupportedPointFormatsExtension(serverExtensions, serverECPointFormats);
+ return serverExtensions;
+ }
+
+ return null;
+ }
+
+ public Vector getServerSupplementalData()
+ throws IOException
+ {
+ return null;
+ }
+
+ public CertificateRequest getCertificateRequest()
+ {
+ return null;
+ }
+
+ public void processClientSupplementalData(Vector clientSupplementalData)
+ throws IOException
+ {
+ if (clientSupplementalData != null)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+
+ public void notifyClientCertificate(Certificate clientCertificate)
+ throws IOException
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public TlsCompression getCompression()
+ throws IOException
+ {
+ switch (selectedCompressionMethod)
+ {
+ case CompressionMethod._null:
+ return new TlsNullCompression();
+
+ default:
+ /*
+ * Note: internal error here; we selected the compression method, so if we now can't
+ * produce an implementation, we shouldn't have chosen it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public NewSessionTicket getNewSessionTicket()
+ throws IOException
+ {
+ /*
+ * RFC 5077 3.3. If the server determines that it does not want to include a ticket after it
+ * has included the SessionTicket extension in the ServerHello, then it sends a zero-length
+ * ticket in the NewSessionTicket handshake message.
+ */
+ return new NewSessionTicket(0L, TlsUtils.EMPTY_BYTES);
+ }
+
+ public void notifyHandshakeComplete()
+ throws IOException
+ {
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSigner.java b/core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSigner.java
new file mode 100644
index 00000000..a0c24c73
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSigner.java
@@ -0,0 +1,13 @@
+package org.bouncycastle.crypto.tls;
+
+public abstract class AbstractTlsSigner
+ implements TlsSigner
+{
+
+ protected TlsContext context;
+
+ public void init(TlsContext context)
+ {
+ this.context = context;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/AlertDescription.java b/core/src/main/java/org/bouncycastle/crypto/tls/AlertDescription.java
new file mode 100644
index 00000000..5e3269bd
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/AlertDescription.java
@@ -0,0 +1,215 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 5246 7.2.
+ */
+public class AlertDescription
+{
+
+ /**
+ * This message notifies the recipient that the sender will not send any more messages on this
+ * connection. The session becomes unresumable if any connection is terminated without proper
+ * close_notify messages with level equal to warning.
+ */
+ public static final short close_notify = 0;
+
+ /**
+ * An inappropriate message was received. This alert is always fatal and should never be
+ * observed in communication between proper implementations.
+ */
+ public static final short unexpected_message = 10;
+
+ /**
+ * This alert is returned if a record is received with an incorrect MAC. This alert also MUST be
+ * returned if an alert is sent because a TLSCiphertext decrypted in an invalid way: either it
+ * wasn't an even multiple of the block length, or its padding values, when checked, weren't
+ * correct. This message is always fatal and should never be observed in communication between
+ * proper implementations (except when messages were corrupted in the network).
+ */
+ public static final short bad_record_mac = 20;
+
+ /**
+ * This alert was used in some earlier versions of TLS, and may have permitted certain attacks
+ * against the CBC mode [CBCATT]. It MUST NOT be sent by compliant implementations.
+ */
+ public static final short decryption_failed = 21;
+
+ /**
+ * A TLSCiphertext record was received that had a length more than 2^14+2048 bytes, or a record
+ * decrypted to a TLSCompressed record with more than 2^14+1024 bytes. This message is always
+ * fatal and should never be observed in communication between proper implementations (except
+ * when messages were corrupted in the network).
+ */
+ public static final short record_overflow = 22;
+
+ /**
+ * The decompression function received improper input (e.g., data that would expand to excessive
+ * length). This message is always fatal and should never be observed in communication between
+ * proper implementations.
+ */
+ public static final short decompression_failure = 30;
+
+ /**
+ * Reception of a handshake_failure alert message indicates that the sender was unable to
+ * negotiate an acceptable set of security parameters given the options available. This is a
+ * fatal error.
+ */
+ public static final short handshake_failure = 40;
+
+ /**
+ * This alert was used in SSLv3 but not any version of TLS. It MUST NOT be sent by compliant
+ * implementations.
+ */
+ public static final short no_certificate = 41;
+
+ /**
+ * A certificate was corrupt, contained signatures that did not verify correctly, etc.
+ */
+ public static final short bad_certificate = 42;
+
+ /**
+ * A certificate was of an unsupported type.
+ */
+ public static final short unsupported_certificate = 43;
+
+ /**
+ * A certificate was revoked by its signer.
+ */
+ public static final short certificate_revoked = 44;
+
+ /**
+ * A certificate has expired or is not currently valid.
+ */
+ public static final short certificate_expired = 45;
+
+ /**
+ * Some other (unspecified) issue arose in processing the certificate, rendering it
+ * unacceptable.
+ */
+ public static final short certificate_unknown = 46;
+
+ /**
+ * A field in the handshake was out of range or inconsistent with other fields. This message is
+ * always fatal.
+ */
+ public static final short illegal_parameter = 47;
+
+ /**
+ * A valid certificate chain or partial chain was received, but the certificate was not accepted
+ * because the CA certificate could not be located or couldn't be matched with a known, trusted
+ * CA. This message is always fatal.
+ */
+ public static final short unknown_ca = 48;
+
+ /**
+ * A valid certificate was received, but when access control was applied, the sender decided not
+ * to proceed with negotiation. This message is always fatal.
+ */
+ public static final short access_denied = 49;
+
+ /**
+ * A message could not be decoded because some field was out of the specified range or the
+ * length of the message was incorrect. This message is always fatal and should never be
+ * observed in communication between proper implementations (except when messages were corrupted
+ * in the network).
+ */
+ public static final short decode_error = 50;
+
+ /**
+ * A handshake cryptographic operation failed, including being unable to correctly verify a
+ * signature or validate a Finished message. This message is always fatal.
+ */
+ public static final short decrypt_error = 51;
+
+ /**
+ * This alert was used in some earlier versions of TLS. It MUST NOT be sent by compliant
+ * implementations.
+ */
+ public static final short export_restriction = 60;
+
+ /**
+ * The protocol version the client has attempted to negotiate is recognized but not supported.
+ * (For example, old protocol versions might be avoided for security reasons.) This message is
+ * always fatal.
+ */
+ public static final short protocol_version = 70;
+
+ /**
+ * Returned instead of handshake_failure when a negotiation has failed specifically because the
+ * server requires ciphers more secure than those supported by the client. This message is
+ * always fatal.
+ */
+ public static final short insufficient_security = 71;
+
+ /**
+ * An internal error unrelated to the peer or the correctness of the protocol (such as a memory
+ * allocation failure) makes it impossible to continue. This message is always fatal.
+ */
+ public static final short internal_error = 80;
+
+ /**
+ * This handshake is being canceled for some reason unrelated to a protocol failure. If the user
+ * cancels an operation after the handshake is complete, just closing the connection by sending
+ * a close_notify is more appropriate. This alert should be followed by a close_notify. This
+ * message is generally a warning.
+ */
+ public static final short user_canceled = 90;
+
+ /**
+ * Sent by the client in response to a hello request or by the server in response to a client
+ * hello after initial handshaking. Either of these would normally lead to renegotiation; when
+ * that is not appropriate, the recipient should respond with this alert. At that point, the
+ * original requester can decide whether to proceed with the connection. One case where this
+ * would be appropriate is where a server has spawned a process to satisfy a request; the
+ * process might receive security parameters (key length, authentication, etc.) at startup, and
+ * it might be difficult to communicate changes to these parameters after that point. This
+ * message is always a warning.
+ */
+ public static final short no_renegotiation = 100;
+
+ /**
+ * Sent by clients that receive an extended server hello containing an extension that they did
+ * not put in the corresponding client hello. This message is always fatal.
+ */
+ public static final short unsupported_extension = 110;
+
+ /*
+ * RFC 3546
+ */
+
+ /**
+ * This alert is sent by servers who are unable to retrieve a certificate chain from the URL
+ * supplied by the client (see Section 3.3). This message MAY be fatal - for example if client
+ * authentication is required by the server for the handshake to continue and the server is
+ * unable to retrieve the certificate chain, it may send a fatal alert.
+ */
+ public static final short certificate_unobtainable = 111;
+
+ /**
+ * This alert is sent by servers that receive a server_name extension request, but do not
+ * recognize the server name. This message MAY be fatal.
+ */
+ public static final short unrecognized_name = 112;
+
+ /**
+ * This alert is sent by clients that receive an invalid certificate status response (see
+ * Section 3.6). This message is always fatal.
+ */
+ public static final short bad_certificate_status_response = 113;
+
+ /**
+ * This alert is sent by servers when a certificate hash does not match a client provided
+ * certificate_hash. This message is always fatal.
+ */
+ public static final short bad_certificate_hash_value = 114;
+
+ /*
+ * RFC 4279
+ */
+
+ /**
+ * If the server does not recognize the PSK identity, it MAY respond with an
+ * "unknown_psk_identity" alert message.
+ */
+ public static final short unknown_psk_identity = 115;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/AlertLevel.java b/core/src/main/java/org/bouncycastle/crypto/tls/AlertLevel.java
new file mode 100644
index 00000000..b0b131d6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/AlertLevel.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 2246 7.2
+ */
+public class AlertLevel
+{
+ public static final short warning = 1;
+ public static final short fatal = 2;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/AlwaysValidVerifyer.java b/core/src/main/java/org/bouncycastle/crypto/tls/AlwaysValidVerifyer.java
new file mode 100644
index 00000000..bf4cd13b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/AlwaysValidVerifyer.java
@@ -0,0 +1,24 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * A certificate verifyer, that will always return true.
+ * <p/>
+ * <pre>
+ * DO NOT USE THIS FILE UNLESS YOU KNOW EXACTLY WHAT YOU ARE DOING.
+ * </pre>
+ *
+ * @deprecated Perform certificate verification in TlsAuthentication implementation
+ */
+public class AlwaysValidVerifyer
+ implements CertificateVerifyer
+{
+ /**
+ * Return true.
+ *
+ * @see org.bouncycastle.crypto.tls.CertificateVerifyer#isValid(org.bouncycastle.asn1.x509.Certificate[])
+ */
+ public boolean isValid(org.bouncycastle.asn1.x509.Certificate[] certs)
+ {
+ return true;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/BulkCipherAlgorithm.java b/core/src/main/java/org/bouncycastle/crypto/tls/BulkCipherAlgorithm.java
new file mode 100644
index 00000000..595bdad2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/BulkCipherAlgorithm.java
@@ -0,0 +1,24 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 2246
+ * <p/>
+ * Note that the values here are implementation-specific and arbitrary. It is recommended not to
+ * depend on the particular values (e.g. serialization).
+ */
+public class BulkCipherAlgorithm
+{
+
+ public static final int _null = 0;
+ public static final int rc4 = 1;
+ public static final int rc2 = 2;
+ public static final int des = 3;
+ public static final int _3des = 4;
+ public static final int des40 = 5;
+
+ /*
+ * RFC 4346
+ */
+ public static final int aes = 6;
+ public static final int idea = 7;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/ByteQueue.java b/core/src/main/java/org/bouncycastle/crypto/tls/ByteQueue.java
new file mode 100644
index 00000000..8b9d4ab1
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/ByteQueue.java
@@ -0,0 +1,123 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * A queue for bytes. This file could be more optimized.
+ */
+public class ByteQueue
+{
+ /**
+ * @return The smallest number which can be written as 2^x which is bigger than i.
+ */
+ public static final int nextTwoPow(int i)
+ {
+ /*
+ * This code is based of a lot of code I found on the Internet which mostly
+ * referenced a book called "Hacking delight".
+ */
+ i |= (i >> 1);
+ i |= (i >> 2);
+ i |= (i >> 4);
+ i |= (i >> 8);
+ i |= (i >> 16);
+ return i + 1;
+ }
+
+ /**
+ * The initial size for our buffer.
+ */
+ private static final int INITBUFSIZE = 1024;
+
+ /**
+ * The buffer where we store our data.
+ */
+ private byte[] databuf = new byte[ByteQueue.INITBUFSIZE];
+
+ /**
+ * How many bytes at the beginning of the buffer are skipped.
+ */
+ private int skipped = 0;
+
+ /**
+ * How many bytes in the buffer are valid data.
+ */
+ private int available = 0;
+
+ /**
+ * Read data from the buffer.
+ *
+ * @param buf The buffer where the read data will be copied to.
+ * @param offset How many bytes to skip at the beginning of buf.
+ * @param len How many bytes to read at all.
+ * @param skip How many bytes from our data to skip.
+ */
+ public void read(byte[] buf, int offset, int len, int skip)
+ {
+ if ((available - skip) < len)
+ {
+ throw new TlsRuntimeException("Not enough data to read");
+ }
+ if ((buf.length - offset) < len)
+ {
+ throw new TlsRuntimeException("Buffer size of " + buf.length
+ + " is too small for a read of " + len + " bytes");
+ }
+ System.arraycopy(databuf, skipped + skip, buf, offset, len);
+ return;
+ }
+
+ /**
+ * Add some data to our buffer.
+ *
+ * @param data A byte-array to read data from.
+ * @param offset How many bytes to skip at the beginning of the array.
+ * @param len How many bytes to read from the array.
+ */
+ public void addData(byte[] data, int offset, int len)
+ {
+ if ((skipped + available + len) > databuf.length)
+ {
+ byte[] tmp = new byte[ByteQueue.nextTwoPow(data.length)];
+ System.arraycopy(databuf, skipped, tmp, 0, available);
+ skipped = 0;
+ databuf = tmp;
+ }
+ System.arraycopy(data, offset, databuf, skipped + available, len);
+ available += len;
+ }
+
+ /**
+ * Remove some bytes from our data from the beginning.
+ *
+ * @param i How many bytes to remove.
+ */
+ public void removeData(int i)
+ {
+ if (i > available)
+ {
+ throw new TlsRuntimeException("Cannot remove " + i + " bytes, only got " + available);
+ }
+
+ /*
+ * Skip the data.
+ */
+ available -= i;
+ skipped += i;
+
+ /*
+ * If more than half of our data is skipped, we will move the data in the buffer.
+ */
+ if (skipped > (databuf.length / 2))
+ {
+ System.arraycopy(databuf, skipped, databuf, 0, available);
+ skipped = 0;
+ }
+ }
+
+ /**
+ * @return The number of bytes which are available in this buffer.
+ */
+ public int size()
+ {
+ return available;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/Certificate.java b/core/src/main/java/org/bouncycastle/crypto/tls/Certificate.java
new file mode 100644
index 00000000..fab79f4c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/Certificate.java
@@ -0,0 +1,153 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Primitive;
+
+/**
+ * Parsing and encoding of a <i>Certificate</i> struct from RFC 4346.
+ * <p/>
+ * <pre>
+ * opaque ASN.1Cert<2^24-1>;
+ *
+ * struct {
+ * ASN.1Cert certificate_list<0..2^24-1>;
+ * } Certificate;
+ * </pre>
+ *
+ * @see org.bouncycastle.asn1.x509.Certificate
+ */
+public class Certificate
+{
+
+ public static final Certificate EMPTY_CHAIN = new Certificate(
+ new org.bouncycastle.asn1.x509.Certificate[0]);
+
+ protected org.bouncycastle.asn1.x509.Certificate[] certificateList;
+
+ public Certificate(org.bouncycastle.asn1.x509.Certificate[] certificateList)
+ {
+ if (certificateList == null)
+ {
+ throw new IllegalArgumentException("'certificateList' cannot be null");
+ }
+
+ this.certificateList = certificateList;
+ }
+
+ /**
+ * @deprecated use {@link #getCertificateList()} instead
+ */
+ public org.bouncycastle.asn1.x509.Certificate[] getCerts()
+ {
+ return clone(certificateList);
+ }
+
+ /**
+ * @return an array of {@link org.bouncycastle.asn1.x509.Certificate} representing a certificate
+ * chain.
+ */
+ public org.bouncycastle.asn1.x509.Certificate[] getCertificateList()
+ {
+ return clone(certificateList);
+ }
+
+ public org.bouncycastle.asn1.x509.Certificate getCertificateAt(int index)
+ {
+ return certificateList[index];
+ }
+
+ public int getLength()
+ {
+ return certificateList.length;
+ }
+
+ /**
+ * @return <code>true</code> if this certificate chain contains no certificates, or
+ * <code>false</code> otherwise.
+ */
+ public boolean isEmpty()
+ {
+ return certificateList.length == 0;
+ }
+
+ /**
+ * Encode this {@link Certificate} to an {@link OutputStream}.
+ *
+ * @param output the {@link OutputStream} to encode to.
+ * @throws IOException
+ */
+ public void encode(OutputStream output)
+ throws IOException
+ {
+ Vector encCerts = new Vector(this.certificateList.length);
+ int totalLength = 0;
+ for (int i = 0; i < this.certificateList.length; ++i)
+ {
+ byte[] encCert = certificateList[i].getEncoded(ASN1Encoding.DER);
+ encCerts.addElement(encCert);
+ totalLength += encCert.length + 3;
+ }
+
+ TlsUtils.writeUint24(totalLength, output);
+
+ for (int i = 0; i < encCerts.size(); ++i)
+ {
+ byte[] encCert = (byte[])encCerts.elementAt(i);
+ TlsUtils.writeOpaque24(encCert, output);
+ }
+ }
+
+ /**
+ * Parse a {@link Certificate} from an {@link InputStream}.
+ *
+ * @param input the {@link InputStream} to parse from.
+ * @return a {@link Certificate} object.
+ * @throws IOException
+ */
+ public static Certificate parse(InputStream input)
+ throws IOException
+ {
+ org.bouncycastle.asn1.x509.Certificate[] certs;
+ int left = TlsUtils.readUint24(input);
+ if (left == 0)
+ {
+ return EMPTY_CHAIN;
+ }
+ Vector tmp = new Vector();
+ while (left > 0)
+ {
+ int size = TlsUtils.readUint24(input);
+ left -= 3 + size;
+
+ byte[] buf = TlsUtils.readFully(size, input);
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(buf);
+ ASN1Primitive asn1 = new ASN1InputStream(bis).readObject();
+ TlsProtocol.assertEmpty(bis);
+
+ tmp.addElement(org.bouncycastle.asn1.x509.Certificate.getInstance(asn1));
+ }
+ certs = new org.bouncycastle.asn1.x509.Certificate[tmp.size()];
+ for (int i = 0; i < tmp.size(); i++)
+ {
+ certs[i] = (org.bouncycastle.asn1.x509.Certificate)tmp.elementAt(i);
+ }
+ return new Certificate(certs);
+ }
+
+ private org.bouncycastle.asn1.x509.Certificate[] clone(org.bouncycastle.asn1.x509.Certificate[] list)
+ {
+ org.bouncycastle.asn1.x509.Certificate[] rv = new org.bouncycastle.asn1.x509.Certificate[list.length];
+
+ System.arraycopy(list, 0, rv, 0, rv.length);
+
+ return rv;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/CertificateRequest.java b/core/src/main/java/org/bouncycastle/crypto/tls/CertificateRequest.java
new file mode 100644
index 00000000..00bf9508
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/CertificateRequest.java
@@ -0,0 +1,140 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.x500.X500Name;
+
+/**
+ * Parsing and encoding of a <i>CertificateRequest</i> struct from RFC 4346.
+ * <p/>
+ * <pre>
+ * struct {
+ * ClientCertificateType certificate_types<1..2^8-1>;
+ * DistinguishedName certificate_authorities<3..2^16-1>;
+ * } CertificateRequest;
+ * </pre>
+ *
+ * @see ClientCertificateType
+ * @see X500Name
+ */
+public class CertificateRequest
+{
+ private short[] certificateTypes;
+ private Vector certificateAuthorities;
+
+ /*
+ * TODO RFC 5264 7.4.4 A list of the hash/signature algorithm pairs that the server is able to
+ * verify, listed in descending order of preference.
+ */
+
+ /**
+ * @param certificateTypes see {@link ClientCertificateType} for valid constants.
+ * @param certificateAuthorities a {@link Vector} of {@link X500Name}.
+ */
+ public CertificateRequest(short[] certificateTypes, Vector certificateAuthorities)
+ {
+ this.certificateTypes = certificateTypes;
+ this.certificateAuthorities = certificateAuthorities;
+ }
+
+ /**
+ * @return an array of certificate types
+ * @see {@link ClientCertificateType}
+ */
+ public short[] getCertificateTypes()
+ {
+ return certificateTypes;
+ }
+
+ /**
+ * @return a {@link Vector} of {@link X500Name}
+ */
+ public Vector getCertificateAuthorities()
+ {
+ return certificateAuthorities;
+ }
+
+ /**
+ * Encode this {@link CertificateRequest} to an {@link OutputStream}.
+ *
+ * @param output the {@link OutputStream} to encode to.
+ * @throws IOException
+ */
+ public void encode(OutputStream output)
+ throws IOException
+ {
+
+ if (certificateTypes == null || certificateTypes.length == 0)
+ {
+ TlsUtils.writeUint8((short)0, output);
+ }
+ else
+ {
+ TlsUtils.writeUint8((short)certificateTypes.length, output);
+ TlsUtils.writeUint8Array(certificateTypes, output);
+ }
+
+ if (certificateAuthorities == null || certificateAuthorities.isEmpty())
+ {
+ TlsUtils.writeUint16(0, output);
+ }
+ else
+ {
+
+ Vector encDNs = new Vector(certificateAuthorities.size());
+ int totalLength = 0;
+ for (int i = 0; i < certificateAuthorities.size(); ++i)
+ {
+ X500Name authorityDN = (X500Name)certificateAuthorities.elementAt(i);
+ byte[] encDN = authorityDN.getEncoded(ASN1Encoding.DER);
+ encDNs.addElement(encDN);
+ totalLength += encDN.length;
+ }
+
+ TlsUtils.writeUint16(totalLength, output);
+
+ for (int i = 0; i < encDNs.size(); ++i)
+ {
+ byte[] encDN = (byte[])encDNs.elementAt(i);
+ output.write(encDN);
+ }
+ }
+ }
+
+ /**
+ * Parse a {@link CertificateRequest} from an {@link InputStream}.
+ *
+ * @param input the {@link InputStream} to parse from.
+ * @return a {@link CertificateRequest} object.
+ * @throws IOException
+ */
+ public static CertificateRequest parse(InputStream input)
+ throws IOException
+ {
+ int numTypes = TlsUtils.readUint8(input);
+ short[] certificateTypes = new short[numTypes];
+ for (int i = 0; i < numTypes; ++i)
+ {
+ certificateTypes[i] = TlsUtils.readUint8(input);
+ }
+
+ byte[] authorities = TlsUtils.readOpaque16(input);
+
+ Vector authorityDNs = new Vector();
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(authorities);
+ while (bis.available() > 0)
+ {
+ byte[] dnBytes = TlsUtils.readOpaque16(bis);
+ authorityDNs.addElement(X500Name.getInstance(ASN1Primitive.fromByteArray(dnBytes)));
+ }
+
+ return new CertificateRequest(certificateTypes, authorityDNs);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/CertificateVerifyer.java b/core/src/main/java/org/bouncycastle/crypto/tls/CertificateVerifyer.java
new file mode 100644
index 00000000..2e3715c1
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/CertificateVerifyer.java
@@ -0,0 +1,16 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * This should be implemented by any class which can find out, if a given certificate
+ * chain is being accepted by an client.
+ *
+ * @deprecated Perform certificate verification in TlsAuthentication implementation
+ */
+public interface CertificateVerifyer
+{
+ /**
+ * @param certs The certs, which are part of the chain.
+ * @return True, if the chain is accepted, false otherwise.
+ */
+ public boolean isValid(org.bouncycastle.asn1.x509.Certificate[] certs);
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/CipherSuite.java b/core/src/main/java/org/bouncycastle/crypto/tls/CipherSuite.java
new file mode 100644
index 00000000..2979cdef
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/CipherSuite.java
@@ -0,0 +1,207 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 2246 A.5
+ */
+public class CipherSuite
+{
+
+ public static final int TLS_NULL_WITH_NULL_NULL = 0x0000;
+ public static final int TLS_RSA_WITH_NULL_MD5 = 0x0001;
+ public static final int TLS_RSA_WITH_NULL_SHA = 0x0002;
+ public static final int TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003;
+ public static final int TLS_RSA_WITH_RC4_128_MD5 = 0x0004;
+ public static final int TLS_RSA_WITH_RC4_128_SHA = 0x0005;
+ public static final int TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x0006;
+ public static final int TLS_RSA_WITH_IDEA_CBC_SHA = 0x0007;
+ public static final int TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0008;
+ public static final int TLS_RSA_WITH_DES_CBC_SHA = 0x0009;
+ public static final int TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A;
+ public static final int TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x000B;
+ public static final int TLS_DH_DSS_WITH_DES_CBC_SHA = 0x000C;
+ public static final int TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000D;
+ public static final int TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x000E;
+ public static final int TLS_DH_RSA_WITH_DES_CBC_SHA = 0x000F;
+ public static final int TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x0010;
+ public static final int TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0011;
+ public static final int TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x0012;
+ public static final int TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013;
+ public static final int TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0014;
+ public static final int TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x0015;
+ public static final int TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016;
+ public static final int TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017;
+ public static final int TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018;
+ public static final int TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x0019;
+ public static final int TLS_DH_anon_WITH_DES_CBC_SHA = 0x001A;
+ public static final int TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x001B;
+
+ /*
+ * Note: The cipher suite values { 0x00, 0x1C } and { 0x00, 0x1D } are reserved to avoid
+ * collision with Fortezza-based cipher suites in SSL 3.
+ */
+
+ /*
+ * RFC 3268
+ */
+ public static final int TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F;
+ public static final int TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x0030;
+ public static final int TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x0031;
+ public static final int TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032;
+ public static final int TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033;
+ public static final int TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x0034;
+ public static final int TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035;
+ public static final int TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x0036;
+ public static final int TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x0037;
+ public static final int TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038;
+ public static final int TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039;
+ public static final int TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x003A;
+
+ /*
+ * RFC 4132
+ */
+ public static final int TLS_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0041;
+ public static final int TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0042;
+ public static final int TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0043;
+ public static final int TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0044;
+ public static final int TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0045;
+ public static final int TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA = 0x0046;
+ public static final int TLS_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0084;
+ public static final int TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0085;
+ public static final int TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0086;
+ public static final int TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0087;
+ public static final int TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0088;
+ public static final int TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA = 0x0089;
+
+ /*
+ * RFC 4162
+ */
+ public static final int TLS_RSA_WITH_SEED_CBC_SHA = 0x0096;
+ public static final int TLS_DH_DSS_WITH_SEED_CBC_SHA = 0x0097;
+ public static final int TLS_DH_RSA_WITH_SEED_CBC_SHA = 0x0098;
+ public static final int TLS_DHE_DSS_WITH_SEED_CBC_SHA = 0x0099;
+ public static final int TLS_DHE_RSA_WITH_SEED_CBC_SHA = 0x009A;
+ public static final int TLS_DH_anon_WITH_SEED_CBC_SHA = 0x009B;
+
+ /*
+ * RFC 4279
+ */
+ public static final int TLS_PSK_WITH_RC4_128_SHA = 0x008A;
+ public static final int TLS_PSK_WITH_3DES_EDE_CBC_SHA = 0x008B;
+ public static final int TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C;
+ public static final int TLS_PSK_WITH_AES_256_CBC_SHA = 0x008D;
+ public static final int TLS_DHE_PSK_WITH_RC4_128_SHA = 0x008E;
+ public static final int TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = 0x008F;
+ public static final int TLS_DHE_PSK_WITH_AES_128_CBC_SHA = 0x0090;
+ public static final int TLS_DHE_PSK_WITH_AES_256_CBC_SHA = 0x0091;
+ public static final int TLS_RSA_PSK_WITH_RC4_128_SHA = 0x0092;
+ public static final int TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = 0x0093;
+ public static final int TLS_RSA_PSK_WITH_AES_128_CBC_SHA = 0x0094;
+ public static final int TLS_RSA_PSK_WITH_AES_256_CBC_SHA = 0x0095;
+
+ /*
+ * RFC 4492
+ */
+ public static final int TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001;
+ public static final int TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xC002;
+ public static final int TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC003;
+ public static final int TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = 0xC004;
+ public static final int TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = 0xC005;
+ public static final int TLS_ECDHE_ECDSA_WITH_NULL_SHA = 0xC006;
+ public static final int TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007;
+ public static final int TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008;
+ public static final int TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009;
+ public static final int TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A;
+ public static final int TLS_ECDH_RSA_WITH_NULL_SHA = 0xC00B;
+ public static final int TLS_ECDH_RSA_WITH_RC4_128_SHA = 0xC00C;
+ public static final int TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = 0xC00D;
+ public static final int TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = 0xC00E;
+ public static final int TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = 0xC00F;
+ public static final int TLS_ECDHE_RSA_WITH_NULL_SHA = 0xC010;
+ public static final int TLS_ECDHE_RSA_WITH_RC4_128_SHA = 0xC011;
+ public static final int TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 0xC012;
+ public static final int TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013;
+ public static final int TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014;
+ public static final int TLS_ECDH_anon_WITH_NULL_SHA = 0xC015;
+ public static final int TLS_ECDH_anon_WITH_RC4_128_SHA = 0xC016;
+ public static final int TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = 0xC017;
+ public static final int TLS_ECDH_anon_WITH_AES_128_CBC_SHA = 0xC018;
+ public static final int TLS_ECDH_anon_WITH_AES_256_CBC_SHA = 0xC019;
+
+ /*
+ * RFC 4785
+ */
+ public static final int TLS_PSK_WITH_NULL_SHA = 0x002C;
+ public static final int TLS_DHE_PSK_WITH_NULL_SHA = 0x002D;
+ public static final int TLS_RSA_PSK_WITH_NULL_SHA = 0x002E;
+
+ /*
+ * RFC 5054
+ */
+ public static final int TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0xC01A;
+ public static final int TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0xC01B;
+ public static final int TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = 0xC01C;
+ public static final int TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0xC01D;
+ public static final int TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0xC01E;
+ public static final int TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = 0xC01F;
+ public static final int TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0xC020;
+ public static final int TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0xC021;
+ public static final int TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = 0xC022;
+
+ /*
+ * RFC 5246
+ */
+ public static final int TLS_RSA_WITH_NULL_SHA256 = 0x003B;
+ public static final int TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C;
+ public static final int TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D;
+ public static final int TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x003E;
+ public static final int TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x003F;
+ public static final int TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040;
+ public static final int TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067;
+ public static final int TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x0068;
+ public static final int TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x0069;
+ public static final int TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006A;
+ public static final int TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B;
+ public static final int TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x006C;
+ public static final int TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x006D;
+
+ /*
+ * RFC 5288
+ */
+ public static final int TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C;
+ public static final int TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D;
+ public static final int TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E;
+ public static final int TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F;
+ public static final int TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = 0x00A0;
+ public static final int TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = 0x00A1;
+ public static final int TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2;
+ public static final int TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3;
+ public static final int TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = 0x00A4;
+ public static final int TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = 0x00A5;
+ public static final int TLS_DH_anon_WITH_AES_128_GCM_SHA256 = 0x00A6;
+ public static final int TLS_DH_anon_WITH_AES_256_GCM_SHA384 = 0x00A7;
+
+ /*
+ * RFC 5289
+ */
+ public static final int TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023;
+ public static final int TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024;
+ public static final int TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC025;
+ public static final int TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC026;
+ public static final int TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027;
+ public static final int TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028;
+ public static final int TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = 0xC029;
+ public static final int TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = 0xC02A;
+ public static final int TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B;
+ public static final int TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C;
+ public static final int TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02D;
+ public static final int TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02E;
+ public static final int TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F;
+ public static final int TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030;
+ public static final int TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = 0xC031;
+ public static final int TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = 0xC032;
+
+ /*
+ * RFC 5746
+ */
+ public static final int TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/CipherType.java b/core/src/main/java/org/bouncycastle/crypto/tls/CipherType.java
new file mode 100644
index 00000000..cac7dbe4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/CipherType.java
@@ -0,0 +1,19 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 2246
+ * <p/>
+ * Note that the values here are implementation-specific and arbitrary. It is recommended not to
+ * depend on the particular values (e.g. serialization).
+ */
+public class CipherType
+{
+
+ public static final int stream = 0;
+ public static final int block = 1;
+
+ /*
+ * RFC 5246
+ */
+ public static final int aead = 2;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/ClientAuthenticationType.java b/core/src/main/java/org/bouncycastle/crypto/tls/ClientAuthenticationType.java
new file mode 100644
index 00000000..a77a8265
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/ClientAuthenticationType.java
@@ -0,0 +1,12 @@
+package org.bouncycastle.crypto.tls;
+
+public class ClientAuthenticationType
+{
+
+ /*
+ * RFC 5077 4
+ */
+ public static final short anonymous = 0;
+ public static final short certificate_based = 1;
+ public static final short psk = 2;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/ClientCertificateType.java b/core/src/main/java/org/bouncycastle/crypto/tls/ClientCertificateType.java
new file mode 100644
index 00000000..0a12acae
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/ClientCertificateType.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.crypto.tls;
+
+public class ClientCertificateType
+{
+
+ /*
+ * RFC 4346 7.4.4
+ */
+ public static final short rsa_sign = 1;
+ public static final short dss_sign = 2;
+ public static final short rsa_fixed_dh = 3;
+ public static final short dss_fixed_dh = 4;
+ public static final short rsa_ephemeral_dh_RESERVED = 5;
+ public static final short dss_ephemeral_dh_RESERVED = 6;
+ public static final short fortezza_dms_RESERVED = 20;
+
+ /*
+ * RFC 4492 5.5
+ */
+ public static final short ecdsa_sign = 64;
+ public static final short rsa_fixed_ecdh = 65;
+ public static final short ecdsa_fixed_ecdh = 66;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java b/core/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java
new file mode 100644
index 00000000..1a484911
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java
@@ -0,0 +1,117 @@
+package org.bouncycastle.crypto.tls;
+
+import org.bouncycastle.crypto.Digest;
+
+/**
+ * A combined hash, which implements md5(m) || sha1(m).
+ */
+class CombinedHash
+ implements TlsHandshakeHash
+{
+
+ protected TlsContext context;
+ protected Digest md5;
+ protected Digest sha1;
+
+ CombinedHash()
+ {
+ this.md5 = TlsUtils.createHash(HashAlgorithm.md5);
+ this.sha1 = TlsUtils.createHash(HashAlgorithm.sha1);
+ }
+
+ CombinedHash(CombinedHash t)
+ {
+ this.context = t.context;
+ this.md5 = TlsUtils.cloneHash(HashAlgorithm.md5, t.md5);
+ this.sha1 = TlsUtils.cloneHash(HashAlgorithm.sha1, t.sha1);
+ }
+
+ public void init(TlsContext context)
+ {
+ this.context = context;
+ }
+
+ public TlsHandshakeHash commit()
+ {
+ return this;
+ }
+
+ public TlsHandshakeHash fork()
+ {
+ return new CombinedHash(this);
+ }
+
+ /**
+ * @see org.bouncycastle.crypto.Digest#getAlgorithmName()
+ */
+ public String getAlgorithmName()
+ {
+ return md5.getAlgorithmName() + " and " + sha1.getAlgorithmName();
+ }
+
+ /**
+ * @see org.bouncycastle.crypto.Digest#getDigestSize()
+ */
+ public int getDigestSize()
+ {
+ return md5.getDigestSize() + sha1.getDigestSize();
+ }
+
+ /**
+ * @see org.bouncycastle.crypto.Digest#update(byte)
+ */
+ public void update(byte in)
+ {
+ md5.update(in);
+ sha1.update(in);
+ }
+
+ /**
+ * @see org.bouncycastle.crypto.Digest#update(byte[], int, int)
+ */
+ public void update(byte[] in, int inOff, int len)
+ {
+ md5.update(in, inOff, len);
+ sha1.update(in, inOff, len);
+ }
+
+ /**
+ * @see org.bouncycastle.crypto.Digest#doFinal(byte[], int)
+ */
+ public int doFinal(byte[] out, int outOff)
+ {
+ if (context != null && context.getServerVersion().isSSL())
+ {
+ ssl3Complete(md5, SSL3Mac.IPAD, SSL3Mac.OPAD, 48);
+ ssl3Complete(sha1, SSL3Mac.IPAD, SSL3Mac.OPAD, 40);
+ }
+
+ int i1 = md5.doFinal(out, outOff);
+ int i2 = sha1.doFinal(out, outOff + i1);
+ return i1 + i2;
+ }
+
+ /**
+ * @see org.bouncycastle.crypto.Digest#reset()
+ */
+ public void reset()
+ {
+ md5.reset();
+ sha1.reset();
+ }
+
+ protected void ssl3Complete(Digest d, byte[] ipad, byte[] opad, int padLength)
+ {
+ byte[] secret = context.getSecurityParameters().masterSecret;
+
+ d.update(secret, 0, secret.length);
+ d.update(ipad, 0, padLength);
+
+ byte[] tmp = new byte[d.getDigestSize()];
+ d.doFinal(tmp, 0);
+
+ d.update(secret, 0, secret.length);
+ d.update(opad, 0, padLength);
+ d.update(tmp, 0, tmp.length);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/CompressionMethod.java b/core/src/main/java/org/bouncycastle/crypto/tls/CompressionMethod.java
new file mode 100644
index 00000000..935d378f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/CompressionMethod.java
@@ -0,0 +1,24 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 2246 6.1
+ */
+public class CompressionMethod
+{
+ public static final short _null = 0;
+
+ /**
+ * @deprecated use '_null' instead
+ */
+ public static final short NULL = _null;
+
+ /*
+ * RFC 3749 2
+ */
+ public static final short DEFLATE = 1;
+
+ /*
+ * Values from 224 decimal (0xE0) through 255 decimal (0xFF)
+ * inclusive are reserved for private use.
+ */
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/ConnectionEnd.java b/core/src/main/java/org/bouncycastle/crypto/tls/ConnectionEnd.java
new file mode 100644
index 00000000..f13def6e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/ConnectionEnd.java
@@ -0,0 +1,14 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 2246
+ * <p/>
+ * Note that the values here are implementation-specific and arbitrary. It is recommended not to
+ * depend on the particular values (e.g. serialization).
+ */
+public class ConnectionEnd
+{
+
+ public static final int server = 0;
+ public static final int client = 1;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/ContentType.java b/core/src/main/java/org/bouncycastle/crypto/tls/ContentType.java
new file mode 100644
index 00000000..d814eac0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/ContentType.java
@@ -0,0 +1,12 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 2246 6.2.1
+ */
+public class ContentType
+{
+ public static final short change_cipher_spec = 20;
+ public static final short alert = 21;
+ public static final short handshake = 22;
+ public static final short application_data = 23;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java
new file mode 100644
index 00000000..8ccacfba
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java
@@ -0,0 +1,634 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.SecureRandom;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.bouncycastle.util.Arrays;
+
+public class DTLSClientProtocol
+ extends DTLSProtocol
+{
+
+ public DTLSClientProtocol(SecureRandom secureRandom)
+ {
+ super(secureRandom);
+ }
+
+ public DTLSTransport connect(TlsClient client, DatagramTransport transport)
+ throws IOException
+ {
+
+ if (client == null)
+ {
+ throw new IllegalArgumentException("'client' cannot be null");
+ }
+ if (transport == null)
+ {
+ throw new IllegalArgumentException("'transport' cannot be null");
+ }
+
+ SecurityParameters securityParameters = new SecurityParameters();
+ securityParameters.entity = ConnectionEnd.client;
+ securityParameters.clientRandom = TlsProtocol.createRandomBlock(secureRandom);
+
+ ClientHandshakeState state = new ClientHandshakeState();
+ state.client = client;
+ state.clientContext = new TlsClientContextImpl(secureRandom, securityParameters);
+ client.init(state.clientContext);
+
+ DTLSRecordLayer recordLayer = new DTLSRecordLayer(transport, state.clientContext, client, ContentType.handshake);
+
+ try
+ {
+ return clientHandshake(state, recordLayer);
+ }
+ catch (TlsFatalAlert fatalAlert)
+ {
+ recordLayer.fail(fatalAlert.getAlertDescription());
+ throw fatalAlert;
+ }
+ catch (IOException e)
+ {
+ recordLayer.fail(AlertDescription.internal_error);
+ throw e;
+ }
+ catch (RuntimeException e)
+ {
+ recordLayer.fail(AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ protected DTLSTransport clientHandshake(ClientHandshakeState state, DTLSRecordLayer recordLayer)
+ throws IOException
+ {
+
+ SecurityParameters securityParameters = state.clientContext.getSecurityParameters();
+ DTLSReliableHandshake handshake = new DTLSReliableHandshake(state.clientContext, recordLayer);
+
+ byte[] clientHelloBody = generateClientHello(state, state.client);
+ handshake.sendMessage(HandshakeType.client_hello, clientHelloBody);
+
+ DTLSReliableHandshake.Message serverMessage = handshake.receiveMessage();
+
+ {
+ // NOTE: After receiving a record from the server, we discover the record layer version
+ ProtocolVersion server_version = recordLayer.getDiscoveredPeerVersion();
+ ProtocolVersion client_version = state.clientContext.getClientVersion();
+
+ if (!server_version.isEqualOrEarlierVersionOf(client_version))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ state.clientContext.setServerVersion(server_version);
+ state.client.notifyServerVersion(server_version);
+ }
+
+ while (serverMessage.getType() == HandshakeType.hello_verify_request)
+ {
+ byte[] cookie = parseHelloVerifyRequest(state.clientContext, serverMessage.getBody());
+ byte[] patched = patchClientHelloWithCookie(clientHelloBody, cookie);
+
+ handshake.resetHandshakeMessagesDigest();
+ handshake.sendMessage(HandshakeType.client_hello, patched);
+
+ serverMessage = handshake.receiveMessage();
+ }
+
+ if (serverMessage.getType() == HandshakeType.server_hello)
+ {
+ processServerHello(state, serverMessage.getBody());
+ serverMessage = handshake.receiveMessage();
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ securityParameters.prfAlgorithm = TlsProtocol.getPRFAlgorithm(state.selectedCipherSuite);
+ securityParameters.compressionAlgorithm = state.selectedCompressionMethod;
+
+ /*
+ * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify verify_data_length has
+ * a verify_data_length equal to 12. This includes all existing cipher suites.
+ */
+ securityParameters.verifyDataLength = 12;
+
+ handshake.notifyHelloComplete();
+
+ if (serverMessage.getType() == HandshakeType.supplemental_data)
+ {
+ processServerSupplementalData(state, serverMessage.getBody());
+ serverMessage = handshake.receiveMessage();
+ }
+ else
+ {
+ state.client.processServerSupplementalData(null);
+ }
+
+ state.keyExchange = state.client.getKeyExchange();
+ state.keyExchange.init(state.clientContext);
+
+ if (serverMessage.getType() == HandshakeType.certificate)
+ {
+ processServerCertificate(state, serverMessage.getBody());
+ serverMessage = handshake.receiveMessage();
+ }
+ else
+ {
+ // Okay, Certificate is optional
+ state.keyExchange.skipServerCredentials();
+ }
+
+ if (serverMessage.getType() == HandshakeType.server_key_exchange)
+ {
+ processServerKeyExchange(state, serverMessage.getBody());
+ serverMessage = handshake.receiveMessage();
+ }
+ else
+ {
+ // Okay, ServerKeyExchange is optional
+ state.keyExchange.skipServerKeyExchange();
+ }
+
+ if (serverMessage.getType() == HandshakeType.certificate_request)
+ {
+ processCertificateRequest(state, serverMessage.getBody());
+ serverMessage = handshake.receiveMessage();
+ }
+ else
+ {
+ // Okay, CertificateRequest is optional
+ }
+
+ if (serverMessage.getType() == HandshakeType.server_hello_done)
+ {
+ if (serverMessage.getBody().length != 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ Vector clientSupplementalData = state.client.getClientSupplementalData();
+ if (clientSupplementalData != null)
+ {
+ byte[] supplementalDataBody = generateSupplementalData(clientSupplementalData);
+ handshake.sendMessage(HandshakeType.supplemental_data, supplementalDataBody);
+ }
+
+ if (state.certificateRequest != null)
+ {
+ state.clientCredentials = state.authentication.getClientCredentials(state.certificateRequest);
+
+ /*
+ * RFC 5246 If no suitable certificate is available, the client MUST send a certificate
+ * message containing no certificates.
+ *
+ * NOTE: In previous RFCs, this was SHOULD instead of MUST.
+ */
+ Certificate clientCertificate = null;
+ if (state.clientCredentials != null)
+ {
+ clientCertificate = state.clientCredentials.getCertificate();
+ }
+ if (clientCertificate == null)
+ {
+ clientCertificate = Certificate.EMPTY_CHAIN;
+ }
+
+ byte[] certificateBody = generateCertificate(clientCertificate);
+ handshake.sendMessage(HandshakeType.certificate, certificateBody);
+ }
+
+ if (state.clientCredentials != null)
+ {
+ state.keyExchange.processClientCredentials(state.clientCredentials);
+ }
+ else
+ {
+ state.keyExchange.skipClientCredentials();
+ }
+
+ byte[] clientKeyExchangeBody = generateClientKeyExchange(state);
+ handshake.sendMessage(HandshakeType.client_key_exchange, clientKeyExchangeBody);
+
+ TlsProtocol.establishMasterSecret(state.clientContext, state.keyExchange);
+
+ if (state.clientCredentials instanceof TlsSignerCredentials)
+ {
+ /*
+ * TODO RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm prepended
+ * from TLS 1.2
+ */
+ TlsSignerCredentials signerCredentials = (TlsSignerCredentials)state.clientCredentials;
+ byte[] md5andsha1 = handshake.getCurrentHash();
+ byte[] signature = signerCredentials.generateCertificateSignature(md5andsha1);
+ byte[] certificateVerifyBody = generateCertificateVerify(state, signature);
+ handshake.sendMessage(HandshakeType.certificate_verify, certificateVerifyBody);
+ }
+
+ recordLayer.initPendingEpoch(state.client.getCipher());
+
+ // NOTE: Calculated exclusive of the Finished message itself
+ byte[] clientVerifyData = TlsUtils.calculateVerifyData(state.clientContext, "client finished",
+ handshake.getCurrentHash());
+ handshake.sendMessage(HandshakeType.finished, clientVerifyData);
+
+ if (state.expectSessionTicket)
+ {
+ serverMessage = handshake.receiveMessage();
+ if (serverMessage.getType() == HandshakeType.session_ticket)
+ {
+ processNewSessionTicket(state, serverMessage.getBody());
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+
+ // NOTE: Calculated exclusive of the actual Finished message from the server
+ byte[] expectedServerVerifyData = TlsUtils.calculateVerifyData(state.clientContext, "server finished",
+ handshake.getCurrentHash());
+ serverMessage = handshake.receiveMessage();
+
+ if (serverMessage.getType() == HandshakeType.finished)
+ {
+ processFinished(serverMessage.getBody(), expectedServerVerifyData);
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ handshake.finish();
+
+ state.client.notifyHandshakeComplete();
+
+ return new DTLSTransport(recordLayer);
+ }
+
+ protected byte[] generateCertificateVerify(ClientHandshakeState state, byte[] signature)
+ throws IOException
+ {
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ TlsUtils.writeOpaque16(signature, buf);
+ return buf.toByteArray();
+ }
+
+ protected byte[] generateClientHello(ClientHandshakeState state, TlsClient client)
+ throws IOException
+ {
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+
+ ProtocolVersion client_version = client.getClientVersion();
+ if (!client_version.isDTLS())
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ state.clientContext.setClientVersion(client_version);
+ TlsUtils.writeVersion(client_version, buf);
+
+ buf.write(state.clientContext.getSecurityParameters().getClientRandom());
+
+ // Session id
+ TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf);
+
+ // Cookie
+ TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf);
+
+ /*
+ * Cipher suites
+ */
+ state.offeredCipherSuites = client.getCipherSuites();
+
+ // Integer -> byte[]
+ state.clientExtensions = client.getClientExtensions();
+
+ // Cipher Suites (and SCSV)
+ {
+ /*
+ * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension,
+ * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the
+ * ClientHello. Including both is NOT RECOMMENDED.
+ */
+ boolean noRenegExt = state.clientExtensions == null
+ || state.clientExtensions.get(TlsProtocol.EXT_RenegotiationInfo) == null;
+
+ int count = state.offeredCipherSuites.length;
+ if (noRenegExt)
+ {
+ // Note: 1 extra slot for TLS_EMPTY_RENEGOTIATION_INFO_SCSV
+ ++count;
+ }
+
+ TlsUtils.writeUint16(2 * count, buf);
+ TlsUtils.writeUint16Array(state.offeredCipherSuites, buf);
+
+ if (noRenegExt)
+ {
+ TlsUtils.writeUint16(CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV, buf);
+ }
+ }
+
+ // TODO Add support for compression
+ // Compression methods
+ // state.offeredCompressionMethods = client.getCompressionMethods();
+ state.offeredCompressionMethods = new short[]{CompressionMethod._null};
+
+ TlsUtils.writeUint8((short)state.offeredCompressionMethods.length, buf);
+ TlsUtils.writeUint8Array(state.offeredCompressionMethods, buf);
+
+ // Extensions
+ if (state.clientExtensions != null)
+ {
+ TlsProtocol.writeExtensions(buf, state.clientExtensions);
+ }
+
+ return buf.toByteArray();
+ }
+
+ protected byte[] generateClientKeyExchange(ClientHandshakeState state)
+ throws IOException
+ {
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ state.keyExchange.generateClientKeyExchange(buf);
+ return buf.toByteArray();
+ }
+
+ protected void processCertificateRequest(ClientHandshakeState state, byte[] body)
+ throws IOException
+ {
+
+ if (state.authentication == null)
+ {
+ /*
+ * RFC 2246 7.4.4. It is a fatal handshake_failure alert for an anonymous server to
+ * request client identification.
+ */
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(body);
+
+ state.certificateRequest = CertificateRequest.parse(buf);
+
+ TlsProtocol.assertEmpty(buf);
+
+ state.keyExchange.validateCertificateRequest(state.certificateRequest);
+ }
+
+ protected void processNewSessionTicket(ClientHandshakeState state, byte[] body)
+ throws IOException
+ {
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(body);
+
+ NewSessionTicket newSessionTicket = NewSessionTicket.parse(buf);
+
+ TlsProtocol.assertEmpty(buf);
+
+ state.client.notifyNewSessionTicket(newSessionTicket);
+ }
+
+ protected void processServerCertificate(ClientHandshakeState state, byte[] body)
+ throws IOException
+ {
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(body);
+
+ Certificate serverCertificate = Certificate.parse(buf);
+
+ TlsProtocol.assertEmpty(buf);
+
+ state.keyExchange.processServerCertificate(serverCertificate);
+ state.authentication = state.client.getAuthentication();
+ state.authentication.notifyServerCertificate(serverCertificate);
+ }
+
+ protected void processServerHello(ClientHandshakeState state, byte[] body)
+ throws IOException
+ {
+
+ SecurityParameters securityParameters = state.clientContext.getSecurityParameters();
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(body);
+
+ // TODO Read RFCs for guidance on the expected record layer version number
+ ProtocolVersion server_version = TlsUtils.readVersion(buf);
+ if (!server_version.equals(state.clientContext.getServerVersion()))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ securityParameters.serverRandom = TlsUtils.readFully(32, buf);
+
+ byte[] sessionID = TlsUtils.readOpaque8(buf);
+ if (sessionID.length > 32)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ state.client.notifySessionID(sessionID);
+
+ state.selectedCipherSuite = TlsUtils.readUint16(buf);
+ if (!TlsProtocol.arrayContains(state.offeredCipherSuites, state.selectedCipherSuite)
+ || state.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
+ || state.selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ validateSelectedCipherSuite(state.selectedCipherSuite, AlertDescription.illegal_parameter);
+
+ state.client.notifySelectedCipherSuite(state.selectedCipherSuite);
+
+ state.selectedCompressionMethod = TlsUtils.readUint8(buf);
+ if (!TlsProtocol.arrayContains(state.offeredCompressionMethods, state.selectedCompressionMethod))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ state.client.notifySelectedCompressionMethod(state.selectedCompressionMethod);
+
+ /*
+ * RFC3546 2.2 The extended server hello message format MAY be sent in place of the server
+ * hello message when the client has requested extended functionality via the extended
+ * client hello message specified in Section 2.1. ... Note that the extended server hello
+ * message is only sent in response to an extended client hello message. This prevents the
+ * possibility that the extended server hello message could "break" existing TLS 1.0
+ * clients.
+ */
+
+ /*
+ * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore
+ * extensions appearing in the client hello, and send a server hello containing no
+ * extensions.
+ */
+
+ // Integer -> byte[]
+ Hashtable serverExtensions = TlsProtocol.readExtensions(buf);
+
+ /*
+ * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an
+ * extended client hello message. However, see RFC 5746 exception below. We always include
+ * the SCSV, so an Extended Server Hello is always allowed.
+ */
+ if (serverExtensions != null)
+ {
+ Enumeration e = serverExtensions.keys();
+ while (e.hasMoreElements())
+ {
+ Integer extType = (Integer)e.nextElement();
+
+ /*
+ * RFC 5746 Note that sending a "renegotiation_info" extension in response to a
+ * ClientHello containing only the SCSV is an explicit exception to the prohibition
+ * in RFC 5246, Section 7.4.1.4, on the server sending unsolicited extensions and is
+ * only allowed because the client is signaling its willingness to receive the
+ * extension via the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. TLS implementations
+ * MUST continue to comply with Section 7.4.1.4 for all other extensions.
+ */
+ if (!extType.equals(TlsProtocol.EXT_RenegotiationInfo)
+ && (state.clientExtensions == null || state.clientExtensions.get(extType) == null))
+ {
+ /*
+ * RFC 3546 2.3 Note that for all extension types (including those defined in
+ * future), the extension type MUST NOT appear in the extended server hello
+ * unless the same extension type appeared in the corresponding client hello.
+ * Thus clients MUST abort the handshake if they receive an extension type in
+ * the extended server hello that they did not request in the associated
+ * (extended) client hello.
+ */
+ throw new TlsFatalAlert(AlertDescription.unsupported_extension);
+ }
+ }
+
+ /*
+ * RFC 5746 3.4. Client Behavior: Initial Handshake
+ */
+ {
+ /*
+ * When a ServerHello is received, the client MUST check if it includes the
+ * "renegotiation_info" extension:
+ */
+ byte[] renegExtValue = (byte[])serverExtensions.get(TlsProtocol.EXT_RenegotiationInfo);
+ if (renegExtValue != null)
+ {
+ /*
+ * If the extension is present, set the secure_renegotiation flag to TRUE. The
+ * client MUST then verify that the length of the "renegotiated_connection"
+ * field is zero, and if it is not, MUST abort the handshake (by sending a fatal
+ * handshake_failure alert).
+ */
+ state.secure_renegotiation = true;
+
+ if (!Arrays.constantTimeAreEqual(renegExtValue,
+ TlsProtocol.createRenegotiationInfo(TlsUtils.EMPTY_BYTES)))
+ {
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ }
+ }
+
+ state.expectSessionTicket = serverExtensions.containsKey(TlsProtocol.EXT_SessionTicket);
+ }
+
+ state.client.notifySecureRenegotiation(state.secure_renegotiation);
+
+ if (state.clientExtensions != null)
+ {
+ state.client.processServerExtensions(serverExtensions);
+ }
+ }
+
+ protected void processServerKeyExchange(ClientHandshakeState state, byte[] body)
+ throws IOException
+ {
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(body);
+
+ state.keyExchange.processServerKeyExchange(buf);
+
+ TlsProtocol.assertEmpty(buf);
+ }
+
+ protected void processServerSupplementalData(ClientHandshakeState state, byte[] body)
+ throws IOException
+ {
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(body);
+ Vector serverSupplementalData = TlsProtocol.readSupplementalDataMessage(buf);
+ state.client.processServerSupplementalData(serverSupplementalData);
+ }
+
+ protected static byte[] parseHelloVerifyRequest(TlsContext context, byte[] body)
+ throws IOException
+ {
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(body);
+
+ ProtocolVersion server_version = TlsUtils.readVersion(buf);
+ if (!server_version.equals(context.getServerVersion()))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ byte[] cookie = TlsUtils.readOpaque8(buf);
+
+ // TODO RFC 4347 has the cookie length restricted to 32, but not in RFC 6347
+
+ TlsProtocol.assertEmpty(buf);
+
+ return cookie;
+ }
+
+ protected static byte[] patchClientHelloWithCookie(byte[] clientHelloBody, byte[] cookie)
+ throws IOException
+ {
+
+ int sessionIDPos = 34;
+ int sessionIDLength = TlsUtils.readUint8(clientHelloBody, sessionIDPos);
+
+ int cookieLengthPos = sessionIDPos + 1 + sessionIDLength;
+ int cookiePos = cookieLengthPos + 1;
+
+ byte[] patched = new byte[clientHelloBody.length + cookie.length];
+ System.arraycopy(clientHelloBody, 0, patched, 0, cookieLengthPos);
+ TlsUtils.writeUint8((short)cookie.length, patched, cookieLengthPos);
+ System.arraycopy(cookie, 0, patched, cookiePos, cookie.length);
+ System.arraycopy(clientHelloBody, cookiePos, patched, cookiePos + cookie.length, clientHelloBody.length
+ - cookiePos);
+
+ return patched;
+ }
+
+ protected static class ClientHandshakeState
+ {
+ TlsClient client = null;
+ TlsClientContextImpl clientContext = null;
+ int[] offeredCipherSuites = null;
+ short[] offeredCompressionMethods = null;
+ Hashtable clientExtensions = null;
+ int selectedCipherSuite = -1;
+ short selectedCompressionMethod = -1;
+ boolean secure_renegotiation = false;
+ boolean expectSessionTicket = false;
+ TlsKeyExchange keyExchange = null;
+ TlsAuthentication authentication = null;
+ CertificateRequest certificateRequest = null;
+ TlsCredentials clientCredentials = null;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DTLSEpoch.java b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSEpoch.java
new file mode 100644
index 00000000..59fbc53e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSEpoch.java
@@ -0,0 +1,53 @@
+package org.bouncycastle.crypto.tls;
+
+class DTLSEpoch
+{
+
+ private final DTLSReplayWindow replayWindow = new DTLSReplayWindow();
+
+ private final int epoch;
+ private final TlsCipher cipher;
+
+ private long sequence_number = 0;
+
+ DTLSEpoch(int epoch, TlsCipher cipher)
+ {
+ if (epoch < 0)
+ {
+ throw new IllegalArgumentException("'epoch' must be >= 0");
+ }
+ if (cipher == null)
+ {
+ throw new IllegalArgumentException("'cipher' cannot be null");
+ }
+
+ this.epoch = epoch;
+ this.cipher = cipher;
+ }
+
+ long allocateSequenceNumber()
+ {
+ // TODO Check for overflow
+ return sequence_number++;
+ }
+
+ TlsCipher getCipher()
+ {
+ return cipher;
+ }
+
+ int getEpoch()
+ {
+ return epoch;
+ }
+
+ DTLSReplayWindow getReplayWindow()
+ {
+ return replayWindow;
+ }
+
+ long getSequence_number()
+ {
+ return sequence_number;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DTLSHandshakeRetransmit.java b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSHandshakeRetransmit.java
new file mode 100644
index 00000000..251d3a28
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSHandshakeRetransmit.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+interface DTLSHandshakeRetransmit
+{
+ void receivedHandshakeRecord(int epoch, byte[] buf, int off, int len)
+ throws IOException;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DTLSProtocol.java b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSProtocol.java
new file mode 100644
index 00000000..2789b22d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSProtocol.java
@@ -0,0 +1,84 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.SecureRandom;
+import java.util.Vector;
+
+import org.bouncycastle.util.Arrays;
+
+public abstract class DTLSProtocol
+{
+
+ protected final SecureRandom secureRandom;
+
+ protected DTLSProtocol(SecureRandom secureRandom)
+ {
+
+ if (secureRandom == null)
+ {
+ throw new IllegalArgumentException("'secureRandom' cannot be null");
+ }
+
+ this.secureRandom = secureRandom;
+ }
+
+ protected void processFinished(byte[] body, byte[] expected_verify_data)
+ throws IOException
+ {
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(body);
+
+ byte[] verify_data = TlsUtils.readFully(expected_verify_data.length, buf);
+
+ TlsProtocol.assertEmpty(buf);
+
+ if (!Arrays.constantTimeAreEqual(expected_verify_data, verify_data))
+ {
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ }
+
+ protected static byte[] generateCertificate(Certificate certificate)
+ throws IOException
+ {
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ certificate.encode(buf);
+ return buf.toByteArray();
+ }
+
+ protected static byte[] generateSupplementalData(Vector supplementalData)
+ throws IOException
+ {
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ TlsProtocol.writeSupplementalData(buf, supplementalData);
+ return buf.toByteArray();
+ }
+
+ protected static void validateSelectedCipherSuite(int selectedCipherSuite, short alertDescription)
+ throws IOException
+ {
+
+ switch (selectedCipherSuite)
+ {
+ case CipherSuite.TLS_RSA_EXPORT_WITH_RC4_40_MD5:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_DH_anon_EXPORT_WITH_RC4_40_MD5:
+ case CipherSuite.TLS_DH_anon_WITH_RC4_128_MD5:
+ case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDH_anon_WITH_RC4_128_SHA:
+ // TODO Alert
+ throw new IllegalStateException("RC4 MUST NOT be used with DTLS");
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DTLSReassembler.java b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSReassembler.java
new file mode 100644
index 00000000..d82bcc97
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSReassembler.java
@@ -0,0 +1,136 @@
+package org.bouncycastle.crypto.tls;
+
+import java.util.Vector;
+
+class DTLSReassembler
+{
+
+ private final short msg_type;
+ private final byte[] body;
+
+ private Vector missing = new Vector();
+
+ DTLSReassembler(short msg_type, int length)
+ {
+ this.msg_type = msg_type;
+ this.body = new byte[length];
+ this.missing.addElement(new Range(0, length));
+ }
+
+ short getType()
+ {
+ return msg_type;
+ }
+
+ byte[] getBodyIfComplete()
+ {
+ return missing.isEmpty() ? body : null;
+ }
+
+ void contributeFragment(short msg_type, int length, byte[] buf, int off, int fragment_offset,
+ int fragment_length)
+ {
+
+ int fragment_end = fragment_offset + fragment_length;
+
+ if (this.msg_type != msg_type || this.body.length != length || fragment_end > length)
+ {
+ return;
+ }
+
+ if (fragment_length == 0)
+ {
+ // NOTE: Empty messages still require an empty fragment to complete it
+ if (fragment_offset == 0 && !missing.isEmpty())
+ {
+ Range firstRange = (Range)missing.firstElement();
+ if (firstRange.getEnd() == 0)
+ {
+ missing.removeElementAt(0);
+ }
+ }
+ return;
+ }
+
+ for (int i = 0; i < missing.size(); ++i)
+ {
+ Range range = (Range)missing.elementAt(i);
+ if (range.getStart() >= fragment_end)
+ {
+ break;
+ }
+ if (range.getEnd() > fragment_offset)
+ {
+
+ int copyStart = Math.max(range.getStart(), fragment_offset);
+ int copyEnd = Math.min(range.getEnd(), fragment_end);
+ int copyLength = copyEnd - copyStart;
+
+ System.arraycopy(buf, off + copyStart - fragment_offset, body, copyStart,
+ copyLength);
+
+ if (copyStart == range.getStart())
+ {
+ if (copyEnd == range.getEnd())
+ {
+ missing.removeElementAt(i--);
+ }
+ else
+ {
+ range.setStart(copyEnd);
+ }
+ }
+ else
+ {
+ if (copyEnd == range.getEnd())
+ {
+ range.setEnd(copyStart);
+ }
+ else
+ {
+ missing.insertElementAt(new Range(copyEnd, range.getEnd()), ++i);
+ range.setEnd(copyStart);
+ }
+ }
+ }
+ }
+ }
+
+ void reset()
+ {
+ this.missing.removeAllElements();
+ this.missing.addElement(new Range(0, body.length));
+ }
+
+ private static class Range
+ {
+
+ private int start, end;
+
+ Range(int start, int end)
+ {
+ this.start = start;
+ this.end = end;
+ }
+
+ public int getStart()
+ {
+ return start;
+ }
+
+ public void setStart(int start)
+ {
+ this.start = start;
+ }
+
+ public int getEnd()
+ {
+ return end;
+ }
+
+ public void setEnd(int end)
+ {
+ this.end = end;
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java
new file mode 100644
index 00000000..3fde01aa
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java
@@ -0,0 +1,497 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+class DTLSRecordLayer
+ implements DatagramTransport
+{
+
+ private static final int RECORD_HEADER_LENGTH = 13;
+ private static final int MAX_FRAGMENT_LENGTH = 1 << 14;
+ private static final long TCP_MSL = 1000L * 60 * 2;
+ private static final long RETRANSMIT_TIMEOUT = TCP_MSL * 2;
+
+ private final DatagramTransport transport;
+ private final TlsContext context;
+ private final TlsPeer peer;
+
+ private final ByteQueue recordQueue = new ByteQueue();
+
+ private volatile boolean closed = false;
+ private volatile boolean failed = false;
+ private volatile ProtocolVersion discoveredPeerVersion = null;
+ private volatile boolean inHandshake;
+ private DTLSEpoch currentEpoch, pendingEpoch;
+ private DTLSEpoch readEpoch, writeEpoch;
+
+ private DTLSHandshakeRetransmit retransmit = null;
+ private DTLSEpoch retransmitEpoch = null;
+ private long retransmitExpiry = 0;
+
+ DTLSRecordLayer(DatagramTransport transport, TlsContext context, TlsPeer peer, short contentType)
+ {
+ this.transport = transport;
+ this.context = context;
+ this.peer = peer;
+
+ this.inHandshake = true;
+
+ this.currentEpoch = new DTLSEpoch(0, new TlsNullCipher(context));
+ this.pendingEpoch = null;
+ this.readEpoch = currentEpoch;
+ this.writeEpoch = currentEpoch;
+ }
+
+ ProtocolVersion getDiscoveredPeerVersion()
+ {
+ return discoveredPeerVersion;
+ }
+
+ void initPendingEpoch(TlsCipher pendingCipher)
+ {
+ if (pendingEpoch != null)
+ {
+ throw new IllegalStateException();
+ }
+
+ /*
+ * TODO "In order to ensure that any given sequence/epoch pair is unique, implementations
+ * MUST NOT allow the same epoch value to be reused within two times the TCP maximum segment
+ * lifetime."
+ */
+
+ // TODO Check for overflow
+ this.pendingEpoch = new DTLSEpoch(writeEpoch.getEpoch() + 1, pendingCipher);
+ }
+
+ void handshakeSuccessful(DTLSHandshakeRetransmit retransmit)
+ {
+ if (readEpoch == currentEpoch || writeEpoch == currentEpoch)
+ {
+ // TODO
+ throw new IllegalStateException();
+ }
+
+ if (retransmit != null)
+ {
+ this.retransmit = retransmit;
+ this.retransmitEpoch = currentEpoch;
+ this.retransmitExpiry = System.currentTimeMillis() + RETRANSMIT_TIMEOUT;
+ }
+
+ this.inHandshake = false;
+ this.currentEpoch = pendingEpoch;
+ this.pendingEpoch = null;
+ }
+
+ void resetWriteEpoch()
+ {
+ if (retransmitEpoch != null)
+ {
+ this.writeEpoch = retransmitEpoch;
+ }
+ else
+ {
+ this.writeEpoch = currentEpoch;
+ }
+ }
+
+ public int getReceiveLimit()
+ throws IOException
+ {
+ return Math.min(MAX_FRAGMENT_LENGTH,
+ readEpoch.getCipher().getPlaintextLimit(transport.getReceiveLimit() - RECORD_HEADER_LENGTH));
+ }
+
+ public int getSendLimit()
+ throws IOException
+ {
+ return Math.min(MAX_FRAGMENT_LENGTH,
+ writeEpoch.getCipher().getPlaintextLimit(transport.getSendLimit() - RECORD_HEADER_LENGTH));
+ }
+
+ public int receive(byte[] buf, int off, int len, int waitMillis)
+ throws IOException
+ {
+
+ byte[] record = null;
+
+ for (; ; )
+ {
+
+ int receiveLimit = Math.min(len, getReceiveLimit()) + RECORD_HEADER_LENGTH;
+ if (record == null || record.length < receiveLimit)
+ {
+ record = new byte[receiveLimit];
+ }
+
+ try
+ {
+ if (retransmit != null && System.currentTimeMillis() > retransmitExpiry)
+ {
+ retransmit = null;
+ retransmitEpoch = null;
+ }
+
+ int received = receiveRecord(record, 0, receiveLimit, waitMillis);
+ if (received < 0)
+ {
+ return received;
+ }
+ if (received < RECORD_HEADER_LENGTH)
+ {
+ continue;
+ }
+ int length = TlsUtils.readUint16(record, 11);
+ if (received != (length + RECORD_HEADER_LENGTH))
+ {
+ continue;
+ }
+
+ short type = TlsUtils.readUint8(record, 0);
+
+ // TODO Support user-specified custom protocols?
+ switch (type)
+ {
+ case ContentType.alert:
+ case ContentType.application_data:
+ case ContentType.change_cipher_spec:
+ case ContentType.handshake:
+ break;
+ default:
+ // TODO Exception?
+ continue;
+ }
+
+ int epoch = TlsUtils.readUint16(record, 3);
+
+ DTLSEpoch recordEpoch = null;
+ if (epoch == readEpoch.getEpoch())
+ {
+ recordEpoch = readEpoch;
+ }
+ else if (type == ContentType.handshake && retransmitEpoch != null
+ && epoch == retransmitEpoch.getEpoch())
+ {
+ recordEpoch = retransmitEpoch;
+ }
+
+ if (recordEpoch == null)
+ {
+ continue;
+ }
+
+ long seq = TlsUtils.readUint48(record, 5);
+ if (recordEpoch.getReplayWindow().shouldDiscard(seq))
+ {
+ continue;
+ }
+
+ ProtocolVersion version = TlsUtils.readVersion(record, 1);
+ if (discoveredPeerVersion != null && !discoveredPeerVersion.equals(version))
+ {
+ continue;
+ }
+
+ byte[] plaintext = recordEpoch.getCipher().decodeCiphertext(
+ getMacSequenceNumber(recordEpoch.getEpoch(), seq), type, record, RECORD_HEADER_LENGTH,
+ received - RECORD_HEADER_LENGTH);
+
+ recordEpoch.getReplayWindow().reportAuthenticated(seq);
+
+ if (discoveredPeerVersion == null)
+ {
+ discoveredPeerVersion = version;
+ }
+
+ switch (type)
+ {
+ case ContentType.alert:
+ {
+
+ if (plaintext.length == 2)
+ {
+ short alertLevel = plaintext[0];
+ short alertDescription = plaintext[1];
+
+ peer.notifyAlertReceived(alertLevel, alertDescription);
+
+ if (alertLevel == AlertLevel.fatal)
+ {
+ fail(alertDescription);
+ throw new TlsFatalAlert(alertDescription);
+ }
+
+ // TODO Can close_notify be a fatal alert?
+ if (alertDescription == AlertDescription.close_notify)
+ {
+ closeTransport();
+ }
+ }
+ else
+ {
+ // TODO What exception?
+ }
+
+ continue;
+ }
+ case ContentType.application_data:
+ {
+ if (inHandshake)
+ {
+ // TODO Consider buffering application data for new epoch that arrives
+ // out-of-order with the Finished message
+ continue;
+ }
+ break;
+ }
+ case ContentType.change_cipher_spec:
+ {
+ // Implicitly receive change_cipher_spec and change to pending cipher state
+
+ if (plaintext.length != 1 || plaintext[0] != 1)
+ {
+ continue;
+ }
+
+ if (pendingEpoch != null)
+ {
+ readEpoch = pendingEpoch;
+ }
+
+ continue;
+ }
+ case ContentType.handshake:
+ {
+ if (!inHandshake)
+ {
+ if (retransmit != null)
+ {
+ retransmit.receivedHandshakeRecord(epoch, plaintext, 0, plaintext.length);
+ }
+
+ // TODO Consider support for HelloRequest
+ continue;
+ }
+ }
+ }
+
+ /*
+ * NOTE: If we receive any non-handshake data in the new epoch implies the peer has
+ * received our final flight.
+ */
+ if (!inHandshake && retransmit != null)
+ {
+ this.retransmit = null;
+ this.retransmitEpoch = null;
+ }
+
+ System.arraycopy(plaintext, 0, buf, off, plaintext.length);
+ return plaintext.length;
+ }
+ catch (IOException e)
+ {
+ // NOTE: Assume this is a timeout for the moment
+ throw e;
+ }
+ }
+ }
+
+ public void send(byte[] buf, int off, int len)
+ throws IOException
+ {
+
+ short contentType = ContentType.application_data;
+
+ if (this.inHandshake || this.writeEpoch == this.retransmitEpoch)
+ {
+
+ contentType = ContentType.handshake;
+
+ short handshakeType = TlsUtils.readUint8(buf, off);
+ if (handshakeType == HandshakeType.finished)
+ {
+
+ DTLSEpoch nextEpoch = null;
+ if (this.inHandshake)
+ {
+ nextEpoch = pendingEpoch;
+ }
+ else if (this.writeEpoch == this.retransmitEpoch)
+ {
+ nextEpoch = currentEpoch;
+ }
+
+ if (nextEpoch == null)
+ {
+ // TODO
+ throw new IllegalStateException();
+ }
+
+ // Implicitly send change_cipher_spec and change to pending cipher state
+
+ // TODO Send change_cipher_spec and finished records in single datagram?
+ byte[] data = new byte[]{1};
+ sendRecord(ContentType.change_cipher_spec, data, 0, data.length);
+
+ writeEpoch = nextEpoch;
+ }
+ }
+
+ sendRecord(contentType, buf, off, len);
+ }
+
+ public void close()
+ throws IOException
+ {
+ if (!closed)
+ {
+ if (inHandshake)
+ {
+ warn(AlertDescription.user_canceled, "User canceled handshake");
+ }
+ closeTransport();
+ }
+ }
+
+ void fail(short alertDescription)
+ {
+ if (!closed)
+ {
+ try
+ {
+ raiseAlert(AlertLevel.fatal, alertDescription, null, null);
+ }
+ catch (Exception e)
+ {
+ // Ignore
+ }
+
+ failed = true;
+
+ closeTransport();
+ }
+ }
+
+ void warn(short alertDescription, String message)
+ throws IOException
+ {
+ raiseAlert(AlertLevel.warning, alertDescription, message, null);
+ }
+
+ private void closeTransport()
+ {
+ if (!closed)
+ {
+ /*
+ * RFC 5246 7.2.1. Unless some other fatal alert has been transmitted, each party is
+ * required to send a close_notify alert before closing the write side of the
+ * connection. The other party MUST respond with a close_notify alert of its own and
+ * close down the connection immediately, discarding any pending writes.
+ */
+
+ try
+ {
+ if (!failed)
+ {
+ warn(AlertDescription.close_notify, null);
+ }
+ transport.close();
+ }
+ catch (Exception e)
+ {
+ // Ignore
+ }
+
+ closed = true;
+ }
+ }
+
+ private void raiseAlert(short alertLevel, short alertDescription, String message, Exception cause)
+ throws IOException
+ {
+
+ peer.notifyAlertRaised(alertLevel, alertDescription, message, cause);
+
+ byte[] error = new byte[2];
+ error[0] = (byte)alertLevel;
+ error[1] = (byte)alertDescription;
+
+ sendRecord(ContentType.alert, error, 0, 2);
+ }
+
+ private int receiveRecord(byte[] buf, int off, int len, int waitMillis)
+ throws IOException
+ {
+ if (recordQueue.size() > 0)
+ {
+ int length = 0;
+ if (recordQueue.size() >= RECORD_HEADER_LENGTH)
+ {
+ byte[] lengthBytes = new byte[2];
+ recordQueue.read(lengthBytes, 0, 2, 11);
+ length = TlsUtils.readUint16(lengthBytes, 0);
+ }
+
+ int received = Math.min(recordQueue.size(), RECORD_HEADER_LENGTH + length);
+ recordQueue.read(buf, off, received, 0);
+ recordQueue.removeData(received);
+ return received;
+ }
+
+ int received = transport.receive(buf, off, len, waitMillis);
+ if (received >= RECORD_HEADER_LENGTH)
+ {
+ int fragmentLength = TlsUtils.readUint16(buf, off + 11);
+ int recordLength = RECORD_HEADER_LENGTH + fragmentLength;
+ if (received > recordLength)
+ {
+ recordQueue.addData(buf, off + recordLength, received - recordLength);
+ received = recordLength;
+ }
+ }
+
+ return received;
+ }
+
+ private void sendRecord(short contentType, byte[] buf, int off, int len)
+ throws IOException
+ {
+
+ /*
+ * RFC 5264 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert,
+ * or ChangeCipherSpec content types.
+ */
+ if (len < 1 && contentType != ContentType.application_data)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ int recordEpoch = writeEpoch.getEpoch();
+ long recordSequenceNumber = writeEpoch.allocateSequenceNumber();
+
+ byte[] ciphertext = writeEpoch.getCipher().encodePlaintext(
+ getMacSequenceNumber(recordEpoch, recordSequenceNumber), contentType, buf, off, len);
+
+ if (ciphertext.length > MAX_FRAGMENT_LENGTH)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ byte[] record = new byte[ciphertext.length + RECORD_HEADER_LENGTH];
+ TlsUtils.writeUint8(contentType, record, 0);
+ ProtocolVersion version = discoveredPeerVersion != null ? discoveredPeerVersion : context.getClientVersion();
+ TlsUtils.writeVersion(version, record, 1);
+ TlsUtils.writeUint16(recordEpoch, record, 3);
+ TlsUtils.writeUint48(recordSequenceNumber, record, 5);
+ TlsUtils.writeUint16(ciphertext.length, record, 11);
+ System.arraycopy(ciphertext, 0, record, RECORD_HEADER_LENGTH, ciphertext.length);
+
+ transport.send(record, 0, record.length);
+ }
+
+ private static long getMacSequenceNumber(int epoch, long sequence_number)
+ {
+ return ((long)epoch << 48) | sequence_number;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java
new file mode 100644
index 00000000..38192513
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java
@@ -0,0 +1,432 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.bouncycastle.util.Integers;
+
+class DTLSReliableHandshake
+{
+
+ private final static int MAX_RECEIVE_AHEAD = 10;
+
+ private final DTLSRecordLayer recordLayer;
+
+ private TlsHandshakeHash hash = new DeferredHash();
+
+ private Hashtable currentInboundFlight = new Hashtable();
+ private Hashtable previousInboundFlight = null;
+ private Vector outboundFlight = new Vector();
+ private boolean sending = true;
+
+ private int message_seq = 0, next_receive_seq = 0;
+
+ DTLSReliableHandshake(TlsContext context, DTLSRecordLayer transport)
+ {
+ this.recordLayer = transport;
+ this.hash.init(context);
+ }
+
+ void notifyHelloComplete()
+ {
+ this.hash = this.hash.commit();
+ }
+
+ byte[] getCurrentHash()
+ {
+ TlsHandshakeHash copyOfHash = hash.fork();
+ byte[] result = new byte[copyOfHash.getDigestSize()];
+ copyOfHash.doFinal(result, 0);
+ return result;
+ }
+
+ void sendMessage(short msg_type, byte[] body)
+ throws IOException
+ {
+
+ if (!sending)
+ {
+ checkInboundFlight();
+ sending = true;
+ outboundFlight.removeAllElements();
+ }
+
+ Message message = new Message(message_seq++, msg_type, body);
+
+ outboundFlight.addElement(message);
+
+ writeMessage(message);
+ updateHandshakeMessagesDigest(message);
+ }
+
+ Message receiveMessage()
+ throws IOException
+ {
+
+ if (sending)
+ {
+ sending = false;
+ prepareInboundFlight();
+ }
+
+ // Check if we already have the next message waiting
+ {
+ DTLSReassembler next = (DTLSReassembler)currentInboundFlight.get(Integers.valueOf(next_receive_seq));
+ if (next != null)
+ {
+ byte[] body = next.getBodyIfComplete();
+ if (body != null)
+ {
+ previousInboundFlight = null;
+ return updateHandshakeMessagesDigest(new Message(next_receive_seq++, next.getType(), body));
+ }
+ }
+ }
+
+ byte[] buf = null;
+
+ // TODO Check the conditions under which we should reset this
+ int readTimeoutMillis = 1000;
+
+ for (; ; )
+ {
+
+ int receiveLimit = recordLayer.getReceiveLimit();
+ if (buf == null || buf.length < receiveLimit)
+ {
+ buf = new byte[receiveLimit];
+ }
+
+ // TODO Handle records containing multiple handshake messages
+
+ try
+ {
+ for (; ; )
+ {
+ int received = recordLayer.receive(buf, 0, receiveLimit, readTimeoutMillis);
+ if (received < 0)
+ {
+ break;
+ }
+ if (received < 12)
+ {
+ continue;
+ }
+ int fragment_length = TlsUtils.readUint24(buf, 9);
+ if (received != (fragment_length + 12))
+ {
+ continue;
+ }
+ int seq = TlsUtils.readUint16(buf, 4);
+ if (seq > (next_receive_seq + MAX_RECEIVE_AHEAD))
+ {
+ continue;
+ }
+ short msg_type = TlsUtils.readUint8(buf, 0);
+ int length = TlsUtils.readUint24(buf, 1);
+ int fragment_offset = TlsUtils.readUint24(buf, 6);
+ if (fragment_offset + fragment_length > length)
+ {
+ continue;
+ }
+
+ if (seq < next_receive_seq)
+ {
+ /*
+ * NOTE: If we receive the previous flight of incoming messages in full
+ * again, retransmit our last flight
+ */
+ if (previousInboundFlight != null)
+ {
+ DTLSReassembler reassembler = (DTLSReassembler)previousInboundFlight.get(Integers
+ .valueOf(seq));
+ if (reassembler != null)
+ {
+
+ reassembler.contributeFragment(msg_type, length, buf, 12, fragment_offset,
+ fragment_length);
+
+ if (checkAll(previousInboundFlight))
+ {
+
+ resendOutboundFlight();
+
+ /*
+ * TODO[DTLS] implementations SHOULD back off handshake packet
+ * size during the retransmit backoff.
+ */
+ readTimeoutMillis = Math.min(readTimeoutMillis * 2, 60000);
+
+ resetAll(previousInboundFlight);
+ }
+ }
+ }
+ }
+ else
+ {
+
+ DTLSReassembler reassembler = (DTLSReassembler)currentInboundFlight.get(Integers.valueOf(seq));
+ if (reassembler == null)
+ {
+ reassembler = new DTLSReassembler(msg_type, length);
+ currentInboundFlight.put(Integers.valueOf(seq), reassembler);
+ }
+
+ reassembler.contributeFragment(msg_type, length, buf, 12, fragment_offset, fragment_length);
+
+ if (seq == next_receive_seq)
+ {
+ byte[] body = reassembler.getBodyIfComplete();
+ if (body != null)
+ {
+ previousInboundFlight = null;
+ return updateHandshakeMessagesDigest(new Message(next_receive_seq++,
+ reassembler.getType(), body));
+ }
+ }
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ // NOTE: Assume this is a timeout for the moment
+ }
+
+ resendOutboundFlight();
+
+ /*
+ * TODO[DTLS] implementations SHOULD back off handshake packet size during the
+ * retransmit backoff.
+ */
+ readTimeoutMillis = Math.min(readTimeoutMillis * 2, 60000);
+ }
+ }
+
+ void finish()
+ {
+ DTLSHandshakeRetransmit retransmit = null;
+ if (!sending)
+ {
+ checkInboundFlight();
+ }
+ else if (currentInboundFlight != null)
+ {
+ /*
+ * RFC 6347 4.2.4. In addition, for at least twice the default MSL defined for [TCP],
+ * when in the FINISHED state, the node that transmits the last flight (the server in an
+ * ordinary handshake or the client in a resumed handshake) MUST respond to a retransmit
+ * of the peer's last flight with a retransmit of the last flight.
+ */
+ retransmit = new DTLSHandshakeRetransmit()
+ {
+ public void receivedHandshakeRecord(int epoch, byte[] buf, int off, int len)
+ throws IOException
+ {
+ /*
+ * TODO Need to handle the case where the previous inbound flight contains
+ * messages from two epochs.
+ */
+ if (len < 12)
+ {
+ return;
+ }
+ int fragment_length = TlsUtils.readUint24(buf, off + 9);
+ if (len != (fragment_length + 12))
+ {
+ return;
+ }
+ int seq = TlsUtils.readUint16(buf, off + 4);
+ if (seq >= next_receive_seq)
+ {
+ return;
+ }
+
+ short msg_type = TlsUtils.readUint8(buf, off);
+
+ // TODO This is a hack that only works until we try to support renegotiation
+ int expectedEpoch = msg_type == HandshakeType.finished ? 1 : 0;
+ if (epoch != expectedEpoch)
+ {
+ return;
+ }
+
+ int length = TlsUtils.readUint24(buf, off + 1);
+ int fragment_offset = TlsUtils.readUint24(buf, off + 6);
+ if (fragment_offset + fragment_length > length)
+ {
+ return;
+ }
+
+ DTLSReassembler reassembler = (DTLSReassembler)currentInboundFlight.get(Integers.valueOf(seq));
+ if (reassembler != null)
+ {
+ reassembler.contributeFragment(msg_type, length, buf, off + 12, fragment_offset,
+ fragment_length);
+ if (checkAll(currentInboundFlight))
+ {
+ resendOutboundFlight();
+ resetAll(currentInboundFlight);
+ }
+ }
+ }
+ };
+ }
+
+ recordLayer.handshakeSuccessful(retransmit);
+ }
+
+ void resetHandshakeMessagesDigest()
+ {
+ hash.reset();
+ }
+
+ /**
+ * Check that there are no "extra" messages left in the current inbound flight
+ */
+ private void checkInboundFlight()
+ {
+ Enumeration e = currentInboundFlight.keys();
+ while (e.hasMoreElements())
+ {
+ Integer key = (Integer)e.nextElement();
+ if (key.intValue() >= next_receive_seq)
+ {
+ // TODO Should this be considered an error?
+ }
+ }
+ }
+
+ private void prepareInboundFlight()
+ {
+ resetAll(currentInboundFlight);
+ previousInboundFlight = currentInboundFlight;
+ currentInboundFlight = new Hashtable();
+ }
+
+ private void resendOutboundFlight()
+ throws IOException
+ {
+ recordLayer.resetWriteEpoch();
+ for (int i = 0; i < outboundFlight.size(); ++i)
+ {
+ writeMessage((Message)outboundFlight.elementAt(i));
+ }
+ }
+
+ private Message updateHandshakeMessagesDigest(Message message)
+ throws IOException
+ {
+ if (message.getType() != HandshakeType.hello_request)
+ {
+ byte[] body = message.getBody();
+ byte[] buf = new byte[12];
+ TlsUtils.writeUint8(message.getType(), buf, 0);
+ TlsUtils.writeUint24(body.length, buf, 1);
+ TlsUtils.writeUint16(message.getSeq(), buf, 4);
+ TlsUtils.writeUint24(0, buf, 6);
+ TlsUtils.writeUint24(body.length, buf, 9);
+ hash.update(buf, 0, buf.length);
+ hash.update(body, 0, body.length);
+ }
+ return message;
+ }
+
+ private void writeMessage(Message message)
+ throws IOException
+ {
+
+ int sendLimit = recordLayer.getSendLimit();
+ int fragmentLimit = sendLimit - 12;
+
+ // TODO Support a higher minimum fragment size?
+ if (fragmentLimit < 1)
+ {
+ // TODO Should we be throwing an exception here?
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ int length = message.getBody().length;
+
+ // NOTE: Must still send a fragment if body is empty
+ int fragment_offset = 0;
+ do
+ {
+ int fragment_length = Math.min(length - fragment_offset, fragmentLimit);
+ writeHandshakeFragment(message, fragment_offset, fragment_length);
+ fragment_offset += fragment_length;
+ }
+ while (fragment_offset < length);
+ }
+
+ private void writeHandshakeFragment(Message message, int fragment_offset, int fragment_length)
+ throws IOException
+ {
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ TlsUtils.writeUint8(message.getType(), buf);
+ TlsUtils.writeUint24(message.getBody().length, buf);
+ TlsUtils.writeUint16(message.getSeq(), buf);
+ TlsUtils.writeUint24(fragment_offset, buf);
+ TlsUtils.writeUint24(fragment_length, buf);
+ buf.write(message.getBody(), fragment_offset, fragment_length);
+
+ byte[] fragment = buf.toByteArray();
+
+ recordLayer.send(fragment, 0, fragment.length);
+ }
+
+ private static boolean checkAll(Hashtable inboundFlight)
+ {
+ Enumeration e = inboundFlight.elements();
+ while (e.hasMoreElements())
+ {
+ if (((DTLSReassembler)e.nextElement()).getBodyIfComplete() == null)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static void resetAll(Hashtable inboundFlight)
+ {
+ Enumeration e = inboundFlight.elements();
+ while (e.hasMoreElements())
+ {
+ ((DTLSReassembler)e.nextElement()).reset();
+ }
+ }
+
+ static class Message
+ {
+
+ private final int message_seq;
+ private final short msg_type;
+ private final byte[] body;
+
+ private Message(int message_seq, short msg_type, byte[] body)
+ {
+ this.message_seq = message_seq;
+ this.msg_type = msg_type;
+ this.body = body;
+ }
+
+ public int getSeq()
+ {
+ return message_seq;
+ }
+
+ public short getType()
+ {
+ return msg_type;
+ }
+
+ public byte[] getBody()
+ {
+ return body;
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DTLSReplayWindow.java b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSReplayWindow.java
new file mode 100644
index 00000000..0a5325be
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSReplayWindow.java
@@ -0,0 +1,91 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 4347 4.1.2.5 Anti-replay
+ * <p/>
+ * Support fast rejection of duplicate records by maintaining a sliding receive window
+ */
+class DTLSReplayWindow
+{
+
+ private static final long VALID_SEQ_MASK = 0x0000FFFFFFFFFFFFL;
+
+ private static final long WINDOW_SIZE = 64L;
+
+ private long latestConfirmedSeq = -1;
+ private long bitmap = 0;
+
+ /**
+ * Check whether a received record with the given sequence number should be rejected as a duplicate.
+ *
+ * @param seq the 48-bit DTLSPlainText.sequence_number field of a received record.
+ * @return true if the record should be discarded without further processing.
+ */
+ boolean shouldDiscard(long seq)
+ {
+ if ((seq & VALID_SEQ_MASK) != seq)
+ {
+ return true;
+ }
+
+ if (seq <= latestConfirmedSeq)
+ {
+ long diff = latestConfirmedSeq - seq;
+ if (diff >= WINDOW_SIZE)
+ {
+ return true;
+ }
+ if ((bitmap & (1L << diff)) != 0)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Report that a received record with the given sequence number passed authentication checks.
+ *
+ * @param seq the 48-bit DTLSPlainText.sequence_number field of an authenticated record.
+ */
+ void reportAuthenticated(long seq)
+ {
+ if ((seq & VALID_SEQ_MASK) != seq)
+ {
+ throw new IllegalArgumentException("'seq' out of range");
+ }
+
+ if (seq <= latestConfirmedSeq)
+ {
+ long diff = latestConfirmedSeq - seq;
+ if (diff < WINDOW_SIZE)
+ {
+ bitmap |= (1L << diff);
+ }
+ }
+ else
+ {
+ long diff = seq - latestConfirmedSeq;
+ if (diff >= WINDOW_SIZE)
+ {
+ bitmap = 1;
+ }
+ else
+ {
+ bitmap <<= (int)diff; // for earlier JDKs
+ bitmap |= 1;
+ }
+ latestConfirmedSeq = seq;
+ }
+ }
+
+ /**
+ * When a new epoch begins, sequence numbers begin again at 0
+ */
+ void reset()
+ {
+ latestConfirmedSeq = -1;
+ bitmap = 0;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java
new file mode 100644
index 00000000..3a100d1c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java
@@ -0,0 +1,631 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.SecureRandom;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.util.PublicKeyFactory;
+import org.bouncycastle.util.Arrays;
+
+public class DTLSServerProtocol
+ extends DTLSProtocol
+{
+
+ protected boolean verifyRequests = true;
+
+ public DTLSServerProtocol(SecureRandom secureRandom)
+ {
+ super(secureRandom);
+ }
+
+ public boolean getVerifyRequests()
+ {
+ return verifyRequests;
+ }
+
+ public void setVerifyRequests(boolean verifyRequests)
+ {
+ this.verifyRequests = verifyRequests;
+ }
+
+ public DTLSTransport accept(TlsServer server, DatagramTransport transport)
+ throws IOException
+ {
+
+ if (server == null)
+ {
+ throw new IllegalArgumentException("'server' cannot be null");
+ }
+ if (transport == null)
+ {
+ throw new IllegalArgumentException("'transport' cannot be null");
+ }
+
+ SecurityParameters securityParameters = new SecurityParameters();
+ securityParameters.entity = ConnectionEnd.server;
+ securityParameters.serverRandom = TlsProtocol.createRandomBlock(secureRandom);
+
+ ServerHandshakeState state = new ServerHandshakeState();
+ state.server = server;
+ state.serverContext = new TlsServerContextImpl(secureRandom, securityParameters);
+ server.init(state.serverContext);
+
+ DTLSRecordLayer recordLayer = new DTLSRecordLayer(transport, state.serverContext, server, ContentType.handshake);
+
+ // TODO Need to handle sending of HelloVerifyRequest without entering a full connection
+
+ try
+ {
+ return serverHandshake(state, recordLayer);
+ }
+ catch (TlsFatalAlert fatalAlert)
+ {
+ recordLayer.fail(fatalAlert.getAlertDescription());
+ throw fatalAlert;
+ }
+ catch (IOException e)
+ {
+ recordLayer.fail(AlertDescription.internal_error);
+ throw e;
+ }
+ catch (RuntimeException e)
+ {
+ recordLayer.fail(AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public DTLSTransport serverHandshake(ServerHandshakeState state, DTLSRecordLayer recordLayer)
+ throws IOException
+ {
+
+ SecurityParameters securityParameters = state.serverContext.getSecurityParameters();
+ DTLSReliableHandshake handshake = new DTLSReliableHandshake(state.serverContext, recordLayer);
+
+ DTLSReliableHandshake.Message clientMessage = handshake.receiveMessage();
+
+ {
+ // NOTE: After receiving a record from the client, we discover the record layer version
+ ProtocolVersion client_version = recordLayer.getDiscoveredPeerVersion();
+ // TODO Read RFCs for guidance on the expected record layer version number
+ state.serverContext.setClientVersion(client_version);
+ }
+
+ if (clientMessage.getType() == HandshakeType.client_hello)
+ {
+ processClientHello(state, clientMessage.getBody());
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ byte[] serverHelloBody = generateServerHello(state);
+ handshake.sendMessage(HandshakeType.server_hello, serverHelloBody);
+
+ // TODO This block could really be done before actually sending the hello
+ {
+ securityParameters.prfAlgorithm = TlsProtocol.getPRFAlgorithm(state.selectedCipherSuite);
+ securityParameters.compressionAlgorithm = state.selectedCompressionMethod;
+
+ /*
+ * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify verify_data_length
+ * has a verify_data_length equal to 12. This includes all existing cipher suites.
+ */
+ securityParameters.verifyDataLength = 12;
+
+ handshake.notifyHelloComplete();
+ }
+
+ Vector serverSupplementalData = state.server.getServerSupplementalData();
+ if (serverSupplementalData != null)
+ {
+ byte[] supplementalDataBody = generateSupplementalData(serverSupplementalData);
+ handshake.sendMessage(HandshakeType.supplemental_data, supplementalDataBody);
+ }
+
+ state.keyExchange = state.server.getKeyExchange();
+ state.keyExchange.init(state.serverContext);
+
+ state.serverCredentials = state.server.getCredentials();
+ if (state.serverCredentials == null)
+ {
+ state.keyExchange.skipServerCredentials();
+ }
+ else
+ {
+ state.keyExchange.processServerCredentials(state.serverCredentials);
+
+ byte[] certificateBody = generateCertificate(state.serverCredentials.getCertificate());
+ handshake.sendMessage(HandshakeType.certificate, certificateBody);
+ }
+
+ byte[] serverKeyExchange = state.keyExchange.generateServerKeyExchange();
+ if (serverKeyExchange != null)
+ {
+ handshake.sendMessage(HandshakeType.server_key_exchange, serverKeyExchange);
+ }
+
+ if (state.serverCredentials != null)
+ {
+ state.certificateRequest = state.server.getCertificateRequest();
+ if (state.certificateRequest != null)
+ {
+ state.keyExchange.validateCertificateRequest(state.certificateRequest);
+
+ byte[] certificateRequestBody = generateCertificateRequest(state, state.certificateRequest);
+ handshake.sendMessage(HandshakeType.certificate_request, certificateRequestBody);
+ }
+ }
+
+ handshake.sendMessage(HandshakeType.server_hello_done, TlsUtils.EMPTY_BYTES);
+
+ clientMessage = handshake.receiveMessage();
+
+ if (clientMessage.getType() == HandshakeType.supplemental_data)
+ {
+ processClientSupplementalData(state, clientMessage.getBody());
+ clientMessage = handshake.receiveMessage();
+ }
+ else
+ {
+ state.server.processClientSupplementalData(null);
+ }
+
+ if (state.certificateRequest == null)
+ {
+ state.keyExchange.skipClientCredentials();
+ }
+ else
+ {
+ if (clientMessage.getType() == HandshakeType.certificate)
+ {
+ processClientCertificate(state, clientMessage.getBody());
+ clientMessage = handshake.receiveMessage();
+ }
+ else
+ {
+ ProtocolVersion equivalentTLSVersion = state.serverContext.getServerVersion().getEquivalentTLSVersion();
+
+ if (ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(equivalentTLSVersion))
+ {
+ /*
+ * RFC 5246 If no suitable certificate is available, the client MUST send a
+ * certificate message containing no certificates.
+ *
+ * NOTE: In previous RFCs, this was SHOULD instead of MUST.
+ */
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ notifyClientCertificate(state, Certificate.EMPTY_CHAIN);
+ }
+ }
+
+ if (clientMessage.getType() == HandshakeType.client_key_exchange)
+ {
+ processClientKeyExchange(state, clientMessage.getBody());
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ recordLayer.initPendingEpoch(state.server.getCipher());
+
+ /*
+ * RFC 5246 7.4.8 This message is only sent following a client certificate that has signing
+ * capability (i.e., all certificates except those containing fixed Diffie-Hellman
+ * parameters).
+ */
+ if (expectCertificateVerifyMessage(state))
+ {
+ byte[] certificateVerifyHash = handshake.getCurrentHash();
+ clientMessage = handshake.receiveMessage();
+
+ if (clientMessage.getType() == HandshakeType.certificate_verify)
+ {
+ processCertificateVerify(state, clientMessage.getBody(), certificateVerifyHash);
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+
+ // NOTE: Calculated exclusive of the actual Finished message from the client
+ byte[] clientFinishedHash = handshake.getCurrentHash();
+ clientMessage = handshake.receiveMessage();
+
+ if (clientMessage.getType() == HandshakeType.finished)
+ {
+ byte[] expectedClientVerifyData = TlsUtils.calculateVerifyData(state.serverContext, "client finished",
+ clientFinishedHash);
+ processFinished(clientMessage.getBody(), expectedClientVerifyData);
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ if (state.expectSessionTicket)
+ {
+ NewSessionTicket newSessionTicket = state.server.getNewSessionTicket();
+ byte[] newSessionTicketBody = generateNewSessionTicket(state, newSessionTicket);
+ handshake.sendMessage(HandshakeType.session_ticket, newSessionTicketBody);
+ }
+
+ // NOTE: Calculated exclusive of the Finished message itself
+ byte[] serverVerifyData = TlsUtils.calculateVerifyData(state.serverContext, "server finished",
+ handshake.getCurrentHash());
+ handshake.sendMessage(HandshakeType.finished, serverVerifyData);
+
+ handshake.finish();
+
+ state.server.notifyHandshakeComplete();
+
+ return new DTLSTransport(recordLayer);
+ }
+
+ protected byte[] generateCertificateRequest(ServerHandshakeState state, CertificateRequest certificateRequest)
+ throws IOException
+ {
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ certificateRequest.encode(buf);
+ return buf.toByteArray();
+ }
+
+ protected byte[] generateNewSessionTicket(ServerHandshakeState state, NewSessionTicket newSessionTicket)
+ throws IOException
+ {
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ newSessionTicket.encode(buf);
+ return buf.toByteArray();
+ }
+
+ protected byte[] generateServerHello(ServerHandshakeState state)
+ throws IOException
+ {
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+
+ ProtocolVersion server_version = state.server.getServerVersion();
+ if (!server_version.isEqualOrEarlierVersionOf(state.serverContext.getClientVersion()))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ // TODO Read RFCs for guidance on the expected record layer version number
+ // recordStream.setReadVersion(server_version);
+ // recordStream.setWriteVersion(server_version);
+ // recordStream.setRestrictReadVersion(true);
+ state.serverContext.setServerVersion(server_version);
+
+ TlsUtils.writeVersion(state.serverContext.getServerVersion(), buf);
+
+ buf.write(state.serverContext.getSecurityParameters().serverRandom);
+
+ /*
+ * The server may return an empty session_id to indicate that the session will not be cached
+ * and therefore cannot be resumed.
+ */
+ TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf);
+
+ state.selectedCipherSuite = state.server.getSelectedCipherSuite();
+ if (!TlsProtocol.arrayContains(state.offeredCipherSuites, state.selectedCipherSuite)
+ || state.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
+ || state.selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ validateSelectedCipherSuite(state.selectedCipherSuite, AlertDescription.internal_error);
+
+ state.selectedCompressionMethod = state.server.getSelectedCompressionMethod();
+ if (!TlsProtocol.arrayContains(state.offeredCompressionMethods, state.selectedCompressionMethod))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ TlsUtils.writeUint16(state.selectedCipherSuite, buf);
+ TlsUtils.writeUint8(state.selectedCompressionMethod, buf);
+
+ state.serverExtensions = state.server.getServerExtensions();
+
+ /*
+ * RFC 5746 3.6. Server Behavior: Initial Handshake
+ */
+ if (state.secure_renegotiation)
+ {
+
+ boolean noRenegExt = state.serverExtensions == null
+ || !state.serverExtensions.containsKey(TlsProtocol.EXT_RenegotiationInfo);
+
+ if (noRenegExt)
+ {
+ /*
+ * Note that sending a "renegotiation_info" extension in response to a ClientHello
+ * containing only the SCSV is an explicit exception to the prohibition in RFC 5246,
+ * Section 7.4.1.4, on the server sending unsolicited extensions and is only allowed
+ * because the client is signaling its willingness to receive the extension via the
+ * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.
+ */
+ if (state.serverExtensions == null)
+ {
+ state.serverExtensions = new Hashtable();
+ }
+
+ /*
+ * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty
+ * "renegotiation_info" extension in the ServerHello message.
+ */
+ state.serverExtensions.put(TlsProtocol.EXT_RenegotiationInfo,
+ TlsProtocol.createRenegotiationInfo(TlsUtils.EMPTY_BYTES));
+ }
+ }
+
+ if (state.serverExtensions != null)
+ {
+ state.expectSessionTicket = state.serverExtensions.containsKey(TlsProtocol.EXT_SessionTicket);
+ TlsProtocol.writeExtensions(buf, state.serverExtensions);
+ }
+
+ return buf.toByteArray();
+ }
+
+ protected void notifyClientCertificate(ServerHandshakeState state, Certificate clientCertificate)
+ throws IOException
+ {
+
+ if (state.certificateRequest == null)
+ {
+ throw new IllegalStateException();
+ }
+
+ if (state.clientCertificate != null)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ state.clientCertificate = clientCertificate;
+
+ if (clientCertificate.isEmpty())
+ {
+ state.keyExchange.skipClientCredentials();
+ }
+ else
+ {
+
+ /*
+ * TODO RFC 5246 7.4.6. If the certificate_authorities list in the certificate request
+ * message was non-empty, one of the certificates in the certificate chain SHOULD be
+ * issued by one of the listed CAs.
+ */
+
+ state.clientCertificateType = TlsUtils.getClientCertificateType(clientCertificate,
+ state.serverCredentials.getCertificate());
+
+ state.keyExchange.processClientCertificate(clientCertificate);
+ }
+
+ /*
+ * RFC 5246 7.4.6. If the client does not send any certificates, the server MAY at its
+ * discretion either continue the handshake without client authentication, or respond with a
+ * fatal handshake_failure alert. Also, if some aspect of the certificate chain was
+ * unacceptable (e.g., it was not signed by a known, trusted CA), the server MAY at its
+ * discretion either continue the handshake (considering the client unauthenticated) or send
+ * a fatal alert.
+ */
+ state.server.notifyClientCertificate(clientCertificate);
+ }
+
+ protected void processClientCertificate(ServerHandshakeState state, byte[] body)
+ throws IOException
+ {
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(body);
+
+ Certificate clientCertificate = Certificate.parse(buf);
+
+ TlsProtocol.assertEmpty(buf);
+
+ notifyClientCertificate(state, clientCertificate);
+ }
+
+ protected void processCertificateVerify(ServerHandshakeState state, byte[] body, byte[] certificateVerifyHash)
+ throws IOException
+ {
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(body);
+
+ byte[] clientCertificateSignature = TlsUtils.readOpaque16(buf);
+
+ TlsProtocol.assertEmpty(buf);
+
+ // Verify the CertificateVerify message contains a correct signature.
+ try
+ {
+ TlsSigner tlsSigner = TlsUtils.createTlsSigner(state.clientCertificateType);
+ tlsSigner.init(state.serverContext);
+
+ org.bouncycastle.asn1.x509.Certificate x509Cert = state.clientCertificate.getCertificateAt(0);
+ SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo();
+ AsymmetricKeyParameter publicKey = PublicKeyFactory.createKey(keyInfo);
+
+ tlsSigner.verifyRawSignature(clientCertificateSignature, publicKey, certificateVerifyHash);
+ }
+ catch (Exception e)
+ {
+ throw new TlsFatalAlert(AlertDescription.decrypt_error);
+ }
+ }
+
+ protected void processClientHello(ServerHandshakeState state, byte[] body)
+ throws IOException
+ {
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(body);
+
+ // TODO Read RFCs for guidance on the expected record layer version number
+ ProtocolVersion client_version = TlsUtils.readVersion(buf);
+ if (!client_version.isDTLS())
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ /*
+ * Read the client random
+ */
+ byte[] client_random = TlsUtils.readFully(32, buf);
+
+ byte[] sessionID = TlsUtils.readOpaque8(buf);
+ if (sessionID.length > 32)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ // TODO RFC 4347 has the cookie length restricted to 32, but not in RFC 6347
+ byte[] cookie = TlsUtils.readOpaque8(buf);
+
+ int cipher_suites_length = TlsUtils.readUint16(buf);
+ if (cipher_suites_length < 2 || (cipher_suites_length & 1) != 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ /*
+ * NOTE: "If the session_id field is not empty (implying a session resumption request) this
+ * vector must include at least the cipher_suite from that session."
+ */
+ state.offeredCipherSuites = TlsUtils.readUint16Array(cipher_suites_length / 2, buf);
+
+ int compression_methods_length = TlsUtils.readUint8(buf);
+ if (compression_methods_length < 1)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ state.offeredCompressionMethods = TlsUtils.readUint8Array(compression_methods_length, buf);
+
+ /*
+ * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore
+ * extensions appearing in the client hello, and send a server hello containing no
+ * extensions.
+ */
+ state.clientExtensions = TlsProtocol.readExtensions(buf);
+
+ state.serverContext.setClientVersion(client_version);
+
+ state.server.notifyClientVersion(client_version);
+
+ state.serverContext.getSecurityParameters().clientRandom = client_random;
+
+ state.server.notifyOfferedCipherSuites(state.offeredCipherSuites);
+ state.server.notifyOfferedCompressionMethods(state.offeredCompressionMethods);
+
+ /*
+ * RFC 5746 3.6. Server Behavior: Initial Handshake
+ */
+ {
+ /*
+ * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension,
+ * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the
+ * ClientHello. Including both is NOT RECOMMENDED.
+ */
+
+ /*
+ * When a ClientHello is received, the server MUST check if it includes the
+ * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, set the secure_renegotiation flag
+ * to TRUE.
+ */
+ if (TlsProtocol.arrayContains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV))
+ {
+ state.secure_renegotiation = true;
+ }
+
+ /*
+ * The server MUST check if the "renegotiation_info" extension is included in the
+ * ClientHello.
+ */
+ if (state.clientExtensions != null)
+ {
+ byte[] renegExtValue = (byte[])state.clientExtensions.get(TlsProtocol.EXT_RenegotiationInfo);
+ if (renegExtValue != null)
+ {
+ /*
+ * If the extension is present, set secure_renegotiation flag to TRUE. The
+ * server MUST then verify that the length of the "renegotiated_connection"
+ * field is zero, and if it is not, MUST abort the handshake.
+ */
+ state.secure_renegotiation = true;
+
+ if (!Arrays.constantTimeAreEqual(renegExtValue,
+ TlsProtocol.createRenegotiationInfo(TlsUtils.EMPTY_BYTES)))
+ {
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ }
+ }
+ }
+
+ state.server.notifySecureRenegotiation(state.secure_renegotiation);
+
+ if (state.clientExtensions != null)
+ {
+ state.server.processClientExtensions(state.clientExtensions);
+ }
+ }
+
+ protected void processClientKeyExchange(ServerHandshakeState state, byte[] body)
+ throws IOException
+ {
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(body);
+
+ state.keyExchange.processClientKeyExchange(buf);
+
+ TlsProtocol.assertEmpty(buf);
+
+ TlsProtocol.establishMasterSecret(state.serverContext, state.keyExchange);
+ }
+
+ protected void processClientSupplementalData(ServerHandshakeState state, byte[] body)
+ throws IOException
+ {
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(body);
+ Vector clientSupplementalData = TlsProtocol.readSupplementalDataMessage(buf);
+ state.server.processClientSupplementalData(clientSupplementalData);
+ }
+
+ protected boolean expectCertificateVerifyMessage(ServerHandshakeState state)
+ {
+ return state.clientCertificateType >= 0 && TlsUtils.hasSigningCapability(state.clientCertificateType);
+ }
+
+ protected static class ServerHandshakeState
+ {
+ TlsServer server = null;
+ TlsServerContextImpl serverContext = null;
+ int[] offeredCipherSuites;
+ short[] offeredCompressionMethods;
+ Hashtable clientExtensions;
+ int selectedCipherSuite = -1;
+ short selectedCompressionMethod = -1;
+ boolean secure_renegotiation = false;
+ boolean expectSessionTicket = false;
+ Hashtable serverExtensions = null;
+ TlsKeyExchange keyExchange = null;
+ TlsCredentials serverCredentials = null;
+ CertificateRequest certificateRequest = null;
+ short clientCertificateType = -1;
+ Certificate clientCertificate = null;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DTLSTransport.java b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSTransport.java
new file mode 100644
index 00000000..a67d7bd6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSTransport.java
@@ -0,0 +1,81 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+public class DTLSTransport
+ implements DatagramTransport
+{
+
+ private final DTLSRecordLayer recordLayer;
+
+ DTLSTransport(DTLSRecordLayer recordLayer)
+ {
+ this.recordLayer = recordLayer;
+ }
+
+ public int getReceiveLimit()
+ throws IOException
+ {
+ return recordLayer.getReceiveLimit();
+ }
+
+ public int getSendLimit()
+ throws IOException
+ {
+ return recordLayer.getSendLimit();
+ }
+
+ public int receive(byte[] buf, int off, int len, int waitMillis)
+ throws IOException
+ {
+ try
+ {
+ return recordLayer.receive(buf, off, len, waitMillis);
+ }
+ catch (TlsFatalAlert fatalAlert)
+ {
+ recordLayer.fail(fatalAlert.getAlertDescription());
+ throw fatalAlert;
+ }
+ catch (IOException e)
+ {
+ recordLayer.fail(AlertDescription.internal_error);
+ throw e;
+ }
+ catch (RuntimeException e)
+ {
+ recordLayer.fail(AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public void send(byte[] buf, int off, int len)
+ throws IOException
+ {
+ try
+ {
+ recordLayer.send(buf, off, len);
+ }
+ catch (TlsFatalAlert fatalAlert)
+ {
+ recordLayer.fail(fatalAlert.getAlertDescription());
+ throw fatalAlert;
+ }
+ catch (IOException e)
+ {
+ recordLayer.fail(AlertDescription.internal_error);
+ throw e;
+ }
+ catch (RuntimeException e)
+ {
+ recordLayer.fail(AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public void close()
+ throws IOException
+ {
+ recordLayer.close();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DatagramTransport.java b/core/src/main/java/org/bouncycastle/crypto/tls/DatagramTransport.java
new file mode 100644
index 00000000..df63b185
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/DatagramTransport.java
@@ -0,0 +1,22 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+public interface DatagramTransport
+{
+
+ int getReceiveLimit()
+ throws IOException;
+
+ int getSendLimit()
+ throws IOException;
+
+ int receive(byte[] buf, int off, int len, int waitMillis)
+ throws IOException;
+
+ void send(byte[] buf, int off, int len)
+ throws IOException;
+
+ void close()
+ throws IOException;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsAgreementCredentials.java b/core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsAgreementCredentials.java
new file mode 100644
index 00000000..98efc4f0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsAgreementCredentials.java
@@ -0,0 +1,79 @@
+package org.bouncycastle.crypto.tls;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.BasicAgreement;
+import org.bouncycastle.crypto.agreement.DHBasicAgreement;
+import org.bouncycastle.crypto.agreement.ECDHBasicAgreement;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.util.BigIntegers;
+
+public class DefaultTlsAgreementCredentials
+ implements TlsAgreementCredentials
+{
+
+ protected Certificate certificate;
+ protected AsymmetricKeyParameter privateKey;
+
+ protected BasicAgreement basicAgreement;
+ protected boolean truncateAgreement;
+
+ public DefaultTlsAgreementCredentials(Certificate certificate, AsymmetricKeyParameter privateKey)
+ {
+ if (certificate == null)
+ {
+ throw new IllegalArgumentException("'certificate' cannot be null");
+ }
+ if (certificate.isEmpty())
+ {
+ throw new IllegalArgumentException("'certificate' cannot be empty");
+ }
+ if (privateKey == null)
+ {
+ throw new IllegalArgumentException("'privateKey' cannot be null");
+ }
+ if (!privateKey.isPrivate())
+ {
+ throw new IllegalArgumentException("'privateKey' must be private");
+ }
+
+ if (privateKey instanceof DHPrivateKeyParameters)
+ {
+ basicAgreement = new DHBasicAgreement();
+ truncateAgreement = true;
+ }
+ else if (privateKey instanceof ECPrivateKeyParameters)
+ {
+ basicAgreement = new ECDHBasicAgreement();
+ truncateAgreement = false;
+ }
+ else
+ {
+ throw new IllegalArgumentException("'privateKey' type not supported: "
+ + privateKey.getClass().getName());
+ }
+
+ this.certificate = certificate;
+ this.privateKey = privateKey;
+ }
+
+ public Certificate getCertificate()
+ {
+ return certificate;
+ }
+
+ public byte[] generateAgreement(AsymmetricKeyParameter peerPublicKey)
+ {
+ basicAgreement.init(privateKey);
+ BigInteger agreementValue = basicAgreement.calculateAgreement(peerPublicKey);
+
+ if (truncateAgreement)
+ {
+ return BigIntegers.asUnsignedByteArray(agreementValue);
+ }
+
+ return BigIntegers.asUnsignedByteArray(basicAgreement.getFieldSize(), agreementValue);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsCipherFactory.java b/core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsCipherFactory.java
new file mode 100644
index 00000000..82b37d90
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsCipherFactory.java
@@ -0,0 +1,163 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.digests.MD5Digest;
+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.engines.AESFastEngine;
+import org.bouncycastle.crypto.engines.CamelliaEngine;
+import org.bouncycastle.crypto.engines.DESedeEngine;
+import org.bouncycastle.crypto.engines.RC4Engine;
+import org.bouncycastle.crypto.engines.SEEDEngine;
+import org.bouncycastle.crypto.modes.AEADBlockCipher;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.modes.GCMBlockCipher;
+
+public class DefaultTlsCipherFactory
+ extends AbstractTlsCipherFactory
+{
+
+ public TlsCipher createCipher(TlsContext context, int encryptionAlgorithm, int macAlgorithm)
+ throws IOException
+ {
+
+ switch (encryptionAlgorithm)
+ {
+ case EncryptionAlgorithm._3DES_EDE_CBC:
+ return createDESedeCipher(context, macAlgorithm);
+ case EncryptionAlgorithm.AES_128_CBC:
+ return createAESCipher(context, 16, macAlgorithm);
+ case EncryptionAlgorithm.AES_128_GCM:
+ // NOTE: Ignores macAlgorithm
+ return createCipher_AES_GCM(context, 16, 16);
+ case EncryptionAlgorithm.AES_256_CBC:
+ return createAESCipher(context, 32, macAlgorithm);
+ case EncryptionAlgorithm.AES_256_GCM:
+ // NOTE: Ignores macAlgorithm
+ return createCipher_AES_GCM(context, 32, 16);
+ case EncryptionAlgorithm.CAMELLIA_128_CBC:
+ return createCamelliaCipher(context, 16, macAlgorithm);
+ case EncryptionAlgorithm.CAMELLIA_256_CBC:
+ return createCamelliaCipher(context, 32, macAlgorithm);
+ case EncryptionAlgorithm.NULL:
+ return createNullCipher(context, macAlgorithm);
+ case EncryptionAlgorithm.RC4_128:
+ return createRC4Cipher(context, 16, macAlgorithm);
+ case EncryptionAlgorithm.SEED_CBC:
+ return createSEEDCipher(context, macAlgorithm);
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ protected TlsBlockCipher createAESCipher(TlsContext context, int cipherKeySize, int macAlgorithm)
+ throws IOException
+ {
+ return new TlsBlockCipher(context, createAESBlockCipher(), createAESBlockCipher(),
+ createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), cipherKeySize);
+ }
+
+ protected TlsAEADCipher createCipher_AES_GCM(TlsContext context, int cipherKeySize, int macSize)
+ throws IOException
+ {
+ return new TlsAEADCipher(context, createAEADBlockCipher_AES_GCM(),
+ createAEADBlockCipher_AES_GCM(), cipherKeySize, macSize);
+ }
+
+ protected TlsBlockCipher createCamelliaCipher(TlsContext context, int cipherKeySize,
+ int macAlgorithm)
+ throws IOException
+ {
+ return new TlsBlockCipher(context, createCamelliaBlockCipher(),
+ createCamelliaBlockCipher(), createHMACDigest(macAlgorithm),
+ createHMACDigest(macAlgorithm), cipherKeySize);
+ }
+
+ protected TlsNullCipher createNullCipher(TlsContext context, int macAlgorithm)
+ throws IOException
+ {
+ return new TlsNullCipher(context, createHMACDigest(macAlgorithm),
+ createHMACDigest(macAlgorithm));
+ }
+
+ protected TlsStreamCipher createRC4Cipher(TlsContext context, int cipherKeySize,
+ int macAlgorithm)
+ throws IOException
+ {
+ return new TlsStreamCipher(context, createRC4StreamCipher(), createRC4StreamCipher(),
+ createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), cipherKeySize);
+ }
+
+ protected TlsBlockCipher createDESedeCipher(TlsContext context, int macAlgorithm)
+ throws IOException
+ {
+ return new TlsBlockCipher(context, createDESedeBlockCipher(), createDESedeBlockCipher(),
+ createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), 24);
+ }
+
+ protected TlsBlockCipher createSEEDCipher(TlsContext context, int macAlgorithm)
+ throws IOException
+ {
+ return new TlsBlockCipher(context, createSEEDBlockCipher(), createSEEDBlockCipher(),
+ createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), 16);
+ }
+
+ protected StreamCipher createRC4StreamCipher()
+ {
+ return new RC4Engine();
+ }
+
+ protected BlockCipher createAESBlockCipher()
+ {
+ return new CBCBlockCipher(new AESFastEngine());
+ }
+
+ protected AEADBlockCipher createAEADBlockCipher_AES_GCM()
+ {
+ // TODO Consider allowing custom configuration of multiplier
+ return new GCMBlockCipher(new AESFastEngine());
+ }
+
+ protected BlockCipher createCamelliaBlockCipher()
+ {
+ return new CBCBlockCipher(new CamelliaEngine());
+ }
+
+ protected BlockCipher createDESedeBlockCipher()
+ {
+ return new CBCBlockCipher(new DESedeEngine());
+ }
+
+ protected BlockCipher createSEEDBlockCipher()
+ {
+ return new CBCBlockCipher(new SEEDEngine());
+ }
+
+ protected Digest createHMACDigest(int macAlgorithm)
+ throws IOException
+ {
+ switch (macAlgorithm)
+ {
+ case MACAlgorithm._null:
+ return null;
+ case MACAlgorithm.hmac_md5:
+ return new MD5Digest();
+ case MACAlgorithm.hmac_sha1:
+ return new SHA1Digest();
+ case MACAlgorithm.hmac_sha256:
+ return new SHA256Digest();
+ case MACAlgorithm.hmac_sha384:
+ return new SHA384Digest();
+ case MACAlgorithm.hmac_sha512:
+ return new SHA512Digest();
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java b/core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java
new file mode 100644
index 00000000..4f9fe27f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java
@@ -0,0 +1,380 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.util.Hashtable;
+
+public abstract class DefaultTlsClient
+ extends AbstractTlsClient
+{
+
+ protected int[] namedCurves;
+ protected short[] clientECPointFormats, serverECPointFormats;
+
+ public DefaultTlsClient()
+ {
+ super();
+ }
+
+ public DefaultTlsClient(TlsCipherFactory cipherFactory)
+ {
+ super(cipherFactory);
+ }
+
+ public int[] getCipherSuites()
+ {
+ return new int[]{CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,};
+ }
+
+ public Hashtable getClientExtensions()
+ throws IOException
+ {
+
+ Hashtable clientExtensions = super.getClientExtensions();
+
+ if (TlsECCUtils.containsECCCipherSuites(getCipherSuites()))
+ {
+ /*
+ * RFC 4492 5.1. A client that proposes ECC cipher suites in its ClientHello message
+ * appends these extensions (along with any others), enumerating the curves it supports
+ * and the point formats it can parse. Clients SHOULD send both the Supported Elliptic
+ * Curves Extension and the Supported Point Formats Extension.
+ */
+ /*
+ * TODO Could just add all the curves since we support them all, but users may not want
+ * to use unnecessarily large fields. Need configuration options.
+ */
+ this.namedCurves = new int[]{NamedCurve.secp256r1, NamedCurve.sect233r1, NamedCurve.secp224r1,
+ NamedCurve.sect193r1, NamedCurve.secp192r1, NamedCurve.arbitrary_explicit_char2_curves,
+ NamedCurve.arbitrary_explicit_prime_curves};
+ this.clientECPointFormats = new short[]{ECPointFormat.ansiX962_compressed_char2,
+ ECPointFormat.ansiX962_compressed_prime, ECPointFormat.uncompressed};
+
+ if (clientExtensions == null)
+ {
+ clientExtensions = new Hashtable();
+ }
+
+ TlsECCUtils.addSupportedEllipticCurvesExtension(clientExtensions, namedCurves);
+ TlsECCUtils.addSupportedPointFormatsExtension(clientExtensions, clientECPointFormats);
+ }
+
+ return clientExtensions;
+ }
+
+ public void processServerExtensions(Hashtable serverExtensions)
+ throws IOException
+ {
+
+ super.processServerExtensions(serverExtensions);
+
+ if (serverExtensions != null)
+ {
+ int[] namedCurves = TlsECCUtils.getSupportedEllipticCurvesExtension(serverExtensions);
+ if (namedCurves != null)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ this.serverECPointFormats = TlsECCUtils.getSupportedPointFormatsExtension(serverExtensions);
+ if (this.serverECPointFormats != null && !TlsECCUtils.isECCCipherSuite(this.selectedCipherSuite))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+ }
+
+ public TlsKeyExchange getKeyExchange()
+ throws IOException
+ {
+
+ switch (selectedCipherSuite)
+ {
+ case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
+ return createDHKeyExchange(KeyExchangeAlgorithm.DH_DSS);
+
+ case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
+ return createDHKeyExchange(KeyExchangeAlgorithm.DH_RSA);
+
+ case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
+ return createDHEKeyExchange(KeyExchangeAlgorithm.DHE_DSS);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
+ return createDHEKeyExchange(KeyExchangeAlgorithm.DHE_RSA);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+ return createECDHKeyExchange(KeyExchangeAlgorithm.ECDH_ECDSA);
+
+ case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
+ return createECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA);
+
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+ return createECDHEKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA);
+
+ case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+ return createECDHEKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA);
+
+ case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_NULL_MD5:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
+ return createRSAKeyExchange();
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher suites, so if
+ * we now can't produce an implementation, we shouldn't have offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public TlsCipher getCipher()
+ throws IOException
+ {
+
+ switch (selectedCipherSuite)
+ {
+ case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm._3DES_EDE_CBC, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_GCM, MACAlgorithm._null);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha384);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_GCM, MACAlgorithm._null);
+
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_128_CBC, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_256_CBC, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_RSA_WITH_NULL_MD5:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_md5);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.RC4_128, MACAlgorithm.hmac_md5);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.RC4_128, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.SEED_CBC, MACAlgorithm.hmac_sha1);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher suites, so if
+ * we now can't produce an implementation, we shouldn't have offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ protected TlsKeyExchange createDHKeyExchange(int keyExchange)
+ {
+ return new TlsDHKeyExchange(keyExchange, supportedSignatureAlgorithms, null);
+ }
+
+ protected TlsKeyExchange createDHEKeyExchange(int keyExchange)
+ {
+ return new TlsDHEKeyExchange(keyExchange, supportedSignatureAlgorithms, null);
+ }
+
+ protected TlsKeyExchange createECDHKeyExchange(int keyExchange)
+ {
+ return new TlsECDHKeyExchange(keyExchange, supportedSignatureAlgorithms, namedCurves, clientECPointFormats,
+ serverECPointFormats);
+ }
+
+ protected TlsKeyExchange createECDHEKeyExchange(int keyExchange)
+ {
+ return new TlsECDHEKeyExchange(keyExchange, supportedSignatureAlgorithms, namedCurves, clientECPointFormats,
+ serverECPointFormats);
+ }
+
+ protected TlsKeyExchange createRSAKeyExchange()
+ {
+ return new TlsRSAKeyExchange(supportedSignatureAlgorithms);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsEncryptionCredentials.java b/core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsEncryptionCredentials.java
new file mode 100644
index 00000000..a338c388
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsEncryptionCredentials.java
@@ -0,0 +1,75 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.encodings.PKCS1Encoding;
+import org.bouncycastle.crypto.engines.RSABlindedEngine;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+
+public class DefaultTlsEncryptionCredentials
+ implements TlsEncryptionCredentials
+{
+ protected TlsContext context;
+ protected Certificate certificate;
+ protected AsymmetricKeyParameter privateKey;
+
+ public DefaultTlsEncryptionCredentials(TlsContext context, Certificate certificate,
+ AsymmetricKeyParameter privateKey)
+ {
+ if (certificate == null)
+ {
+ throw new IllegalArgumentException("'certificate' cannot be null");
+ }
+ if (certificate.isEmpty())
+ {
+ throw new IllegalArgumentException("'certificate' cannot be empty");
+ }
+ if (privateKey == null)
+ {
+ throw new IllegalArgumentException("'privateKey' cannot be null");
+ }
+ if (!privateKey.isPrivate())
+ {
+ throw new IllegalArgumentException("'privateKey' must be private");
+ }
+
+ if (privateKey instanceof RSAKeyParameters)
+ {
+ }
+ else
+ {
+ throw new IllegalArgumentException("'privateKey' type not supported: "
+ + privateKey.getClass().getName());
+ }
+
+ this.context = context;
+ this.certificate = certificate;
+ this.privateKey = privateKey;
+ }
+
+ public Certificate getCertificate()
+ {
+ return certificate;
+ }
+
+ public byte[] decryptPreMasterSecret(byte[] encryptedPreMasterSecret)
+ throws IOException
+ {
+
+ PKCS1Encoding encoding = new PKCS1Encoding(new RSABlindedEngine());
+ encoding.init(false, new ParametersWithRandom(this.privateKey, context.getSecureRandom()));
+
+ try
+ {
+ return encoding.processBlock(encryptedPreMasterSecret, 0,
+ encryptedPreMasterSecret.length);
+ }
+ catch (InvalidCipherTextException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java b/core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java
new file mode 100644
index 00000000..246b87ec
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java
@@ -0,0 +1,384 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+import org.bouncycastle.crypto.agreement.DHStandardGroups;
+import org.bouncycastle.crypto.params.DHParameters;
+
+public abstract class DefaultTlsServer
+ extends AbstractTlsServer
+{
+
+ public DefaultTlsServer()
+ {
+ super();
+ }
+
+ public DefaultTlsServer(TlsCipherFactory cipherFactory)
+ {
+ super(cipherFactory);
+ }
+
+ protected TlsEncryptionCredentials getRSAEncryptionCredentials()
+ throws IOException
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ protected TlsSignerCredentials getRSASignerCredentials()
+ throws IOException
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ protected DHParameters getDHParameters()
+ {
+ return DHStandardGroups.rfc5114_1024_160;
+ }
+
+ protected int[] getCipherSuites()
+ {
+ return new int[]{CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,};
+ }
+
+ public TlsCredentials getCredentials()
+ throws IOException
+ {
+
+ switch (selectedCipherSuite)
+ {
+ case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_NULL_MD5:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
+ return getRSAEncryptionCredentials();
+
+ case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ return getRSASignerCredentials();
+
+ default:
+ /*
+ * Note: internal error here; selected a key exchange we don't implement!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public TlsKeyExchange getKeyExchange()
+ throws IOException
+ {
+
+ switch (selectedCipherSuite)
+ {
+ case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
+ return createDHKeyExchange(KeyExchangeAlgorithm.DH_DSS);
+
+ case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
+ return createDHKeyExchange(KeyExchangeAlgorithm.DH_RSA);
+
+ case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
+ return createDHEKeyExchange(KeyExchangeAlgorithm.DHE_DSS);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
+ return createDHEKeyExchange(KeyExchangeAlgorithm.DHE_RSA);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+ return createECDHKeyExchange(KeyExchangeAlgorithm.ECDH_ECDSA);
+
+ case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
+ return createECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA);
+
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+ return createECDHEKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA);
+
+ case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+ return createECDHEKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA);
+
+ case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_NULL_MD5:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
+ return createRSAKeyExchange();
+
+ default:
+ /*
+ * Note: internal error here; selected a key exchange we don't implement!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public TlsCipher getCipher()
+ throws IOException
+ {
+
+ switch (selectedCipherSuite)
+ {
+ case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm._3DES_EDE_CBC, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_GCM, MACAlgorithm._null);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha384);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_GCM, MACAlgorithm._null);
+
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_128_CBC, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_256_CBC, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_RSA_WITH_NULL_MD5:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_md5);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.RC4_128, MACAlgorithm.hmac_md5);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.RC4_128, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.SEED_CBC, MACAlgorithm.hmac_sha1);
+
+ default:
+ /*
+ * Note: internal error here; selected a cipher suite we don't implement!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ protected TlsKeyExchange createDHKeyExchange(int keyExchange)
+ {
+ return new TlsDHKeyExchange(keyExchange, supportedSignatureAlgorithms, getDHParameters());
+ }
+
+ protected TlsKeyExchange createDHEKeyExchange(int keyExchange)
+ {
+ return new TlsDHEKeyExchange(keyExchange, supportedSignatureAlgorithms, getDHParameters());
+ }
+
+ protected TlsKeyExchange createECDHKeyExchange(int keyExchange)
+ {
+ return new TlsECDHKeyExchange(keyExchange, supportedSignatureAlgorithms, namedCurves, clientECPointFormats,
+ serverECPointFormats);
+ }
+
+ protected TlsKeyExchange createECDHEKeyExchange(int keyExchange)
+ {
+ return new TlsECDHEKeyExchange(keyExchange, supportedSignatureAlgorithms, namedCurves, clientECPointFormats,
+ serverECPointFormats);
+ }
+
+ protected TlsKeyExchange createRSAKeyExchange()
+ {
+ return new TlsRSAKeyExchange(supportedSignatureAlgorithms);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSignerCredentials.java b/core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSignerCredentials.java
new file mode 100755
index 00000000..b7752501
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSignerCredentials.java
@@ -0,0 +1,81 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+
+public class DefaultTlsSignerCredentials
+ implements TlsSignerCredentials
+{
+ protected TlsContext context;
+ protected Certificate certificate;
+ protected AsymmetricKeyParameter privateKey;
+
+ protected TlsSigner signer;
+
+ public DefaultTlsSignerCredentials(TlsContext context, Certificate certificate, AsymmetricKeyParameter privateKey)
+ {
+
+ if (certificate == null)
+ {
+ throw new IllegalArgumentException("'certificate' cannot be null");
+ }
+ if (certificate.isEmpty())
+ {
+ throw new IllegalArgumentException("'certificate' cannot be empty");
+ }
+ if (privateKey == null)
+ {
+ throw new IllegalArgumentException("'privateKey' cannot be null");
+ }
+ if (!privateKey.isPrivate())
+ {
+ throw new IllegalArgumentException("'privateKey' must be private");
+ }
+
+ if (privateKey instanceof RSAKeyParameters)
+ {
+ this.signer = new TlsRSASigner();
+ }
+ else if (privateKey instanceof DSAPrivateKeyParameters)
+ {
+ this.signer = new TlsDSSSigner();
+ }
+ else if (privateKey instanceof ECPrivateKeyParameters)
+ {
+ this.signer = new TlsECDSASigner();
+ }
+ else
+ {
+ throw new IllegalArgumentException("'privateKey' type not supported: " + privateKey.getClass().getName());
+ }
+
+ this.signer.init(context);
+
+ this.context = context;
+ this.certificate = certificate;
+ this.privateKey = privateKey;
+ }
+
+ public Certificate getCertificate()
+ {
+ return certificate;
+ }
+
+ public byte[] generateCertificateSignature(byte[] md5andsha1)
+ throws IOException
+ {
+ try
+ {
+ return signer.generateRawSignature(privateKey, md5andsha1);
+ }
+ catch (CryptoException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DeferredHash.java b/core/src/main/java/org/bouncycastle/crypto/tls/DeferredHash.java
new file mode 100644
index 00000000..e8c76e65
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/DeferredHash.java
@@ -0,0 +1,128 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayOutputStream;
+
+import org.bouncycastle.crypto.Digest;
+
+/**
+ * Buffers input until the hash algorithm is determined.
+ */
+class DeferredHash
+ implements TlsHandshakeHash
+{
+
+ protected TlsContext context;
+
+ private ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ private int prfAlgorithm = -1;
+ private Digest hash = null;
+
+ DeferredHash()
+ {
+ this.buf = new ByteArrayOutputStream();
+ this.hash = null;
+ }
+
+ private DeferredHash(Digest hash)
+ {
+ this.buf = null;
+ this.hash = hash;
+ }
+
+ public void init(TlsContext context)
+ {
+ this.context = context;
+ }
+
+ public TlsHandshakeHash commit()
+ {
+
+ int prfAlgorithm = context.getSecurityParameters().getPrfAlgorithm();
+
+ Digest prfHash = TlsUtils.createPRFHash(prfAlgorithm);
+
+ byte[] data = buf.toByteArray();
+ prfHash.update(data, 0, data.length);
+
+ if (prfHash instanceof TlsHandshakeHash)
+ {
+ TlsHandshakeHash tlsPRFHash = (TlsHandshakeHash)prfHash;
+ tlsPRFHash.init(context);
+ return tlsPRFHash.commit();
+ }
+
+ this.prfAlgorithm = prfAlgorithm;
+ this.hash = prfHash;
+ this.buf = null;
+
+ return this;
+ }
+
+ public TlsHandshakeHash fork()
+ {
+ checkHash();
+ return new DeferredHash(TlsUtils.clonePRFHash(prfAlgorithm, hash));
+ }
+
+ public String getAlgorithmName()
+ {
+ checkHash();
+ return hash.getAlgorithmName();
+ }
+
+ public int getDigestSize()
+ {
+ checkHash();
+ return hash.getDigestSize();
+ }
+
+ public void update(byte input)
+ {
+ if (hash == null)
+ {
+ buf.write(input);
+ }
+ else
+ {
+ hash.update(input);
+ }
+ }
+
+ public void update(byte[] input, int inOff, int len)
+ {
+ if (hash == null)
+ {
+ buf.write(input, inOff, len);
+ }
+ else
+ {
+ hash.update(input, inOff, len);
+ }
+ }
+
+ public int doFinal(byte[] output, int outOff)
+ {
+ checkHash();
+ return hash.doFinal(output, outOff);
+ }
+
+ public void reset()
+ {
+ if (hash == null)
+ {
+ buf.reset();
+ }
+ else
+ {
+ hash.reset();
+ }
+ }
+
+ protected void checkHash()
+ {
+ if (hash == null)
+ {
+ throw new IllegalStateException("No hash algorithm has been set");
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DigestAlgorithm.java b/core/src/main/java/org/bouncycastle/crypto/tls/DigestAlgorithm.java
new file mode 100644
index 00000000..41d54009
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/DigestAlgorithm.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 2246
+ * <p/>
+ * Note that the values here are implementation-specific and arbitrary. It is recommended not to
+ * depend on the particular values (e.g. serialization).
+ *
+ * @deprecated use MACAlgorithm constants instead
+ */
+public class DigestAlgorithm
+{
+ public static final int NULL = 0;
+ public static final int MD5 = 1;
+ public static final int SHA = 2;
+
+ /*
+ * RFC 5246
+ */
+ public static final int SHA256 = 3;
+ public static final int SHA384 = 4;
+ public static final int SHA512 = 5;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/ECBasisType.java b/core/src/main/java/org/bouncycastle/crypto/tls/ECBasisType.java
new file mode 100644
index 00000000..57f0ad01
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/ECBasisType.java
@@ -0,0 +1,11 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 4492 5.4. (Errata ID: 2389)
+ */
+public class ECBasisType
+{
+
+ public static final short ec_basis_trinomial = 1;
+ public static final short ec_basis_pentanomial = 2;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/ECCurveType.java b/core/src/main/java/org/bouncycastle/crypto/tls/ECCurveType.java
new file mode 100644
index 00000000..0b6542fc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/ECCurveType.java
@@ -0,0 +1,28 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 4492 5.4
+ */
+public class ECCurveType
+{
+ /**
+ * Indicates the elliptic curve domain parameters are conveyed verbosely, and the
+ * underlying finite field is a prime field.
+ */
+ public static final short explicit_prime = 1;
+
+ /**
+ * Indicates the elliptic curve domain parameters are conveyed verbosely, and the
+ * underlying finite field is a characteristic-2 field.
+ */
+ public static final short explicit_char2 = 2;
+
+ /**
+ * Indicates that a named curve is used. This option SHOULD be used when applicable.
+ */
+ public static final short named_curve = 3;
+
+ /*
+ * Values 248 through 255 are reserved for private use.
+ */
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/ECPointFormat.java b/core/src/main/java/org/bouncycastle/crypto/tls/ECPointFormat.java
new file mode 100644
index 00000000..969d42ee
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/ECPointFormat.java
@@ -0,0 +1,15 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 4492 5.1.2
+ */
+public class ECPointFormat
+{
+ public static final short uncompressed = 0;
+ public static final short ansiX962_compressed_prime = 1;
+ public static final short ansiX962_compressed_char2 = 2;
+
+ /*
+ * reserved (248..255)
+ */
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java b/core/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java
new file mode 100644
index 00000000..f991e4ab
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java
@@ -0,0 +1,43 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 2246
+ * <p/>
+ * Note that the values here are implementation-specific and arbitrary. It is recommended not to
+ * depend on the particular values (e.g. serialization).
+ */
+public class EncryptionAlgorithm
+{
+
+ public static final int NULL = 0;
+ public static final int RC4_40 = 1;
+ public static final int RC4_128 = 2;
+ public static final int RC2_CBC_40 = 3;
+ public static final int IDEA_CBC = 4;
+ public static final int DES40_CBC = 5;
+ public static final int DES_CBC = 6;
+ public static final int _3DES_EDE_CBC = 7;
+
+ /*
+ * RFC 3268
+ */
+ public static final int AES_128_CBC = 8;
+ public static final int AES_256_CBC = 9;
+
+ /*
+ * RFC 4132
+ */
+ public static final int CAMELLIA_128_CBC = 12;
+ public static final int CAMELLIA_256_CBC = 13;
+
+ /*
+ * RFC 4162
+ */
+ public static final int SEED_CBC = 14;
+
+ /*
+ * RFC 5289
+ */
+ public static final int AES_128_GCM = 10;
+ public static final int AES_256_GCM = 11;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/ExporterLabel.java b/core/src/main/java/org/bouncycastle/crypto/tls/ExporterLabel.java
new file mode 100644
index 00000000..902720ac
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/ExporterLabel.java
@@ -0,0 +1,31 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 5705
+ */
+public class ExporterLabel
+{
+ /*
+ * RFC 5246
+ */
+ public static final String client_finished = "client finished";
+ public static final String server_finished = "server finished";
+ public static final String master_secret = "master secret";
+ public static final String key_expansion = "key expansion";
+
+ /*
+ * RFC 5216
+ */
+ public static final String client_EAP_encryption = "client EAP encryption";
+
+ /*
+ * RFC 5281
+ */
+ public static final String ttls_keying_material = "ttls keying material";
+ public static final String ttls_challenge = "ttls challenge";
+
+ /*
+ * RFC 5764
+ */
+ public static final String dtls_srtp = "EXTRACTOR-dtls_srtp";
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java b/core/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java
new file mode 100644
index 00000000..0be64654
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java
@@ -0,0 +1,50 @@
+package org.bouncycastle.crypto.tls;
+
+public class ExtensionType
+{
+ /*
+ * RFC 6066 1.1.
+ */
+ public static final int server_name = 0;
+ public static final int max_fragment_length = 1;
+ public static final int client_certificate_url = 2;
+ public static final int trusted_ca_keys = 3;
+ public static final int truncated_hmac = 4;
+ public static final int status_request = 5;
+
+ /*
+ * RFC 4681
+ */
+ public static final int user_mapping = 6;
+
+ /*
+ * RFC 4492 5.1.
+ */
+ public static final int elliptic_curves = 10;
+ public static final int ec_point_formats = 11;
+
+ /*
+ * RFC 5054 2.8.1.
+ */
+ public static final int srp = 12;
+
+ /*
+ * RFC 5077 7.
+ */
+ public static final int session_ticket = 35;
+
+ /*
+ * RFC 5246 7.4.1.4.
+ */
+ public static final int signature_algorithms = 13;
+
+ /*
+ * RFC 5764 9.
+ */
+ public static final int use_srtp = 14;
+
+ /*
+ * RFC 5746 3.2.
+ */
+ public static final int renegotiation_info = 0xff01;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/HandshakeType.java b/core/src/main/java/org/bouncycastle/crypto/tls/HandshakeType.java
new file mode 100644
index 00000000..53b4520c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/HandshakeType.java
@@ -0,0 +1,33 @@
+package org.bouncycastle.crypto.tls;
+
+public class HandshakeType
+{
+ /*
+ * RFC 2246 7.4
+ */
+ public static final short hello_request = 0;
+ public static final short client_hello = 1;
+ public static final short server_hello = 2;
+ public static final short certificate = 11;
+ public static final short server_key_exchange = 12;
+ public static final short certificate_request = 13;
+ public static final short server_hello_done = 14;
+ public static final short certificate_verify = 15;
+ public static final short client_key_exchange = 16;
+ public static final short finished = 20;
+
+ /*
+ * (DTLS) RFC 4347 4.3.2
+ */
+ public static final short hello_verify_request = 3;
+
+ /*
+ * RFC 4680
+ */
+ public static final short supplemental_data = 23;
+
+ /*
+ * RFC 5077
+ */
+ public static final short session_ticket = 4;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/HashAlgorithm.java b/core/src/main/java/org/bouncycastle/crypto/tls/HashAlgorithm.java
new file mode 100644
index 00000000..ac0a4c61
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/HashAlgorithm.java
@@ -0,0 +1,16 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 5246 7.4.1.4.1
+ */
+public class HashAlgorithm
+{
+
+ public static final short none = 0;
+ public static final short md5 = 1;
+ public static final short sha1 = 2;
+ public static final short sha224 = 3;
+ public static final short sha256 = 4;
+ public static final short sha384 = 5;
+ public static final short sha512 = 6;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/KeyExchangeAlgorithm.java b/core/src/main/java/org/bouncycastle/crypto/tls/KeyExchangeAlgorithm.java
new file mode 100644
index 00000000..c049bb70
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/KeyExchangeAlgorithm.java
@@ -0,0 +1,47 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 2246
+ * <p/>
+ * Note that the values here are implementation-specific and arbitrary. It is recommended not to
+ * depend on the particular values (e.g. serialization).
+ */
+public class KeyExchangeAlgorithm
+{
+ public static final int NULL = 0;
+ public static final int RSA = 1;
+ public static final int RSA_EXPORT = 2;
+ public static final int DHE_DSS = 3;
+ public static final int DHE_DSS_EXPORT = 4;
+ public static final int DHE_RSA = 5;
+ public static final int DHE_RSA_EXPORT = 6;
+ public static final int DH_DSS = 7;
+ public static final int DH_DSS_EXPORT = 8;
+ public static final int DH_RSA = 9;
+ public static final int DH_RSA_EXPORT = 10;
+ public static final int DH_anon = 11;
+ public static final int DH_anon_EXPORT = 12;
+
+ /*
+ * RFC 4279
+ */
+ public static final int PSK = 13;
+ public static final int DHE_PSK = 14;
+ public static final int RSA_PSK = 15;
+
+ /*
+ * RFC 4429
+ */
+ public static final int ECDH_ECDSA = 16;
+ public static final int ECDHE_ECDSA = 17;
+ public static final int ECDH_RSA = 18;
+ public static final int ECDHE_RSA = 19;
+ public static final int ECDH_anon = 20;
+
+ /*
+ * RFC 5054
+ */
+ public static final int SRP = 21;
+ public static final int SRP_DSS = 22;
+ public static final int SRP_RSA = 23;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/LegacyTlsAuthentication.java b/core/src/main/java/org/bouncycastle/crypto/tls/LegacyTlsAuthentication.java
new file mode 100644
index 00000000..f638714e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/LegacyTlsAuthentication.java
@@ -0,0 +1,28 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+/**
+ * A temporary class to wrap old CertificateVerifyer stuff for new TlsAuthentication
+ *
+ * @deprecated
+ */
+public class LegacyTlsAuthentication
+ extends ServerOnlyTlsAuthentication
+{
+ protected CertificateVerifyer verifyer;
+
+ public LegacyTlsAuthentication(CertificateVerifyer verifyer)
+ {
+ this.verifyer = verifyer;
+ }
+
+ public void notifyServerCertificate(Certificate serverCertificate)
+ throws IOException
+ {
+ if (!this.verifyer.isValid(serverCertificate.getCertificateList()))
+ {
+ throw new TlsFatalAlert(AlertDescription.user_canceled);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/LegacyTlsClient.java b/core/src/main/java/org/bouncycastle/crypto/tls/LegacyTlsClient.java
new file mode 100644
index 00000000..33217ac4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/LegacyTlsClient.java
@@ -0,0 +1,33 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+/**
+ * A temporary class to use LegacyTlsAuthentication
+ *
+ * @deprecated
+ */
+public class LegacyTlsClient
+ extends DefaultTlsClient
+{
+ /**
+ * @deprecated
+ */
+ protected CertificateVerifyer verifyer;
+
+ /**
+ * @deprecated
+ */
+ public LegacyTlsClient(CertificateVerifyer verifyer)
+ {
+ super();
+
+ this.verifyer = verifyer;
+ }
+
+ public TlsAuthentication getAuthentication()
+ throws IOException
+ {
+ return new LegacyTlsAuthentication(verifyer);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/MACAlgorithm.java b/core/src/main/java/org/bouncycastle/crypto/tls/MACAlgorithm.java
new file mode 100644
index 00000000..40ef15c4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/MACAlgorithm.java
@@ -0,0 +1,24 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 2246
+ * <p/>
+ * Note that the values here are implementation-specific and arbitrary. It is recommended not to
+ * depend on the particular values (e.g. serialization).
+ */
+public class MACAlgorithm
+{
+
+ public static final int _null = 0;
+ public static final int md5 = 1;
+ public static final int sha = 2;
+
+ /*
+ * RFC 5246
+ */
+ public static final int hmac_md5 = md5;
+ public static final int hmac_sha1 = sha;
+ public static final int hmac_sha256 = 3;
+ public static final int hmac_sha384 = 4;
+ public static final int hmac_sha512 = 5;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/NamedCurve.java b/core/src/main/java/org/bouncycastle/crypto/tls/NamedCurve.java
new file mode 100644
index 00000000..690115cc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/NamedCurve.java
@@ -0,0 +1,59 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 4492 5.1.1
+ * <p/>
+ * The named curves defined here are those specified in SEC 2 [13]. Note that many of these curves
+ * are also recommended in ANSI X9.62 [7] and FIPS 186-2 [11]. Values 0xFE00 through 0xFEFF are
+ * reserved for private use. Values 0xFF01 and 0xFF02 indicate that the client supports arbitrary
+ * prime and characteristic-2 curves, respectively (the curve parameters must be encoded explicitly
+ * in ECParameters).
+ */
+public class NamedCurve
+{
+ public static final int sect163k1 = 1;
+ public static final int sect163r1 = 2;
+ public static final int sect163r2 = 3;
+ public static final int sect193r1 = 4;
+ public static final int sect193r2 = 5;
+ public static final int sect233k1 = 6;
+ public static final int sect233r1 = 7;
+ public static final int sect239k1 = 8;
+ public static final int sect283k1 = 9;
+ public static final int sect283r1 = 10;
+ public static final int sect409k1 = 11;
+ public static final int sect409r1 = 12;
+ public static final int sect571k1 = 13;
+ public static final int sect571r1 = 14;
+ public static final int secp160k1 = 15;
+ public static final int secp160r1 = 16;
+ public static final int secp160r2 = 17;
+ public static final int secp192k1 = 18;
+ public static final int secp192r1 = 19;
+ public static final int secp224k1 = 20;
+ public static final int secp224r1 = 21;
+ public static final int secp256k1 = 22;
+ public static final int secp256r1 = 23;
+ public static final int secp384r1 = 24;
+ public static final int secp521r1 = 25;
+
+ /*
+ * reserved (0xFE00..0xFEFF)
+ */
+
+ public static final int arbitrary_explicit_prime_curves = 0xFF01;
+ public static final int arbitrary_explicit_char2_curves = 0xFF02;
+
+ public static boolean refersToASpecificNamedCurve(int namedCurve)
+ {
+ switch (namedCurve)
+ {
+ case arbitrary_explicit_prime_curves:
+ case arbitrary_explicit_char2_curves:
+ return false;
+ default:
+ return true;
+ }
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/NewSessionTicket.java b/core/src/main/java/org/bouncycastle/crypto/tls/NewSessionTicket.java
new file mode 100644
index 00000000..f3d10222
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/NewSessionTicket.java
@@ -0,0 +1,43 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class NewSessionTicket
+{
+
+ protected long ticketLifetimeHint;
+ protected byte[] ticket;
+
+ public NewSessionTicket(long ticketLifetimeHint, byte[] ticket)
+ {
+ this.ticketLifetimeHint = ticketLifetimeHint;
+ this.ticket = ticket;
+ }
+
+ public long getTicketLifetimeHint()
+ {
+ return ticketLifetimeHint;
+ }
+
+ public byte[] getTicket()
+ {
+ return ticket;
+ }
+
+ public void encode(OutputStream output)
+ throws IOException
+ {
+ TlsUtils.writeUint32(ticketLifetimeHint, output);
+ TlsUtils.writeOpaque16(ticket, output);
+ }
+
+ public static NewSessionTicket parse(InputStream input)
+ throws IOException
+ {
+ long ticketLifetimeHint = TlsUtils.readUint32(input);
+ byte[] ticket = TlsUtils.readOpaque16(input);
+ return new NewSessionTicket(ticketLifetimeHint, ticket);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/PRFAlgorithm.java b/core/src/main/java/org/bouncycastle/crypto/tls/PRFAlgorithm.java
new file mode 100644
index 00000000..81a3bffb
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/PRFAlgorithm.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 5246
+ * <p/>
+ * Note that the values here are implementation-specific and arbitrary. It is recommended not to
+ * depend on the particular values (e.g. serialization).
+ */
+public class PRFAlgorithm
+{
+
+ /*
+ * Placeholder to refer to the legacy TLS algorithm
+ */
+ public static final int tls_prf_legacy = 0;
+
+ public static final int tls_prf_sha256 = 1;
+
+ /*
+ * Implied by RFC 5288
+ */
+ public static final int tls_prf_sha384 = 2;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java b/core/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java
new file mode 100644
index 00000000..29750cb7
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java
@@ -0,0 +1,114 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+public abstract class PSKTlsClient
+ extends AbstractTlsClient
+{
+ protected TlsPSKIdentity pskIdentity;
+
+ public PSKTlsClient(TlsPSKIdentity pskIdentity)
+ {
+ super();
+ this.pskIdentity = pskIdentity;
+ }
+
+ public PSKTlsClient(TlsCipherFactory cipherFactory, TlsPSKIdentity pskIdentity)
+ {
+ super(cipherFactory);
+ this.pskIdentity = pskIdentity;
+ }
+
+ public int[] getCipherSuites()
+ {
+ return new int[]{CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA, CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA,
+ CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA, CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA,
+ CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA, CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA, CipherSuite.TLS_PSK_WITH_RC4_128_SHA,};
+ }
+
+ public TlsKeyExchange getKeyExchange()
+ throws IOException
+ {
+
+ switch (selectedCipherSuite)
+ {
+ case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
+ return createPSKKeyExchange(KeyExchangeAlgorithm.PSK);
+
+ case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
+ return createPSKKeyExchange(KeyExchangeAlgorithm.RSA_PSK);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+ return createPSKKeyExchange(KeyExchangeAlgorithm.DHE_PSK);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher suites, so if
+ * we now can't produce an implementation, we shouldn't have offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public TlsCipher getCipher()
+ throws IOException
+ {
+
+ switch (selectedCipherSuite)
+ {
+ case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm._3DES_EDE_CBC, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.RC4_128, MACAlgorithm.hmac_sha1);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher suites, so if
+ * we now can't produce an implementation, we shouldn't have offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ protected TlsKeyExchange createPSKKeyExchange(int keyExchange)
+ {
+ return new TlsPSKKeyExchange(keyExchange, supportedSignatureAlgorithms, pskIdentity);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/ProtocolVersion.java b/core/src/main/java/org/bouncycastle/crypto/tls/ProtocolVersion.java
new file mode 100644
index 00000000..c001e584
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/ProtocolVersion.java
@@ -0,0 +1,126 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+public final class ProtocolVersion
+{
+
+ public static final ProtocolVersion SSLv3 = new ProtocolVersion(0x0300, "SSL 3.0");
+ public static final ProtocolVersion TLSv10 = new ProtocolVersion(0x0301, "TLS 1.0");
+ public static final ProtocolVersion TLSv11 = new ProtocolVersion(0x0302, "TLS 1.1");
+ public static final ProtocolVersion TLSv12 = new ProtocolVersion(0x0303, "TLS 1.2");
+ public static final ProtocolVersion DTLSv10 = new ProtocolVersion(0xFEFF, "DTLS 1.0");
+ public static final ProtocolVersion DTLSv12 = new ProtocolVersion(0xFEFD, "DTLS 1.2");
+
+ private int version;
+ private String name;
+
+ private ProtocolVersion(int v, String name)
+ {
+ this.version = v & 0xffff;
+ this.name = name;
+ }
+
+ public int getFullVersion()
+ {
+ return version;
+ }
+
+ public int getMajorVersion()
+ {
+ return version >> 8;
+ }
+
+ public int getMinorVersion()
+ {
+ return version & 0xff;
+ }
+
+ public boolean isDTLS()
+ {
+ return getMajorVersion() == 0xFE;
+ }
+
+ public boolean isSSL()
+ {
+ return this == SSLv3;
+ }
+
+ public ProtocolVersion getEquivalentTLSVersion()
+ {
+ if (!isDTLS())
+ {
+ return this;
+ }
+ if (this == DTLSv10)
+ {
+ return TLSv11;
+ }
+ return TLSv12;
+ }
+
+ public boolean isEqualOrEarlierVersionOf(ProtocolVersion version)
+ {
+ if (getMajorVersion() != version.getMajorVersion())
+ {
+ return false;
+ }
+ int diffMinorVersion = version.getMinorVersion() - getMinorVersion();
+ return isDTLS() ? diffMinorVersion <= 0 : diffMinorVersion >= 0;
+ }
+
+ public boolean isLaterVersionOf(ProtocolVersion version)
+ {
+ if (getMajorVersion() != version.getMajorVersion())
+ {
+ return false;
+ }
+ int diffMinorVersion = version.getMinorVersion() - getMinorVersion();
+ return isDTLS() ? diffMinorVersion > 0 : diffMinorVersion < 0;
+ }
+
+ public boolean equals(Object obj)
+ {
+ return this == obj;
+ }
+
+ public int hashCode()
+ {
+ return version;
+ }
+
+ public static ProtocolVersion get(int major, int minor)
+ throws IOException
+ {
+ switch (major)
+ {
+ case 0x03:
+ switch (minor)
+ {
+ case 0x00:
+ return SSLv3;
+ case 0x01:
+ return TLSv10;
+ case 0x02:
+ return TLSv11;
+ case 0x03:
+ return TLSv12;
+ }
+ case 0xFE:
+ switch (minor)
+ {
+ case 0xFF:
+ return DTLSv10;
+ case 0xFD:
+ return DTLSv12;
+ }
+ }
+
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ public String toString()
+ {
+ return name;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java b/core/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java
new file mode 100644
index 00000000..3a31c201
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java
@@ -0,0 +1,356 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.bouncycastle.crypto.Digest;
+
+/**
+ * An implementation of the TLS 1.0/1.1/1.2 record layer, allowing downgrade to SSLv3.
+ */
+class RecordStream
+{
+
+ private static int PLAINTEXT_LIMIT = (1 << 14);
+ private static int COMPRESSED_LIMIT = PLAINTEXT_LIMIT + 1024;
+ private static int CIPHERTEXT_LIMIT = COMPRESSED_LIMIT + 1024;
+
+ private TlsProtocol handler;
+ private InputStream input;
+ private OutputStream output;
+ private TlsCompression pendingCompression = null, readCompression = null, writeCompression = null;
+ private TlsCipher pendingCipher = null, readCipher = null, writeCipher = null;
+ private long readSeqNo = 0, writeSeqNo = 0;
+ private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+ private TlsContext context = null;
+ private TlsHandshakeHash hash = null;
+
+ private ProtocolVersion readVersion = null, writeVersion = null;
+ private boolean restrictReadVersion = true;
+
+ RecordStream(TlsProtocol handler, InputStream input, OutputStream output)
+ {
+ this.handler = handler;
+ this.input = input;
+ this.output = output;
+ this.readCompression = new TlsNullCompression();
+ this.writeCompression = this.readCompression;
+ this.readCipher = new TlsNullCipher(context);
+ this.writeCipher = this.readCipher;
+ }
+
+ void init(TlsContext context)
+ {
+ this.context = context;
+ this.hash = new DeferredHash();
+ this.hash.init(context);
+ }
+
+ ProtocolVersion getReadVersion()
+ {
+ return readVersion;
+ }
+
+ void setReadVersion(ProtocolVersion readVersion)
+ {
+ this.readVersion = readVersion;
+ }
+
+ void setWriteVersion(ProtocolVersion writeVersion)
+ {
+ this.writeVersion = writeVersion;
+ }
+
+ /**
+ * RFC 5246 E.1. "Earlier versions of the TLS specification were not fully clear on what the
+ * record layer version number (TLSPlaintext.version) should contain when sending ClientHello
+ * (i.e., before it is known which version of the protocol will be employed). Thus, TLS servers
+ * compliant with this specification MUST accept any value {03,XX} as the record layer version
+ * number for ClientHello."
+ */
+ void setRestrictReadVersion(boolean enabled)
+ {
+ this.restrictReadVersion = enabled;
+ }
+
+ void notifyHelloComplete()
+ {
+ this.hash = this.hash.commit();
+ }
+
+ void setPendingConnectionState(TlsCompression tlsCompression, TlsCipher tlsCipher)
+ {
+ this.pendingCompression = tlsCompression;
+ this.pendingCipher = tlsCipher;
+ }
+
+ void sentWriteCipherSpec()
+ throws IOException
+ {
+ if (pendingCompression == null || pendingCipher == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ this.writeCompression = this.pendingCompression;
+ this.writeCipher = this.pendingCipher;
+ this.writeSeqNo = 0;
+ }
+
+ void receivedReadCipherSpec()
+ throws IOException
+ {
+ if (pendingCompression == null || pendingCipher == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ this.readCompression = this.pendingCompression;
+ this.readCipher = this.pendingCipher;
+ this.readSeqNo = 0;
+ }
+
+ void finaliseHandshake()
+ throws IOException
+ {
+ if (readCompression != pendingCompression || writeCompression != pendingCompression
+ || readCipher != pendingCipher || writeCipher != pendingCipher)
+ {
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ pendingCompression = null;
+ pendingCipher = null;
+ }
+
+ public void readRecord()
+ throws IOException
+ {
+
+ short type = TlsUtils.readUint8(input);
+
+ // TODO In earlier RFCs, it was "SHOULD ignore"; should this be version-dependent?
+ /*
+ * RFC 5246 6. If a TLS implementation receives an unexpected record type, it MUST send an
+ * unexpected_message alert.
+ */
+ checkType(type, AlertDescription.unexpected_message);
+
+ if (!restrictReadVersion)
+ {
+ int version = TlsUtils.readVersionRaw(input);
+ if ((version & 0xffffff00) != 0x0300)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+ else
+ {
+ ProtocolVersion version = TlsUtils.readVersion(input);
+ if (readVersion == null)
+ {
+ readVersion = version;
+ }
+ else if (!version.equals(readVersion))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+
+ int length = TlsUtils.readUint16(input);
+ byte[] plaintext = decodeAndVerify(type, input, length);
+ handler.processRecord(type, plaintext, 0, plaintext.length);
+ }
+
+ protected byte[] decodeAndVerify(short type, InputStream input, int len)
+ throws IOException
+ {
+
+ checkLength(len, CIPHERTEXT_LIMIT, AlertDescription.record_overflow);
+
+ byte[] buf = TlsUtils.readFully(len, input);
+ byte[] decoded = readCipher.decodeCiphertext(readSeqNo++, type, buf, 0, buf.length);
+
+ checkLength(decoded.length, COMPRESSED_LIMIT, AlertDescription.record_overflow);
+
+ /*
+ * TODO RFC5264 6.2.2. Implementation note: Decompression functions are responsible for
+ * ensuring that messages cannot cause internal buffer overflows.
+ */
+ OutputStream cOut = readCompression.decompress(buffer);
+ if (cOut != buffer)
+ {
+ cOut.write(decoded, 0, decoded.length);
+ cOut.flush();
+ decoded = getBufferContents();
+ }
+
+ /*
+ * RFC 5264 6.2.2. If the decompression function encounters a TLSCompressed.fragment that
+ * would decompress to a length in excess of 2^14 bytes, it should report a fatal
+ * decompression failure error.
+ */
+ checkLength(decoded.length, PLAINTEXT_LIMIT, AlertDescription.decompression_failure);
+
+ return decoded;
+ }
+
+ protected void writeRecord(short type, byte[] plaintext, int plaintextOffset, int plaintextLength)
+ throws IOException
+ {
+
+ /*
+ * RFC 5264 6. Implementations MUST NOT send record types not defined in this document
+ * unless negotiated by some extension.
+ */
+ checkType(type, AlertDescription.internal_error);
+
+ /*
+ * RFC 5264 6.2.1 The length should not exceed 2^14.
+ */
+ checkLength(plaintextLength, PLAINTEXT_LIMIT, AlertDescription.internal_error);
+
+ /*
+ * RFC 5264 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert,
+ * or ChangeCipherSpec content types.
+ */
+ if (plaintextLength < 1 && type != ContentType.application_data)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ if (type == ContentType.handshake)
+ {
+ updateHandshakeData(plaintext, plaintextOffset, plaintextLength);
+ }
+
+ OutputStream cOut = writeCompression.compress(buffer);
+
+ byte[] ciphertext;
+ if (cOut == buffer)
+ {
+ ciphertext = writeCipher.encodePlaintext(writeSeqNo++, type, plaintext, plaintextOffset, plaintextLength);
+ }
+ else
+ {
+ cOut.write(plaintext, plaintextOffset, plaintextLength);
+ cOut.flush();
+ byte[] compressed = getBufferContents();
+
+ /*
+ * RFC5264 6.2.2. Compression must be lossless and may not increase the content length
+ * by more than 1024 bytes.
+ */
+ checkLength(compressed.length, plaintextLength + 1024, AlertDescription.internal_error);
+
+ ciphertext = writeCipher.encodePlaintext(writeSeqNo++, type, compressed, 0, compressed.length);
+ }
+
+ /*
+ * RFC 5264 6.2.3. The length may not exceed 2^14 + 2048.
+ */
+ checkLength(ciphertext.length, CIPHERTEXT_LIMIT, AlertDescription.internal_error);
+
+ byte[] record = new byte[ciphertext.length + 5];
+ TlsUtils.writeUint8(type, record, 0);
+ TlsUtils.writeVersion(writeVersion, record, 1);
+ TlsUtils.writeUint16(ciphertext.length, record, 3);
+ System.arraycopy(ciphertext, 0, record, 5, ciphertext.length);
+ output.write(record);
+ output.flush();
+ }
+
+ void updateHandshakeData(byte[] message, int offset, int len)
+ {
+ hash.update(message, offset, len);
+ }
+
+ /**
+ * 'sender' only relevant to SSLv3
+ */
+ byte[] getCurrentHash(byte[] sender)
+ {
+ TlsHandshakeHash d = hash.fork();
+
+ if (context.getServerVersion().isSSL())
+ {
+ if (sender != null)
+ {
+ d.update(sender, 0, sender.length);
+ }
+ }
+
+ return doFinal(d);
+ }
+
+ protected void close()
+ throws IOException
+ {
+ IOException e = null;
+ try
+ {
+ input.close();
+ }
+ catch (IOException ex)
+ {
+ e = ex;
+ }
+ try
+ {
+ output.close();
+ }
+ catch (IOException ex)
+ {
+ e = ex;
+ }
+ if (e != null)
+ {
+ throw e;
+ }
+ }
+
+ protected void flush()
+ throws IOException
+ {
+ output.flush();
+ }
+
+ private byte[] getBufferContents()
+ {
+ byte[] contents = buffer.toByteArray();
+ buffer.reset();
+ return contents;
+ }
+
+ private static byte[] doFinal(Digest d)
+ {
+ byte[] bs = new byte[d.getDigestSize()];
+ d.doFinal(bs, 0);
+ return bs;
+ }
+
+ private static void checkType(short type, short alertDescription)
+ throws IOException
+ {
+
+ switch (type)
+ {
+ case ContentType.change_cipher_spec:
+ case ContentType.alert:
+ case ContentType.handshake:
+ case ContentType.application_data:
+ break;
+ default:
+ throw new TlsFatalAlert(alertDescription);
+ }
+ }
+
+ private static void checkLength(int length, int limit, short alertDescription)
+ throws IOException
+ {
+ if (length > limit)
+ {
+ throw new TlsFatalAlert(alertDescription);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/SRPTlsClient.java b/core/src/main/java/org/bouncycastle/crypto/tls/SRPTlsClient.java
new file mode 100644
index 00000000..a5d48404
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/SRPTlsClient.java
@@ -0,0 +1,137 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Hashtable;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Integers;
+
+public abstract class SRPTlsClient
+ extends AbstractTlsClient
+{
+ public static final Integer EXT_SRP = Integers.valueOf(ExtensionType.srp);
+
+ protected byte[] identity;
+ protected byte[] password;
+
+ public SRPTlsClient(byte[] identity, byte[] password)
+ {
+ super();
+ this.identity = Arrays.clone(identity);
+ this.password = Arrays.clone(password);
+ }
+
+ public SRPTlsClient(TlsCipherFactory cipherFactory, byte[] identity, byte[] password)
+ {
+ super(cipherFactory);
+ this.identity = Arrays.clone(identity);
+ this.password = Arrays.clone(password);
+ }
+
+ public int[] getCipherSuites()
+ {
+ return new int[]{CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA,
+ CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA, CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA,};
+ }
+
+ public Hashtable getClientExtensions()
+ throws IOException
+ {
+
+ Hashtable clientExtensions = super.getClientExtensions();
+ if (clientExtensions == null)
+ {
+ clientExtensions = new Hashtable();
+ }
+
+ ByteArrayOutputStream srpData = new ByteArrayOutputStream();
+ TlsUtils.writeOpaque8(this.identity, srpData);
+ clientExtensions.put(EXT_SRP, srpData.toByteArray());
+
+ return clientExtensions;
+ }
+
+ public void processServerExtensions(Hashtable serverExtensions)
+ throws IOException
+ {
+ // No explicit guidance in RFC 5054 here; we allow an optional empty extension from server
+ if (serverExtensions != null)
+ {
+ byte[] extValue = (byte[])serverExtensions.get(EXT_SRP);
+ if (extValue != null && extValue.length > 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+ }
+
+ public TlsKeyExchange getKeyExchange()
+ throws IOException
+ {
+
+ switch (selectedCipherSuite)
+ {
+ case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
+ return createSRPKeyExchange(KeyExchangeAlgorithm.SRP);
+
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
+ return createSRPKeyExchange(KeyExchangeAlgorithm.SRP_RSA);
+
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+ return createSRPKeyExchange(KeyExchangeAlgorithm.SRP_DSS);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher suites, so if
+ * we now can't produce an implementation, we shouldn't have offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public TlsCipher getCipher()
+ throws IOException
+ {
+
+ switch (selectedCipherSuite)
+ {
+ case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm._3DES_EDE_CBC, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha1);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher suites, so if
+ * we now can't produce an implementation, we shouldn't have offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ protected TlsKeyExchange createSRPKeyExchange(int keyExchange)
+ {
+ return new TlsSRPKeyExchange(keyExchange, supportedSignatureAlgorithms, identity, password);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/SRTPProtectionProfile.java b/core/src/main/java/org/bouncycastle/crypto/tls/SRTPProtectionProfile.java
new file mode 100644
index 00000000..1faac960
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/SRTPProtectionProfile.java
@@ -0,0 +1,12 @@
+package org.bouncycastle.crypto.tls;
+
+public class SRTPProtectionProfile
+{
+ /*
+ * RFC 5764 4.1.2.
+ */
+ public static final int SRTP_AES128_CM_HMAC_SHA1_80 = 0x0001;
+ public static final int SRTP_AES128_CM_HMAC_SHA1_32 = 0x0002;
+ public static final int SRTP_NULL_HMAC_SHA1_80 = 0x0005;
+ public static final int SRTP_NULL_HMAC_SHA1_32 = 0x0006;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/SSL3Mac.java b/core/src/main/java/org/bouncycastle/crypto/tls/SSL3Mac.java
new file mode 100644
index 00000000..0d2e2f19
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/SSL3Mac.java
@@ -0,0 +1,114 @@
+package org.bouncycastle.crypto.tls;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * HMAC implementation based on original internet draft for HMAC (RFC 2104)
+ * <p/>
+ * The difference is that padding is concatenated versus XORed with the key
+ * <p/>
+ * H(K + opad, H(K + ipad, text))
+ */
+public class SSL3Mac
+ implements Mac
+{
+ private final static byte IPAD_BYTE = (byte)0x36;
+ private final static byte OPAD_BYTE = (byte)0x5C;
+
+ static final byte[] IPAD = genPad(IPAD_BYTE, 48);
+ static final byte[] OPAD = genPad(OPAD_BYTE, 48);
+
+ private Digest digest;
+
+ private byte[] secret;
+ private int padLength;
+
+ /**
+ * Base constructor for one of the standard digest algorithms that the byteLength of
+ * the algorithm is know for. Behaviour is undefined for digests other than MD5 or SHA1.
+ *
+ * @param digest the digest.
+ */
+ public SSL3Mac(Digest digest)
+ {
+ this.digest = digest;
+
+ if (digest.getDigestSize() == 20)
+ {
+ this.padLength = 40;
+ }
+ else
+ {
+ this.padLength = 48;
+ }
+ }
+
+ public String getAlgorithmName()
+ {
+ return digest.getAlgorithmName() + "/SSL3MAC";
+ }
+
+ public Digest getUnderlyingDigest()
+ {
+ return digest;
+ }
+
+ public void init(CipherParameters params)
+ {
+ secret = Arrays.clone(((KeyParameter)params).getKey());
+
+ reset();
+ }
+
+ public int getMacSize()
+ {
+ return digest.getDigestSize();
+ }
+
+ public void update(byte in)
+ {
+ digest.update(in);
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ {
+ digest.update(in, inOff, len);
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ {
+ byte[] tmp = new byte[digest.getDigestSize()];
+ digest.doFinal(tmp, 0);
+
+ digest.update(secret, 0, secret.length);
+ digest.update(OPAD, 0, padLength);
+ digest.update(tmp, 0, tmp.length);
+
+ int len = digest.doFinal(out, outOff);
+
+ reset();
+
+ return len;
+ }
+
+ /**
+ * Reset the mac generator.
+ */
+ public void reset()
+ {
+ digest.reset();
+ digest.update(secret, 0, secret.length);
+ digest.update(IPAD, 0, padLength);
+ }
+
+ private static byte[] genPad(byte b, int count)
+ {
+ byte[] padding = new byte[count];
+ Arrays.fill(padding, b);
+ return padding;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java b/core/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java
new file mode 100644
index 00000000..a7701fe4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java
@@ -0,0 +1,57 @@
+package org.bouncycastle.crypto.tls;
+
+public class SecurityParameters
+{
+
+ int entity = -1;
+ int prfAlgorithm = -1;
+ short compressionAlgorithm = -1;
+ int verifyDataLength = -1;
+ byte[] masterSecret = null;
+ byte[] clientRandom = null;
+ byte[] serverRandom = null;
+
+ /**
+ * @return {@link ConnectionEnd}
+ */
+ public int getEntity()
+ {
+ return entity;
+ }
+
+ /**
+ * @return {@link PRFAlgorithm}
+ */
+ public int getPrfAlgorithm()
+ {
+ return prfAlgorithm;
+ }
+
+ /**
+ * @return {@link CompressionMethod}
+ */
+ public short getCompressionAlgorithm()
+ {
+ return compressionAlgorithm;
+ }
+
+ public int getVerifyDataLength()
+ {
+ return verifyDataLength;
+ }
+
+ public byte[] getMasterSecret()
+ {
+ return masterSecret;
+ }
+
+ public byte[] getClientRandom()
+ {
+ return clientRandom;
+ }
+
+ public byte[] getServerRandom()
+ {
+ return serverRandom;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/ServerOnlyTlsAuthentication.java b/core/src/main/java/org/bouncycastle/crypto/tls/ServerOnlyTlsAuthentication.java
new file mode 100644
index 00000000..eccbb3f6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/ServerOnlyTlsAuthentication.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.crypto.tls;
+
+public abstract class ServerOnlyTlsAuthentication
+ implements TlsAuthentication
+{
+ public final TlsCredentials getClientCredentials(CertificateRequest certificateRequest)
+ {
+ return null;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/SignatureAlgorithm.java b/core/src/main/java/org/bouncycastle/crypto/tls/SignatureAlgorithm.java
new file mode 100644
index 00000000..e63c7936
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/SignatureAlgorithm.java
@@ -0,0 +1,13 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 5246 7.4.1.4.1 (in RFC 2246, there were no specific values assigned)
+ */
+public class SignatureAlgorithm
+{
+
+ public static final short anonymous = 0;
+ public static final short rsa = 1;
+ public static final short dsa = 2;
+ public static final short ecdsa = 3;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/SignatureAndHashAlgorithm.java b/core/src/main/java/org/bouncycastle/crypto/tls/SignatureAndHashAlgorithm.java
new file mode 100644
index 00000000..7ad46442
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/SignatureAndHashAlgorithm.java
@@ -0,0 +1,98 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * RFC 5246 7.4.1.4.1
+ */
+public class SignatureAndHashAlgorithm
+{
+
+ private short hash;
+ private short signature;
+
+ /**
+ * @param hash {@link HashAlgorithm}
+ * @param signature {@link SignatureAlgorithm}
+ */
+ public SignatureAndHashAlgorithm(short hash, short signature)
+ {
+
+ if (!TlsUtils.isValidUint8(hash))
+ {
+ throw new IllegalArgumentException("'hash' should be a uint8");
+ }
+ if (!TlsUtils.isValidUint8(signature))
+ {
+ throw new IllegalArgumentException("'signature' should be a uint8");
+ }
+ if (signature == SignatureAlgorithm.anonymous)
+ {
+ throw new IllegalArgumentException("'signature' MUST NOT be \"anonymous\"");
+ }
+
+ this.hash = hash;
+ this.signature = signature;
+ }
+
+ /**
+ * @return {@link HashAlgorithm}
+ */
+ public short getHash()
+ {
+ return hash;
+ }
+
+ /**
+ * @return {@link SignatureAlgorithm}
+ */
+ public short getSignature()
+ {
+ return signature;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (!(obj instanceof SignatureAndHashAlgorithm))
+ {
+ return false;
+ }
+ SignatureAndHashAlgorithm other = (SignatureAndHashAlgorithm)obj;
+ return other.getHash() == getHash() && other.getSignature() == getSignature();
+ }
+
+ public int hashCode()
+ {
+ return (getHash() << 8) | getSignature();
+ }
+
+ /**
+ * Encode this {@link SignatureAndHashAlgorithm} to an {@link OutputStream}.
+ *
+ * @param output the {@link OutputStream} to encode to.
+ * @throws IOException
+ */
+ public void encode(OutputStream output)
+ throws IOException
+ {
+ TlsUtils.writeUint8(hash, output);
+ TlsUtils.writeUint8(signature, output);
+ }
+
+ /**
+ * Parse a {@link SignatureAndHashAlgorithm} from an {@link InputStream}.
+ *
+ * @param input the {@link InputStream} to parse from.
+ * @return a {@link SignatureAndHashAlgorithm} object.
+ * @throws IOException
+ */
+ public static SignatureAndHashAlgorithm parse(InputStream input)
+ throws IOException
+ {
+ short hash = TlsUtils.readUint8(input);
+ short signature = TlsUtils.readUint8(input);
+ return new SignatureAndHashAlgorithm(hash, signature);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/SupplementalDataEntry.java b/core/src/main/java/org/bouncycastle/crypto/tls/SupplementalDataEntry.java
new file mode 100644
index 00000000..5a71f9b0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/SupplementalDataEntry.java
@@ -0,0 +1,24 @@
+package org.bouncycastle.crypto.tls;
+
+public class SupplementalDataEntry
+{
+
+ private int supp_data_type;
+ private byte[] data;
+
+ public SupplementalDataEntry(int supp_data_type, byte[] data)
+ {
+ this.supp_data_type = supp_data_type;
+ this.data = data;
+ }
+
+ public int getDataType()
+ {
+ return supp_data_type;
+ }
+
+ public byte[] getData()
+ {
+ return data;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/SupplementalDataType.java b/core/src/main/java/org/bouncycastle/crypto/tls/SupplementalDataType.java
new file mode 100644
index 00000000..218f36b1
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/SupplementalDataType.java
@@ -0,0 +1,12 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 4680
+ */
+public class SupplementalDataType
+{
+ /*
+ * RFC 4681
+ */
+ public static final int user_mapping_data = 0;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsAEADCipher.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsAEADCipher.java
new file mode 100644
index 00000000..dbf9d79a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsAEADCipher.java
@@ -0,0 +1,197 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+import org.bouncycastle.crypto.modes.AEADBlockCipher;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+
+public class TlsAEADCipher
+ implements TlsCipher
+{
+
+ protected TlsContext context;
+ protected int macSize;
+ protected int nonce_explicit_length;
+
+ protected AEADBlockCipher encryptCipher;
+ protected AEADBlockCipher decryptCipher;
+
+ protected byte[] encryptImplicitNonce, decryptImplicitNonce;
+
+ public TlsAEADCipher(TlsContext context, AEADBlockCipher clientWriteCipher, AEADBlockCipher serverWriteCipher,
+ int cipherKeySize, int macSize)
+ throws IOException
+ {
+
+ if (!ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(context.getServerVersion().getEquivalentTLSVersion()))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ this.context = context;
+ this.macSize = macSize;
+
+ // NOTE: Valid for RFC 5288 ciphers but may need review for other AEAD ciphers
+ this.nonce_explicit_length = 8;
+
+ // TODO SecurityParameters.fixed_iv_length
+ int fixed_iv_length = 4;
+
+ int key_block_size = (2 * cipherKeySize) + (2 * fixed_iv_length);
+
+ byte[] key_block = TlsUtils.calculateKeyBlock(context, key_block_size);
+
+ int offset = 0;
+
+ KeyParameter client_write_key = new KeyParameter(key_block, offset, cipherKeySize);
+ offset += cipherKeySize;
+ KeyParameter server_write_key = new KeyParameter(key_block, offset, cipherKeySize);
+ offset += cipherKeySize;
+ byte[] client_write_IV = Arrays.copyOfRange(key_block, offset, offset + fixed_iv_length);
+ offset += fixed_iv_length;
+ byte[] server_write_IV = Arrays.copyOfRange(key_block, offset, offset + fixed_iv_length);
+ offset += fixed_iv_length;
+
+ if (offset != key_block_size)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ KeyParameter encryptKey, decryptKey;
+ if (context.isServer())
+ {
+ this.encryptCipher = serverWriteCipher;
+ this.decryptCipher = clientWriteCipher;
+ this.encryptImplicitNonce = server_write_IV;
+ this.decryptImplicitNonce = client_write_IV;
+ encryptKey = server_write_key;
+ decryptKey = client_write_key;
+ }
+ else
+ {
+ this.encryptCipher = clientWriteCipher;
+ this.decryptCipher = serverWriteCipher;
+ this.encryptImplicitNonce = client_write_IV;
+ this.decryptImplicitNonce = server_write_IV;
+ encryptKey = client_write_key;
+ decryptKey = server_write_key;
+ }
+
+ byte[] dummyNonce = new byte[fixed_iv_length + nonce_explicit_length];
+
+ this.encryptCipher.init(true, new AEADParameters(encryptKey, 8 * macSize, dummyNonce));
+ this.decryptCipher.init(false, new AEADParameters(decryptKey, 8 * macSize, dummyNonce));
+ }
+
+ public int getPlaintextLimit(int ciphertextLimit)
+ {
+ // TODO We ought to be able to ask the decryptCipher (independently of it's current state!)
+ return ciphertextLimit - macSize - nonce_explicit_length;
+ }
+
+ public byte[] encodePlaintext(long seqNo, short type, byte[] plaintext, int offset, int len)
+ throws IOException
+ {
+
+ byte[] nonce = new byte[this.encryptImplicitNonce.length + nonce_explicit_length];
+ System.arraycopy(encryptImplicitNonce, 0, nonce, 0, encryptImplicitNonce.length);
+
+ /*
+ * RFC 5288 The nonce_explicit MAY be the 64-bit sequence number.
+ *
+ * (May need review for other AEAD ciphers).
+ */
+ TlsUtils.writeUint64(seqNo, nonce, encryptImplicitNonce.length);
+
+ int plaintextOffset = offset;
+ int plaintextLength = len;
+ int ciphertextLength = encryptCipher.getOutputSize(plaintextLength);
+
+ byte[] output = new byte[nonce_explicit_length + ciphertextLength];
+ System.arraycopy(nonce, encryptImplicitNonce.length, output, 0, nonce_explicit_length);
+ int outputPos = nonce_explicit_length;
+
+ encryptCipher.init(true,
+ new AEADParameters(null, 8 * macSize, nonce, getAdditionalData(seqNo, type, plaintextLength)));
+
+ outputPos += encryptCipher.processBytes(plaintext, plaintextOffset, plaintextLength, output, outputPos);
+ try
+ {
+ outputPos += encryptCipher.doFinal(output, outputPos);
+ }
+ catch (Exception e)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ if (outputPos != output.length)
+ {
+ // NOTE: Existing AEAD cipher implementations all give exact output lengths
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ return output;
+ }
+
+ public byte[] decodeCiphertext(long seqNo, short type, byte[] ciphertext, int offset, int len)
+ throws IOException
+ {
+
+ if (getPlaintextLimit(len) < 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ byte[] nonce = new byte[this.decryptImplicitNonce.length + nonce_explicit_length];
+ System.arraycopy(decryptImplicitNonce, 0, nonce, 0, decryptImplicitNonce.length);
+ System.arraycopy(ciphertext, offset, nonce, decryptImplicitNonce.length, nonce_explicit_length);
+
+ int ciphertextOffset = offset + nonce_explicit_length;
+ int ciphertextLength = len - nonce_explicit_length;
+ int plaintextLength = decryptCipher.getOutputSize(ciphertextLength);
+
+ byte[] output = new byte[plaintextLength];
+ int outputPos = 0;
+
+ decryptCipher.init(false,
+ new AEADParameters(null, 8 * macSize, nonce, getAdditionalData(seqNo, type, plaintextLength)));
+
+ outputPos += decryptCipher.processBytes(ciphertext, ciphertextOffset, ciphertextLength, output, outputPos);
+
+ try
+ {
+ outputPos += decryptCipher.doFinal(output, outputPos);
+ }
+ catch (Exception e)
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+ }
+
+ if (outputPos != output.length)
+ {
+ // NOTE: Existing AEAD cipher implementations all give exact output lengths
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ return output;
+ }
+
+ protected byte[] getAdditionalData(long seqNo, short type, int len)
+ throws IOException
+ {
+ /*
+ * additional_data = seq_num + TLSCompressed.type + TLSCompressed.version +
+ * TLSCompressed.length
+ */
+
+ byte[] additional_data = new byte[13];
+ TlsUtils.writeUint64(seqNo, additional_data, 0);
+ TlsUtils.writeUint8(type, additional_data, 8);
+ TlsUtils.writeVersion(context.getServerVersion(), additional_data, 9);
+ TlsUtils.writeUint16(len, additional_data, 11);
+
+ return additional_data;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsAgreementCredentials.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsAgreementCredentials.java
new file mode 100644
index 00000000..d8fe239c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsAgreementCredentials.java
@@ -0,0 +1,13 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+
+public interface TlsAgreementCredentials
+ extends TlsCredentials
+{
+
+ byte[] generateAgreement(AsymmetricKeyParameter peerPublicKey)
+ throws IOException;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsAuthentication.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsAuthentication.java
new file mode 100755
index 00000000..62c2616e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsAuthentication.java
@@ -0,0 +1,26 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+public interface TlsAuthentication
+{
+ /**
+ * Called by the protocol handler to report the server certificate
+ * Note: this method is responsible for certificate verification and validation
+ *
+ * @param serverCertificate the server certificate received
+ * @throws IOException
+ */
+ void notifyServerCertificate(Certificate serverCertificate)
+ throws IOException;
+
+ /**
+ * Return client credentials in response to server's certificate request
+ *
+ * @param certificateRequest details of the certificate request
+ * @return a TlsCredentials object or null for no client authentication
+ * @throws IOException
+ */
+ TlsCredentials getClientCredentials(CertificateRequest certificateRequest)
+ throws IOException;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java
new file mode 100644
index 00000000..0b218c18
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java
@@ -0,0 +1,313 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A generic TLS 1.0-1.1 / SSLv3 block cipher. This can be used for AES or 3DES for example.
+ */
+public class TlsBlockCipher
+ implements TlsCipher
+{
+
+ protected TlsContext context;
+ protected byte[] randomData;
+ protected boolean useExplicitIV;
+
+ protected BlockCipher encryptCipher;
+ protected BlockCipher decryptCipher;
+
+ protected TlsMac writeMac;
+ protected TlsMac readMac;
+
+ public TlsMac getWriteMac()
+ {
+ return writeMac;
+ }
+
+ public TlsMac getReadMac()
+ {
+ return readMac;
+ }
+
+ public TlsBlockCipher(TlsContext context, BlockCipher clientWriteCipher, BlockCipher serverWriteCipher,
+ Digest clientWriteDigest, Digest serverWriteDigest, int cipherKeySize)
+ throws IOException
+ {
+
+ this.context = context;
+
+ this.randomData = new byte[256];
+ context.getSecureRandom().nextBytes(randomData);
+
+ this.useExplicitIV = ProtocolVersion.TLSv11.isEqualOrEarlierVersionOf(context.getServerVersion()
+ .getEquivalentTLSVersion());
+
+ int key_block_size = (2 * cipherKeySize) + clientWriteDigest.getDigestSize()
+ + serverWriteDigest.getDigestSize();
+
+ // From TLS 1.1 onwards, block ciphers don't need client_write_IV
+ if (!useExplicitIV)
+ {
+ key_block_size += clientWriteCipher.getBlockSize() + serverWriteCipher.getBlockSize();
+ }
+
+ byte[] key_block = TlsUtils.calculateKeyBlock(context, key_block_size);
+
+ int offset = 0;
+
+ TlsMac clientWriteMac = new TlsMac(context, clientWriteDigest, key_block, offset,
+ clientWriteDigest.getDigestSize());
+ offset += clientWriteDigest.getDigestSize();
+ TlsMac serverWriteMac = new TlsMac(context, serverWriteDigest, key_block, offset,
+ serverWriteDigest.getDigestSize());
+ offset += serverWriteDigest.getDigestSize();
+
+ KeyParameter client_write_key = new KeyParameter(key_block, offset, cipherKeySize);
+ offset += cipherKeySize;
+ KeyParameter server_write_key = new KeyParameter(key_block, offset, cipherKeySize);
+ offset += cipherKeySize;
+
+ byte[] client_write_IV, server_write_IV;
+ if (useExplicitIV)
+ {
+ client_write_IV = new byte[clientWriteCipher.getBlockSize()];
+ server_write_IV = new byte[serverWriteCipher.getBlockSize()];
+ }
+ else
+ {
+ client_write_IV = Arrays.copyOfRange(key_block, offset, offset + clientWriteCipher.getBlockSize());
+ offset += clientWriteCipher.getBlockSize();
+ server_write_IV = Arrays.copyOfRange(key_block, offset, offset + serverWriteCipher.getBlockSize());
+ offset += serverWriteCipher.getBlockSize();
+ }
+
+ if (offset != key_block_size)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ CipherParameters encryptParams, decryptParams;
+ if (context.isServer())
+ {
+ this.writeMac = serverWriteMac;
+ this.readMac = clientWriteMac;
+ this.encryptCipher = serverWriteCipher;
+ this.decryptCipher = clientWriteCipher;
+ encryptParams = new ParametersWithIV(server_write_key, server_write_IV);
+ decryptParams = new ParametersWithIV(client_write_key, client_write_IV);
+ }
+ else
+ {
+ this.writeMac = clientWriteMac;
+ this.readMac = serverWriteMac;
+ this.encryptCipher = clientWriteCipher;
+ this.decryptCipher = serverWriteCipher;
+ encryptParams = new ParametersWithIV(client_write_key, client_write_IV);
+ decryptParams = new ParametersWithIV(server_write_key, server_write_IV);
+ }
+
+ this.encryptCipher.init(true, encryptParams);
+ this.decryptCipher.init(false, decryptParams);
+ }
+
+ public int getPlaintextLimit(int ciphertextLimit)
+ {
+ int blockSize = encryptCipher.getBlockSize();
+ int macSize = writeMac.getSize();
+
+ int result = ciphertextLimit - (ciphertextLimit % blockSize) - macSize - 1;
+ if (useExplicitIV)
+ {
+ result -= blockSize;
+ }
+
+ return result;
+ }
+
+ public byte[] encodePlaintext(long seqNo, short type, byte[] plaintext, int offset, int len)
+ {
+ int blockSize = encryptCipher.getBlockSize();
+ int macSize = writeMac.getSize();
+
+ ProtocolVersion version = context.getServerVersion();
+
+ int padding_length = blockSize - 1 - ((len + macSize) % blockSize);
+
+ // TODO[DTLS] Consider supporting in DTLS (without exceeding send limit though)
+ if (!version.isDTLS() && !version.isSSL())
+ {
+ // Add a random number of extra blocks worth of padding
+ int maxExtraPadBlocks = (255 - padding_length) / blockSize;
+ int actualExtraPadBlocks = chooseExtraPadBlocks(context.getSecureRandom(), maxExtraPadBlocks);
+ padding_length += actualExtraPadBlocks * blockSize;
+ }
+
+ int totalSize = len + macSize + padding_length + 1;
+ if (useExplicitIV)
+ {
+ totalSize += blockSize;
+ }
+
+ byte[] outbuf = new byte[totalSize];
+ int outOff = 0;
+
+ if (useExplicitIV)
+ {
+ byte[] explicitIV = new byte[blockSize];
+ context.getSecureRandom().nextBytes(explicitIV);
+
+ encryptCipher.init(true, new ParametersWithIV(null, explicitIV));
+
+ System.arraycopy(explicitIV, 0, outbuf, outOff, blockSize);
+ outOff += blockSize;
+ }
+
+ byte[] mac = writeMac.calculateMac(seqNo, type, plaintext, offset, len);
+
+ System.arraycopy(plaintext, offset, outbuf, outOff, len);
+ System.arraycopy(mac, 0, outbuf, outOff + len, mac.length);
+
+ int padOffset = outOff + len + mac.length;
+ for (int i = 0; i <= padding_length; i++)
+ {
+ outbuf[i + padOffset] = (byte)padding_length;
+ }
+ for (int i = outOff; i < totalSize; i += blockSize)
+ {
+ encryptCipher.processBlock(outbuf, i, outbuf, i);
+ }
+ return outbuf;
+ }
+
+ public byte[] decodeCiphertext(long seqNo, short type, byte[] ciphertext, int offset, int len)
+ throws IOException
+ {
+ int blockSize = decryptCipher.getBlockSize();
+ int macSize = readMac.getSize();
+
+ int minLen = Math.max(blockSize, macSize + 1);
+ if (useExplicitIV)
+ {
+ minLen += blockSize;
+ }
+
+ if (len < minLen)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ if (len % blockSize != 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.decryption_failed);
+ }
+
+ if (useExplicitIV)
+ {
+ decryptCipher.init(false, new ParametersWithIV(null, ciphertext, offset, blockSize));
+
+ offset += blockSize;
+ len -= blockSize;
+ }
+
+ for (int i = 0; i < len; i += blockSize)
+ {
+ decryptCipher.processBlock(ciphertext, offset + i, ciphertext, offset + i);
+ }
+
+ // If there's anything wrong with the padding, this will return zero
+ int totalPad = checkPaddingConstantTime(ciphertext, offset, len, blockSize, macSize);
+
+ int macInputLen = len - totalPad - macSize;
+
+ byte[] decryptedMac = Arrays.copyOfRange(ciphertext, offset + macInputLen, offset + macInputLen + macSize);
+ byte[] calculatedMac = readMac.calculateMacConstantTime(seqNo, type, ciphertext, offset, macInputLen, len
+ - macSize, randomData);
+
+ boolean badMac = !Arrays.constantTimeAreEqual(calculatedMac, decryptedMac);
+
+ if (badMac || totalPad == 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+ }
+
+ return Arrays.copyOfRange(ciphertext, offset, offset + macInputLen);
+ }
+
+ protected int checkPaddingConstantTime(byte[] buf, int off, int len, int blockSize, int macSize)
+ {
+ int end = off + len;
+ byte lastByte = buf[end - 1];
+ int padlen = lastByte & 0xff;
+ int totalPad = padlen + 1;
+
+ int dummyIndex = 0;
+ byte padDiff = 0;
+
+ if ((context.getServerVersion().isSSL() && totalPad > blockSize) || (macSize + totalPad > len))
+ {
+ totalPad = 0;
+ }
+ else
+ {
+ int padPos = end - totalPad;
+ do
+ {
+ padDiff |= (buf[padPos++] ^ lastByte);
+ }
+ while (padPos < end);
+
+ dummyIndex = totalPad;
+
+ if (padDiff != 0)
+ {
+ totalPad = 0;
+ }
+ }
+
+ // Run some extra dummy checks so the number of checks is always constant
+ {
+ byte[] dummyPad = randomData;
+ while (dummyIndex < 256)
+ {
+ padDiff |= (dummyPad[dummyIndex++] ^ lastByte);
+ }
+ // Ensure the above loop is not eliminated
+ dummyPad[0] ^= padDiff;
+ }
+
+ return totalPad;
+ }
+
+ protected int chooseExtraPadBlocks(SecureRandom r, int max)
+ {
+ // return r.nextInt(max + 1);
+
+ int x = r.nextInt();
+ int n = lowestBitSet(x);
+ return Math.min(n, max);
+ }
+
+ protected int lowestBitSet(int x)
+ {
+ if (x == 0)
+ {
+ return 32;
+ }
+
+ int n = 0;
+ while ((x & 1) == 0)
+ {
+ ++n;
+ x >>= 1;
+ }
+ return n;
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsCipher.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsCipher.java
new file mode 100644
index 00000000..2f0af080
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsCipher.java
@@ -0,0 +1,14 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+public interface TlsCipher
+{
+ int getPlaintextLimit(int ciphertextLimit);
+
+ byte[] encodePlaintext(long seqNo, short type, byte[] plaintext, int offset, int len)
+ throws IOException;
+
+ byte[] decodeCiphertext(long seqNo, short type, byte[] ciphertext, int offset, int len)
+ throws IOException;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsCipherFactory.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsCipherFactory.java
new file mode 100644
index 00000000..29d961ff
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsCipherFactory.java
@@ -0,0 +1,13 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+public interface TlsCipherFactory
+{
+
+ /**
+ * See enumeration classes EncryptionAlgorithm, MACAlgorithm for appropriate argument values
+ */
+ TlsCipher createCipher(TlsContext context, int encryptionAlgorithm, int macAlgorithm)
+ throws IOException;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java
new file mode 100644
index 00000000..62444fa8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java
@@ -0,0 +1,76 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Vector;
+
+public interface TlsClient
+ extends TlsPeer
+{
+
+ void init(TlsClientContext context);
+
+ ProtocolVersion getClientHelloRecordLayerVersion();
+
+ ProtocolVersion getClientVersion();
+
+ int[] getCipherSuites();
+
+ short[] getCompressionMethods();
+
+ // Hashtable is (Integer -> byte[])
+ Hashtable getClientExtensions()
+ throws IOException;
+
+ void notifyServerVersion(ProtocolVersion selectedVersion)
+ throws IOException;
+
+ void notifySessionID(byte[] sessionID);
+
+ void notifySelectedCipherSuite(int selectedCipherSuite);
+
+ void notifySelectedCompressionMethod(short selectedCompressionMethod);
+
+ void notifySecureRenegotiation(boolean secureNegotiation)
+ throws IOException;
+
+ // Hashtable is (Integer -> byte[])
+ void processServerExtensions(Hashtable serverExtensions)
+ throws IOException;
+
+ // Vector is (SupplementalDataEntry)
+ void processServerSupplementalData(Vector serverSupplementalData)
+ throws IOException;
+
+ TlsKeyExchange getKeyExchange()
+ throws IOException;
+
+ TlsAuthentication getAuthentication()
+ throws IOException;
+
+ // Vector is (SupplementalDataEntry)
+ Vector getClientSupplementalData()
+ throws IOException;
+
+ TlsCompression getCompression()
+ throws IOException;
+
+ TlsCipher getCipher()
+ throws IOException;
+
+ /**
+ * RFC 5077 3.3. NewSessionTicket Handshake Message
+ * <p/>
+ * This method will be called (only) when a NewSessionTicket handshake message is received. The
+ * ticket is opaque to the client and clients MUST NOT examine the ticket under the assumption
+ * that it complies with e.g. <i>RFC 5077 4. Recommended Ticket Construction</i>.
+ *
+ * @param newSessionTicket The ticket.
+ * @throws IOException
+ */
+ void notifyNewSessionTicket(NewSessionTicket newSessionTicket)
+ throws IOException;
+
+ void notifyHandshakeComplete()
+ throws IOException;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsClientContext.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsClientContext.java
new file mode 100644
index 00000000..db9f15b7
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsClientContext.java
@@ -0,0 +1,6 @@
+package org.bouncycastle.crypto.tls;
+
+public interface TlsClientContext
+ extends TlsContext
+{
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsClientContextImpl.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsClientContextImpl.java
new file mode 100644
index 00000000..d91f7f8e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsClientContextImpl.java
@@ -0,0 +1,19 @@
+package org.bouncycastle.crypto.tls;
+
+import java.security.SecureRandom;
+
+class TlsClientContextImpl
+ extends AbstractTlsContext
+ implements TlsClientContext
+{
+
+ TlsClientContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters)
+ {
+ super(secureRandom, securityParameters);
+ }
+
+ public boolean isServer()
+ {
+ return false;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java
new file mode 100644
index 00000000..33cd9141
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java
@@ -0,0 +1,732 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.bouncycastle.crypto.prng.ThreadedSeedGenerator;
+import org.bouncycastle.util.Arrays;
+
+public class TlsClientProtocol
+ extends TlsProtocol
+{
+
+ protected TlsClient tlsClient = null;
+ protected TlsClientContextImpl tlsClientContext = null;
+
+ protected int[] offeredCipherSuites = null;
+ protected short[] offeredCompressionMethods = null;
+ protected Hashtable clientExtensions = null;
+
+ protected int selectedCipherSuite;
+ protected short selectedCompressionMethod;
+
+ protected TlsKeyExchange keyExchange = null;
+ protected TlsAuthentication authentication = null;
+ protected CertificateRequest certificateRequest = null;
+
+ private static SecureRandom createSecureRandom()
+ {
+ /*
+ * We use our threaded seed generator to generate a good random seed. If the user has a
+ * better random seed, he should use the constructor with a SecureRandom.
+ */
+ ThreadedSeedGenerator tsg = new ThreadedSeedGenerator();
+ SecureRandom random = new SecureRandom();
+
+ /*
+ * Hopefully, 20 bytes in fast mode are good enough.
+ */
+ random.setSeed(tsg.generateSeed(20, true));
+
+ return random;
+ }
+
+ public TlsClientProtocol(InputStream input, OutputStream output)
+ {
+ this(input, output, createSecureRandom());
+ }
+
+ public TlsClientProtocol(InputStream input, OutputStream output, SecureRandom secureRandom)
+ {
+ super(input, output, secureRandom);
+ }
+
+ /**
+ * Initiates a TLS handshake in the role of client
+ *
+ * @param tlsClient
+ * @throws IOException If handshake was not successful.
+ */
+ public void connect(TlsClient tlsClient)
+ throws IOException
+ {
+ if (tlsClient == null)
+ {
+ throw new IllegalArgumentException("'tlsClient' cannot be null");
+ }
+ if (this.tlsClient != null)
+ {
+ throw new IllegalStateException("connect can only be called once");
+ }
+
+ this.tlsClient = tlsClient;
+
+ this.securityParameters = new SecurityParameters();
+ this.securityParameters.entity = ConnectionEnd.client;
+ this.securityParameters.clientRandom = createRandomBlock(secureRandom);
+
+ this.tlsClientContext = new TlsClientContextImpl(secureRandom, securityParameters);
+ this.tlsClient.init(tlsClientContext);
+ this.recordStream.init(tlsClientContext);
+
+ sendClientHelloMessage();
+ this.connection_state = CS_CLIENT_HELLO;
+
+ completeHandshake();
+
+ this.tlsClient.notifyHandshakeComplete();
+ }
+
+ protected AbstractTlsContext getContext()
+ {
+ return tlsClientContext;
+ }
+
+ protected TlsPeer getPeer()
+ {
+ return tlsClient;
+ }
+
+ protected void handleChangeCipherSpecMessage()
+ throws IOException
+ {
+
+ switch (this.connection_state)
+ {
+ case CS_CLIENT_FINISHED:
+ {
+ if (this.expectSessionTicket)
+ {
+ /*
+ * RFC 5077 3.3. This message MUST be sent if the server included a SessionTicket
+ * extension in the ServerHello.
+ */
+ this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
+ }
+ // NB: Fall through to next case label
+ }
+ case CS_SERVER_SESSION_TICKET:
+ this.connection_state = CS_SERVER_CHANGE_CIPHER_SPEC;
+ break;
+ default:
+ this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
+ }
+ }
+
+ protected void handleHandshakeMessage(short type, byte[] data)
+ throws IOException
+ {
+ ByteArrayInputStream buf = new ByteArrayInputStream(data);
+
+ switch (type)
+ {
+ case HandshakeType.certificate:
+ {
+ switch (this.connection_state)
+ {
+ case CS_SERVER_HELLO:
+ {
+ handleSupplementalData(null);
+ // NB: Fall through to next case label
+ }
+ case CS_SERVER_SUPPLEMENTAL_DATA:
+ {
+ // Parse the Certificate message and send to cipher suite
+
+ Certificate serverCertificate = Certificate.parse(buf);
+
+ assertEmpty(buf);
+
+ this.keyExchange.processServerCertificate(serverCertificate);
+
+ this.authentication = tlsClient.getAuthentication();
+ this.authentication.notifyServerCertificate(serverCertificate);
+
+ break;
+ }
+ default:
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+
+ this.connection_state = CS_SERVER_CERTIFICATE;
+ break;
+ }
+ case HandshakeType.finished:
+ switch (this.connection_state)
+ {
+ case CS_SERVER_CHANGE_CIPHER_SPEC:
+ processFinishedMessage(buf);
+ this.connection_state = CS_SERVER_FINISHED;
+ break;
+ default:
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+ break;
+ case HandshakeType.server_hello:
+ switch (this.connection_state)
+ {
+ case CS_CLIENT_HELLO:
+ receiveServerHelloMessage(buf);
+ this.connection_state = CS_SERVER_HELLO;
+
+ securityParameters.prfAlgorithm = getPRFAlgorithm(selectedCipherSuite);
+ securityParameters.compressionAlgorithm = this.selectedCompressionMethod;
+
+ /*
+ * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify
+ * verify_data_length has a verify_data_length equal to 12. This includes all
+ * existing cipher suites.
+ */
+ securityParameters.verifyDataLength = 12;
+
+ recordStream.notifyHelloComplete();
+
+ break;
+ default:
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+ break;
+ case HandshakeType.supplemental_data:
+ {
+ switch (this.connection_state)
+ {
+ case CS_SERVER_HELLO:
+ handleSupplementalData(readSupplementalDataMessage(buf));
+ break;
+ default:
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.server_hello_done:
+ switch (this.connection_state)
+ {
+ case CS_SERVER_HELLO:
+ {
+ handleSupplementalData(null);
+ // NB: Fall through to next case label
+ }
+ case CS_SERVER_SUPPLEMENTAL_DATA:
+ {
+
+ // There was no server certificate message; check it's OK
+ this.keyExchange.skipServerCredentials();
+ this.authentication = null;
+
+ // NB: Fall through to next case label
+ }
+ case CS_SERVER_CERTIFICATE:
+
+ // There was no server key exchange message; check it's OK
+ this.keyExchange.skipServerKeyExchange();
+
+ // NB: Fall through to next case label
+
+ case CS_SERVER_KEY_EXCHANGE:
+ case CS_CERTIFICATE_REQUEST:
+
+ assertEmpty(buf);
+
+ this.connection_state = CS_SERVER_HELLO_DONE;
+
+ Vector clientSupplementalData = tlsClient.getClientSupplementalData();
+ if (clientSupplementalData != null)
+ {
+ sendSupplementalDataMessage(clientSupplementalData);
+ }
+ this.connection_state = CS_CLIENT_SUPPLEMENTAL_DATA;
+
+ TlsCredentials clientCreds = null;
+ if (certificateRequest == null)
+ {
+ this.keyExchange.skipClientCredentials();
+ }
+ else
+ {
+ clientCreds = this.authentication.getClientCredentials(certificateRequest);
+
+ if (clientCreds == null)
+ {
+ this.keyExchange.skipClientCredentials();
+
+ /*
+ * RFC 5246 If no suitable certificate is available, the client MUST send a
+ * certificate message containing no certificates.
+ *
+ * NOTE: In previous RFCs, this was SHOULD instead of MUST.
+ */
+ sendCertificateMessage(Certificate.EMPTY_CHAIN);
+ }
+ else
+ {
+ this.keyExchange.processClientCredentials(clientCreds);
+
+ sendCertificateMessage(clientCreds.getCertificate());
+ }
+ }
+
+ this.connection_state = CS_CLIENT_CERTIFICATE;
+
+ /*
+ * Send the client key exchange message, depending on the key exchange we are using
+ * in our CipherSuite.
+ */
+ sendClientKeyExchangeMessage();
+
+ establishMasterSecret(getContext(), keyExchange);
+
+ /*
+ * Initialize our cipher suite
+ */
+ recordStream.setPendingConnectionState(tlsClient.getCompression(), tlsClient.getCipher());
+
+ this.connection_state = CS_CLIENT_KEY_EXCHANGE;
+
+ if (clientCreds != null && clientCreds instanceof TlsSignerCredentials)
+ {
+ /*
+ * TODO RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm
+ * prepended from TLS 1.2
+ */
+ TlsSignerCredentials signerCreds = (TlsSignerCredentials)clientCreds;
+ byte[] md5andsha1 = recordStream.getCurrentHash(null);
+ byte[] clientCertificateSignature = signerCreds.generateCertificateSignature(md5andsha1);
+ sendCertificateVerifyMessage(clientCertificateSignature);
+
+ this.connection_state = CS_CERTIFICATE_VERIFY;
+ }
+
+ sendChangeCipherSpecMessage();
+ this.connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC;
+
+ sendFinishedMessage();
+ this.connection_state = CS_CLIENT_FINISHED;
+ break;
+ default:
+ this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
+ }
+ break;
+ case HandshakeType.server_key_exchange:
+ {
+ switch (this.connection_state)
+ {
+ case CS_SERVER_HELLO:
+ {
+ handleSupplementalData(null);
+ // NB: Fall through to next case label
+ }
+ case CS_SERVER_SUPPLEMENTAL_DATA:
+ {
+
+ // There was no server certificate message; check it's OK
+ this.keyExchange.skipServerCredentials();
+ this.authentication = null;
+
+ // NB: Fall through to next case label
+ }
+ case CS_SERVER_CERTIFICATE:
+
+ this.keyExchange.processServerKeyExchange(buf);
+
+ assertEmpty(buf);
+ break;
+
+ default:
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+
+ this.connection_state = CS_SERVER_KEY_EXCHANGE;
+ break;
+ }
+ case HandshakeType.certificate_request:
+ {
+ switch (this.connection_state)
+ {
+ case CS_SERVER_CERTIFICATE:
+
+ // There was no server key exchange message; check it's OK
+ this.keyExchange.skipServerKeyExchange();
+
+ // NB: Fall through to next case label
+
+ case CS_SERVER_KEY_EXCHANGE:
+ {
+ if (this.authentication == null)
+ {
+ /*
+ * RFC 2246 7.4.4. It is a fatal handshake_failure alert for an anonymous server
+ * to request client identification.
+ */
+ this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
+ }
+
+ this.certificateRequest = CertificateRequest.parse(buf);
+
+ assertEmpty(buf);
+
+ this.keyExchange.validateCertificateRequest(this.certificateRequest);
+
+ break;
+ }
+ default:
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+
+ this.connection_state = CS_CERTIFICATE_REQUEST;
+ break;
+ }
+ case HandshakeType.session_ticket:
+ {
+ switch (this.connection_state)
+ {
+ case CS_CLIENT_FINISHED:
+ if (!this.expectSessionTicket)
+ {
+ /*
+ * RFC 5077 3.3. This message MUST NOT be sent if the server did not include a
+ * SessionTicket extension in the ServerHello.
+ */
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+ receiveNewSessionTicketMessage(buf);
+ this.connection_state = CS_SERVER_SESSION_TICKET;
+ break;
+ default:
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+ }
+ case HandshakeType.hello_request:
+
+ assertEmpty(buf);
+
+ /*
+ * RFC 2246 7.4.1.1 Hello request This message will be ignored by the client if the
+ * client is currently negotiating a session. This message may be ignored by the client
+ * if it does not wish to renegotiate a session, or the client may, if it wishes,
+ * respond with a no_renegotiation alert.
+ */
+ if (this.connection_state == CS_SERVER_FINISHED)
+ {
+ String message = "Renegotiation not supported";
+ raiseWarning(AlertDescription.no_renegotiation, message);
+ }
+ break;
+ case HandshakeType.client_key_exchange:
+ case HandshakeType.certificate_verify:
+ case HandshakeType.client_hello:
+ case HandshakeType.hello_verify_request:
+ default:
+ // We do not support this!
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ break;
+ }
+ }
+
+ protected void handleSupplementalData(Vector serverSupplementalData)
+ throws IOException
+ {
+
+ this.tlsClient.processServerSupplementalData(serverSupplementalData);
+ this.connection_state = CS_SERVER_SUPPLEMENTAL_DATA;
+
+ this.keyExchange = tlsClient.getKeyExchange();
+ this.keyExchange.init(getContext());
+ }
+
+ protected void receiveNewSessionTicketMessage(ByteArrayInputStream buf)
+ throws IOException
+ {
+
+ NewSessionTicket newSessionTicket = NewSessionTicket.parse(buf);
+
+ TlsProtocol.assertEmpty(buf);
+
+ tlsClient.notifyNewSessionTicket(newSessionTicket);
+ }
+
+ protected void receiveServerHelloMessage(ByteArrayInputStream buf)
+ throws IOException
+ {
+
+ ProtocolVersion server_version = TlsUtils.readVersion(buf);
+ if (server_version.isDTLS())
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+ }
+
+ // Check that this matches what the server is sending in the record layer
+ if (!server_version.equals(recordStream.getReadVersion()))
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+ }
+
+ ProtocolVersion client_version = getContext().getClientVersion();
+ if (!server_version.isEqualOrEarlierVersionOf(client_version))
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+ }
+
+ this.recordStream.setWriteVersion(server_version);
+ getContext().setServerVersion(server_version);
+ this.tlsClient.notifyServerVersion(server_version);
+
+ /*
+ * Read the server random
+ */
+ securityParameters.serverRandom = TlsUtils.readFully(32, buf);
+
+ byte[] sessionID = TlsUtils.readOpaque8(buf);
+ if (sessionID.length > 32)
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+ }
+
+ this.tlsClient.notifySessionID(sessionID);
+
+ /*
+ * Find out which CipherSuite the server has chosen and check that it was one of the offered
+ * ones.
+ */
+ this.selectedCipherSuite = TlsUtils.readUint16(buf);
+ if (!arrayContains(offeredCipherSuites, this.selectedCipherSuite)
+ || this.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
+ || this.selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+ }
+
+ this.tlsClient.notifySelectedCipherSuite(this.selectedCipherSuite);
+
+ /*
+ * Find out which CompressionMethod the server has chosen and check that it was one of the
+ * offered ones.
+ */
+ short selectedCompressionMethod = TlsUtils.readUint8(buf);
+ if (!arrayContains(offeredCompressionMethods, selectedCompressionMethod))
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+ }
+
+ this.tlsClient.notifySelectedCompressionMethod(selectedCompressionMethod);
+
+ /*
+ * RFC3546 2.2 The extended server hello message format MAY be sent in place of the server
+ * hello message when the client has requested extended functionality via the extended
+ * client hello message specified in Section 2.1. ... Note that the extended server hello
+ * message is only sent in response to an extended client hello message. This prevents the
+ * possibility that the extended server hello message could "break" existing TLS 1.0
+ * clients.
+ */
+
+ /*
+ * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore
+ * extensions appearing in the client hello, and send a server hello containing no
+ * extensions.
+ */
+
+ // Integer -> byte[]
+ Hashtable serverExtensions = readExtensions(buf);
+
+ /*
+ * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an
+ * extended client hello message.
+ *
+ * However, see RFC 5746 exception below. We always include the SCSV, so an Extended Server
+ * Hello is always allowed.
+ */
+ if (serverExtensions != null)
+ {
+ Enumeration e = serverExtensions.keys();
+ while (e.hasMoreElements())
+ {
+ Integer extType = (Integer)e.nextElement();
+
+ /*
+ * RFC 5746 3.6. Note that sending a "renegotiation_info" extension in response to a
+ * ClientHello containing only the SCSV is an explicit exception to the prohibition
+ * in RFC 5246, Section 7.4.1.4, on the server sending unsolicited extensions and is
+ * only allowed because the client is signaling its willingness to receive the
+ * extension via the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.
+ */
+ if (!extType.equals(EXT_RenegotiationInfo)
+ && (clientExtensions == null || clientExtensions.get(extType) == null))
+ {
+ /*
+ * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless
+ * the same extension type appeared in the corresponding ClientHello. If a
+ * client receives an extension type in ServerHello that it did not request in
+ * the associated ClientHello, it MUST abort the handshake with an
+ * unsupported_extension fatal alert.
+ */
+ this.failWithError(AlertLevel.fatal, AlertDescription.unsupported_extension);
+ }
+ }
+
+ /*
+ * RFC 5746 3.4. Client Behavior: Initial Handshake
+ */
+ {
+ /*
+ * When a ServerHello is received, the client MUST check if it includes the
+ * "renegotiation_info" extension:
+ */
+ byte[] renegExtValue = (byte[])serverExtensions.get(EXT_RenegotiationInfo);
+ if (renegExtValue != null)
+ {
+ /*
+ * If the extension is present, set the secure_renegotiation flag to TRUE. The
+ * client MUST then verify that the length of the "renegotiated_connection"
+ * field is zero, and if it is not, MUST abort the handshake (by sending a fatal
+ * handshake_failure alert).
+ */
+ this.secure_renegotiation = true;
+
+ if (!Arrays.constantTimeAreEqual(renegExtValue, createRenegotiationInfo(TlsUtils.EMPTY_BYTES)))
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
+ }
+ }
+ }
+
+ this.expectSessionTicket = serverExtensions.containsKey(EXT_SessionTicket);
+ }
+
+ tlsClient.notifySecureRenegotiation(this.secure_renegotiation);
+
+ if (clientExtensions != null)
+ {
+ tlsClient.processServerExtensions(serverExtensions);
+ }
+ }
+
+ protected void sendCertificateVerifyMessage(byte[] data)
+ throws IOException
+ {
+ /*
+ * Send signature of handshake messages so far to prove we are the owner of the cert See RFC
+ * 2246 sections 4.7, 7.4.3 and 7.4.8
+ */
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ TlsUtils.writeUint8(HandshakeType.certificate_verify, bos);
+ TlsUtils.writeUint24(data.length + 2, bos);
+ TlsUtils.writeOpaque16(data, bos);
+ byte[] message = bos.toByteArray();
+
+ safeWriteRecord(ContentType.handshake, message, 0, message.length);
+ }
+
+ protected void sendClientHelloMessage()
+ throws IOException
+ {
+
+ recordStream.setWriteVersion(this.tlsClient.getClientHelloRecordLayerVersion());
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ TlsUtils.writeUint8(HandshakeType.client_hello, buf);
+
+ // Reserve space for length
+ TlsUtils.writeUint24(0, buf);
+
+ ProtocolVersion client_version = this.tlsClient.getClientVersion();
+ if (client_version.isDTLS())
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+ }
+
+ getContext().setClientVersion(client_version);
+ TlsUtils.writeVersion(client_version, buf);
+
+ buf.write(securityParameters.clientRandom);
+
+ // Session id
+ TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf);
+
+ /*
+ * Cipher suites
+ */
+ this.offeredCipherSuites = this.tlsClient.getCipherSuites();
+
+ // Integer -> byte[]
+ this.clientExtensions = this.tlsClient.getClientExtensions();
+
+ // Cipher Suites (and SCSV)
+ {
+ /*
+ * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension,
+ * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the
+ * ClientHello. Including both is NOT RECOMMENDED.
+ */
+ boolean noRenegExt = clientExtensions == null || clientExtensions.get(EXT_RenegotiationInfo) == null;
+
+ int count = offeredCipherSuites.length;
+ if (noRenegExt)
+ {
+ // Note: 1 extra slot for TLS_EMPTY_RENEGOTIATION_INFO_SCSV
+ ++count;
+ }
+
+ TlsUtils.writeUint16(2 * count, buf);
+ TlsUtils.writeUint16Array(offeredCipherSuites, buf);
+
+ if (noRenegExt)
+ {
+ TlsUtils.writeUint16(CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV, buf);
+ }
+ }
+
+ // Compression methods
+ this.offeredCompressionMethods = this.tlsClient.getCompressionMethods();
+
+ TlsUtils.writeUint8((short)offeredCompressionMethods.length, buf);
+ TlsUtils.writeUint8Array(offeredCompressionMethods, buf);
+
+ // Extensions
+ if (clientExtensions != null)
+ {
+ writeExtensions(buf, clientExtensions);
+ }
+
+ byte[] message = buf.toByteArray();
+
+ // Patch actual length back in
+ TlsUtils.writeUint24(message.length - 4, message, 1);
+
+ safeWriteRecord(ContentType.handshake, message, 0, message.length);
+ }
+
+ protected void sendClientKeyExchangeMessage()
+ throws IOException
+ {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+ TlsUtils.writeUint8(HandshakeType.client_key_exchange, bos);
+
+ // Reserve space for length
+ TlsUtils.writeUint24(0, bos);
+
+ this.keyExchange.generateClientKeyExchange(bos);
+ byte[] message = bos.toByteArray();
+
+ // Patch actual length back in
+ TlsUtils.writeUint24(message.length - 4, message, 1);
+
+ safeWriteRecord(ContentType.handshake, message, 0, message.length);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsCompression.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsCompression.java
new file mode 100644
index 00000000..cdeb7e33
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsCompression.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.OutputStream;
+
+public interface TlsCompression
+{
+ OutputStream compress(OutputStream output);
+
+ OutputStream decompress(OutputStream output);
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsContext.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsContext.java
new file mode 100644
index 00000000..dfb10524
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsContext.java
@@ -0,0 +1,32 @@
+package org.bouncycastle.crypto.tls;
+
+import java.security.SecureRandom;
+
+public interface TlsContext
+{
+
+ SecureRandom getSecureRandom();
+
+ SecurityParameters getSecurityParameters();
+
+ boolean isServer();
+
+ ProtocolVersion getClientVersion();
+
+ ProtocolVersion getServerVersion();
+
+ Object getUserObject();
+
+ void setUserObject(Object userObject);
+
+ /**
+ * Export keying material according to RFC 5705: "Keying Material Exporters for TLS".
+ *
+ * @param asciiLabel indicates which application will use the exported keys.
+ * @param context_value allows the application using the exporter to mix its own data with the TLS PRF for
+ * the exporter output.
+ * @param length the number of bytes to generate
+ * @return a pseudorandom bit string of 'length' bytes generated from the master_secret.
+ */
+ byte[] exportKeyingMaterial(String asciiLabel, byte[] context_value, int length);
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsCredentials.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsCredentials.java
new file mode 100644
index 00000000..b8a8747e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsCredentials.java
@@ -0,0 +1,6 @@
+package org.bouncycastle.crypto.tls;
+
+public interface TlsCredentials
+{
+ Certificate getCertificate();
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java
new file mode 100644
index 00000000..57376592
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java
@@ -0,0 +1,113 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.util.Vector;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.generators.DHKeyPairGenerator;
+import org.bouncycastle.crypto.io.SignerInputStream;
+import org.bouncycastle.crypto.params.DHKeyGenerationParameters;
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+
+public class TlsDHEKeyExchange
+ extends TlsDHKeyExchange
+{
+
+ protected TlsSignerCredentials serverCredentials = null;
+
+ public TlsDHEKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, DHParameters dhParameters)
+ {
+ super(keyExchange, supportedSignatureAlgorithms, dhParameters);
+ }
+
+ public void processServerCredentials(TlsCredentials serverCredentials)
+ throws IOException
+ {
+
+ if (!(serverCredentials instanceof TlsSignerCredentials))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ processServerCertificate(serverCredentials.getCertificate());
+
+ this.serverCredentials = (TlsSignerCredentials)serverCredentials;
+ }
+
+ public byte[] generateServerKeyExchange()
+ throws IOException
+ {
+
+ if (this.dhParameters == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+
+ DHKeyPairGenerator kpg = new DHKeyPairGenerator();
+ kpg.init(new DHKeyGenerationParameters(context.getSecureRandom(), this.dhParameters));
+ AsymmetricCipherKeyPair kp = kpg.generateKeyPair();
+
+ BigInteger Ys = ((DHPublicKeyParameters)kp.getPublic()).getY();
+
+ TlsDHUtils.writeDHParameter(dhParameters.getP(), buf);
+ TlsDHUtils.writeDHParameter(dhParameters.getG(), buf);
+ TlsDHUtils.writeDHParameter(Ys, buf);
+
+ byte[] digestInput = buf.toByteArray();
+
+ Digest d = new CombinedHash();
+ SecurityParameters securityParameters = context.getSecurityParameters();
+ d.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length);
+ d.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length);
+ d.update(digestInput, 0, digestInput.length);
+
+ byte[] hash = new byte[d.getDigestSize()];
+ d.doFinal(hash, 0);
+
+ byte[] sigBytes = serverCredentials.generateCertificateSignature(hash);
+ /*
+ * TODO RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm prepended from TLS 1.2
+ */
+ TlsUtils.writeOpaque16(sigBytes, buf);
+
+ return buf.toByteArray();
+ }
+
+ public void processServerKeyExchange(InputStream input)
+ throws IOException
+ {
+
+ SecurityParameters securityParameters = context.getSecurityParameters();
+
+ Signer signer = initVerifyer(tlsSigner, securityParameters);
+ InputStream sigIn = new SignerInputStream(input, signer);
+
+ BigInteger p = TlsDHUtils.readDHParameter(sigIn);
+ BigInteger g = TlsDHUtils.readDHParameter(sigIn);
+ BigInteger Ys = TlsDHUtils.readDHParameter(sigIn);
+
+ byte[] sigBytes = TlsUtils.readOpaque16(input);
+ if (!signer.verifySignature(sigBytes))
+ {
+ throw new TlsFatalAlert(AlertDescription.decrypt_error);
+ }
+
+ this.dhAgreeServerPublicKey = validateDHPublicKey(new DHPublicKeyParameters(Ys, new DHParameters(p, g)));
+ }
+
+ protected Signer initVerifyer(TlsSigner tlsSigner, SecurityParameters securityParameters)
+ {
+ Signer signer = tlsSigner.createVerifyer(this.serverPublicKey);
+ signer.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length);
+ signer.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length);
+ return signer;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java
new file mode 100644
index 00000000..60e5105c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java
@@ -0,0 +1,222 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
+import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+import org.bouncycastle.crypto.util.PublicKeyFactory;
+
+/**
+ * TLS 1.0/1.1 DH key exchange.
+ */
+public class TlsDHKeyExchange
+ extends AbstractTlsKeyExchange
+{
+
+ protected static final BigInteger ONE = BigInteger.valueOf(1);
+ protected static final BigInteger TWO = BigInteger.valueOf(2);
+
+ protected TlsSigner tlsSigner;
+ protected DHParameters dhParameters;
+
+ protected AsymmetricKeyParameter serverPublicKey;
+ protected DHPublicKeyParameters dhAgreeServerPublicKey;
+ protected TlsAgreementCredentials agreementCredentials;
+ protected DHPrivateKeyParameters dhAgreeClientPrivateKey;
+
+ protected DHPublicKeyParameters dhAgreeClientPublicKey;
+
+ public TlsDHKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, DHParameters dhParameters)
+ {
+
+ super(keyExchange, supportedSignatureAlgorithms);
+
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.DH_RSA:
+ case KeyExchangeAlgorithm.DH_DSS:
+ this.tlsSigner = null;
+ break;
+ case KeyExchangeAlgorithm.DHE_RSA:
+ this.tlsSigner = new TlsRSASigner();
+ break;
+ case KeyExchangeAlgorithm.DHE_DSS:
+ this.tlsSigner = new TlsDSSSigner();
+ break;
+ default:
+ throw new IllegalArgumentException("unsupported key exchange algorithm");
+ }
+
+ this.dhParameters = dhParameters;
+ }
+
+ public void init(TlsContext context)
+ {
+ super.init(context);
+
+ if (this.tlsSigner != null)
+ {
+ this.tlsSigner.init(context);
+ }
+ }
+
+ public void skipServerCredentials()
+ throws IOException
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public void processServerCertificate(Certificate serverCertificate)
+ throws IOException
+ {
+
+ if (serverCertificate.isEmpty())
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+ }
+
+ org.bouncycastle.asn1.x509.Certificate x509Cert = serverCertificate.getCertificateAt(0);
+
+ SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo();
+ try
+ {
+ this.serverPublicKey = PublicKeyFactory.createKey(keyInfo);
+ }
+ catch (RuntimeException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ }
+
+ if (tlsSigner == null)
+ {
+ try
+ {
+ this.dhAgreeServerPublicKey = validateDHPublicKey((DHPublicKeyParameters)this.serverPublicKey);
+ }
+ catch (ClassCastException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ }
+
+ TlsUtils.validateKeyUsage(x509Cert, KeyUsage.keyAgreement);
+ }
+ else
+ {
+ if (!tlsSigner.isValidPublicKey(this.serverPublicKey))
+ {
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ }
+
+ TlsUtils.validateKeyUsage(x509Cert, KeyUsage.digitalSignature);
+ }
+
+ super.processServerCertificate(serverCertificate);
+ }
+
+ public boolean requiresServerKeyExchange()
+ {
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.DHE_DSS:
+ case KeyExchangeAlgorithm.DHE_RSA:
+ case KeyExchangeAlgorithm.DH_anon:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public void validateCertificateRequest(CertificateRequest certificateRequest)
+ throws IOException
+ {
+ short[] types = certificateRequest.getCertificateTypes();
+ for (int i = 0; i < types.length; ++i)
+ {
+ switch (types[i])
+ {
+ case ClientCertificateType.rsa_sign:
+ case ClientCertificateType.dss_sign:
+ case ClientCertificateType.rsa_fixed_dh:
+ case ClientCertificateType.dss_fixed_dh:
+ case ClientCertificateType.ecdsa_sign:
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+ }
+
+ public void processClientCredentials(TlsCredentials clientCredentials)
+ throws IOException
+ {
+ if (clientCredentials instanceof TlsAgreementCredentials)
+ {
+ // TODO Validate client cert has matching parameters (see 'areCompatibleParameters')?
+
+ this.agreementCredentials = (TlsAgreementCredentials)clientCredentials;
+ }
+ else if (clientCredentials instanceof TlsSignerCredentials)
+ {
+ // OK
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public void generateClientKeyExchange(OutputStream output)
+ throws IOException
+ {
+ /*
+ * RFC 2246 7.4.7.2 If the client certificate already contains a suitable Diffie-Hellman
+ * key, then Yc is implicit and does not need to be sent again. In this case, the Client Key
+ * Exchange message will be sent, but will be empty.
+ */
+ if (agreementCredentials == null)
+ {
+ this.dhAgreeClientPrivateKey = TlsDHUtils.generateEphemeralClientKeyExchange(context.getSecureRandom(),
+ dhAgreeServerPublicKey.getParameters(), output);
+ }
+ }
+
+ public byte[] generatePremasterSecret()
+ throws IOException
+ {
+ if (agreementCredentials != null)
+ {
+ return agreementCredentials.generateAgreement(dhAgreeServerPublicKey);
+ }
+
+ return calculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey);
+ }
+
+ protected boolean areCompatibleParameters(DHParameters a, DHParameters b)
+ {
+ return a.getP().equals(b.getP()) && a.getG().equals(b.getG());
+ }
+
+ protected byte[] calculateDHBasicAgreement(DHPublicKeyParameters publicKey, DHPrivateKeyParameters privateKey)
+ {
+ return TlsDHUtils.calculateDHBasicAgreement(publicKey, privateKey);
+ }
+
+ protected AsymmetricCipherKeyPair generateDHKeyPair(DHParameters dhParams)
+ {
+ return TlsDHUtils.generateDHKeyPair(context.getSecureRandom(), dhParams);
+ }
+
+ protected DHPublicKeyParameters validateDHPublicKey(DHPublicKeyParameters key)
+ throws IOException
+ {
+ return TlsDHUtils.validateDHPublicKey(key);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java
new file mode 100644
index 00000000..014e40f0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java
@@ -0,0 +1,100 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.agreement.DHBasicAgreement;
+import org.bouncycastle.crypto.generators.DHBasicKeyPairGenerator;
+import org.bouncycastle.crypto.params.DHKeyGenerationParameters;
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
+import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+import org.bouncycastle.util.BigIntegers;
+
+public class TlsDHUtils
+{
+
+ static final BigInteger ONE = BigInteger.valueOf(1);
+ static final BigInteger TWO = BigInteger.valueOf(2);
+
+ public static byte[] calculateDHBasicAgreement(DHPublicKeyParameters publicKey,
+ DHPrivateKeyParameters privateKey)
+ {
+
+ DHBasicAgreement basicAgreement = new DHBasicAgreement();
+ basicAgreement.init(privateKey);
+ BigInteger agreementValue = basicAgreement.calculateAgreement(publicKey);
+
+ /*
+ * RFC 5246 8.1.2. Leading bytes of Z that contain all zero bits are stripped before it is
+ * used as the pre_master_secret.
+ */
+ return BigIntegers.asUnsignedByteArray(agreementValue);
+ }
+
+ public static AsymmetricCipherKeyPair generateDHKeyPair(SecureRandom random,
+ DHParameters dhParams)
+ {
+ DHBasicKeyPairGenerator dhGen = new DHBasicKeyPairGenerator();
+ dhGen.init(new DHKeyGenerationParameters(random, dhParams));
+ return dhGen.generateKeyPair();
+ }
+
+ public static DHPrivateKeyParameters generateEphemeralClientKeyExchange(SecureRandom random,
+ DHParameters dhParams, OutputStream output)
+ throws IOException
+ {
+
+ AsymmetricCipherKeyPair dhAgreeClientKeyPair = generateDHKeyPair(random, dhParams);
+ DHPrivateKeyParameters dhAgreeClientPrivateKey = (DHPrivateKeyParameters)dhAgreeClientKeyPair
+ .getPrivate();
+
+ BigInteger Yc = ((DHPublicKeyParameters)dhAgreeClientKeyPair.getPublic()).getY();
+ byte[] keData = BigIntegers.asUnsignedByteArray(Yc);
+ TlsUtils.writeOpaque16(keData, output);
+
+ return dhAgreeClientPrivateKey;
+ }
+
+ public static DHPublicKeyParameters validateDHPublicKey(DHPublicKeyParameters key)
+ throws IOException
+ {
+ BigInteger Y = key.getY();
+ DHParameters params = key.getParameters();
+ BigInteger p = params.getP();
+ BigInteger g = params.getG();
+
+ if (!p.isProbablePrime(2))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ if (g.compareTo(TWO) < 0 || g.compareTo(p.subtract(TWO)) > 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ if (Y.compareTo(TWO) < 0 || Y.compareTo(p.subtract(ONE)) > 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ // TODO See RFC 2631 for more discussion of Diffie-Hellman validation
+
+ return key;
+ }
+
+ public static BigInteger readDHParameter(InputStream input)
+ throws IOException
+ {
+ return new BigInteger(1, TlsUtils.readOpaque16(input));
+ }
+
+ public static void writeDHParameter(BigInteger x, OutputStream output)
+ throws IOException
+ {
+ TlsUtils.writeOpaque16(BigIntegers.asUnsignedByteArray(x), output);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsDSASigner.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsDSASigner.java
new file mode 100644
index 00000000..b0e89572
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsDSASigner.java
@@ -0,0 +1,57 @@
+package org.bouncycastle.crypto.tls;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.digests.NullDigest;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.signers.DSADigestSigner;
+
+public abstract class TlsDSASigner
+ extends AbstractTlsSigner
+{
+
+ public byte[] generateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1)
+ throws CryptoException
+ {
+
+ // Note: Only use the SHA1 part of the hash
+ Signer signer = makeSigner(new NullDigest(), true,
+ new ParametersWithRandom(privateKey, this.context.getSecureRandom()));
+ signer.update(md5AndSha1, 16, 20);
+ return signer.generateSignature();
+ }
+
+ public boolean verifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1)
+ throws CryptoException
+ {
+
+ // Note: Only use the SHA1 part of the hash
+ Signer signer = makeSigner(new NullDigest(), false, publicKey);
+ signer.update(md5AndSha1, 16, 20);
+ return signer.verifySignature(sigBytes);
+ }
+
+ public Signer createSigner(AsymmetricKeyParameter privateKey)
+ {
+ return makeSigner(new SHA1Digest(), true, new ParametersWithRandom(privateKey, this.context.getSecureRandom()));
+ }
+
+ public Signer createVerifyer(AsymmetricKeyParameter publicKey)
+ {
+ return makeSigner(new SHA1Digest(), false, publicKey);
+ }
+
+ protected Signer makeSigner(Digest d, boolean forSigning, CipherParameters cp)
+ {
+ Signer s = new DSADigestSigner(createDSAImpl(), d);
+ s.init(forSigning, cp);
+ return s;
+ }
+
+ protected abstract DSA createDSAImpl();
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsDSSSigner.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsDSSSigner.java
new file mode 100644
index 00000000..e0eeca98
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsDSSSigner.java
@@ -0,0 +1,21 @@
+package org.bouncycastle.crypto.tls;
+
+import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
+import org.bouncycastle.crypto.signers.DSASigner;
+
+public class TlsDSSSigner
+ extends TlsDSASigner
+{
+
+ public boolean isValidPublicKey(AsymmetricKeyParameter publicKey)
+ {
+ return publicKey instanceof DSAPublicKeyParameters;
+ }
+
+ protected DSA createDSAImpl()
+ {
+ return new DSASigner();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsECCUtils.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsECCUtils.java
new file mode 100644
index 00000000..a49f83ff
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsECCUtils.java
@@ -0,0 +1,619 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.Hashtable;
+
+import org.bouncycastle.asn1.sec.SECNamedCurves;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.agreement.ECDHBasicAgreement;
+import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.BigIntegers;
+import org.bouncycastle.util.Integers;
+
+public class TlsECCUtils
+{
+
+ public static final Integer EXT_elliptic_curves = Integers.valueOf(ExtensionType.elliptic_curves);
+ public static final Integer EXT_ec_point_formats = Integers.valueOf(ExtensionType.ec_point_formats);
+
+ private static final String[] curveNames = new String[]{"sect163k1", "sect163r1", "sect163r2", "sect193r1",
+ "sect193r2", "sect233k1", "sect233r1", "sect239k1", "sect283k1", "sect283r1", "sect409k1", "sect409r1",
+ "sect571k1", "sect571r1", "secp160k1", "secp160r1", "secp160r2", "secp192k1", "secp192r1", "secp224k1",
+ "secp224r1", "secp256k1", "secp256r1", "secp384r1", "secp521r1",};
+
+ public static void addSupportedEllipticCurvesExtension(Hashtable extensions, int[] namedCurves)
+ throws IOException
+ {
+
+ extensions.put(EXT_elliptic_curves, createSupportedEllipticCurvesExtension(namedCurves));
+ }
+
+ public static void addSupportedPointFormatsExtension(Hashtable extensions, short[] ecPointFormats)
+ throws IOException
+ {
+
+ extensions.put(EXT_ec_point_formats, createSupportedPointFormatsExtension(ecPointFormats));
+ }
+
+ public static int[] getSupportedEllipticCurvesExtension(Hashtable extensions)
+ throws IOException
+ {
+
+ if (extensions == null)
+ {
+ return null;
+ }
+ byte[] extensionValue = (byte[])extensions.get(EXT_elliptic_curves);
+ if (extensionValue == null)
+ {
+ return null;
+ }
+ return readSupportedEllipticCurvesExtension(extensionValue);
+ }
+
+ public static short[] getSupportedPointFormatsExtension(Hashtable extensions)
+ throws IOException
+ {
+
+ if (extensions == null)
+ {
+ return null;
+ }
+ byte[] extensionValue = (byte[])extensions.get(EXT_ec_point_formats);
+ if (extensionValue == null)
+ {
+ return null;
+ }
+ return readSupportedPointFormatsExtension(extensionValue);
+ }
+
+ public static byte[] createSupportedEllipticCurvesExtension(int[] namedCurves)
+ throws IOException
+ {
+
+ if (namedCurves == null || namedCurves.length < 1)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ TlsUtils.writeUint16(2 * namedCurves.length, buf);
+ TlsUtils.writeUint16Array(namedCurves, buf);
+ return buf.toByteArray();
+ }
+
+ public static byte[] createSupportedPointFormatsExtension(short[] ecPointFormats)
+ throws IOException
+ {
+
+ if (ecPointFormats == null)
+ {
+ ecPointFormats = new short[]{ECPointFormat.uncompressed};
+ }
+ else if (!TlsProtocol.arrayContains(ecPointFormats, ECPointFormat.uncompressed))
+ {
+ /*
+ * RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST
+ * contain the value 0 (uncompressed) as one of the items in the list of point formats.
+ */
+
+ // NOTE: We add it at the end (lowest preference)
+ short[] tmp = new short[ecPointFormats.length + 1];
+ System.arraycopy(ecPointFormats, 0, tmp, 0, ecPointFormats.length);
+ tmp[ecPointFormats.length] = ECPointFormat.uncompressed;
+
+ ecPointFormats = tmp;
+ }
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ TlsUtils.writeUint8((short)ecPointFormats.length, buf);
+ TlsUtils.writeUint8Array(ecPointFormats, buf);
+ return buf.toByteArray();
+ }
+
+ public static int[] readSupportedEllipticCurvesExtension(byte[] extensionValue)
+ throws IOException
+ {
+
+ if (extensionValue == null)
+ {
+ throw new IllegalArgumentException("'extensionValue' cannot be null");
+ }
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(extensionValue);
+
+ int length = TlsUtils.readUint16(buf);
+ if (length < 2 || (length & 1) != 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ int[] namedCurves = TlsUtils.readUint16Array(length / 2, buf);
+
+ TlsProtocol.assertEmpty(buf);
+
+ return namedCurves;
+ }
+
+ public static short[] readSupportedPointFormatsExtension(byte[] extensionValue)
+ throws IOException
+ {
+
+ if (extensionValue == null)
+ {
+ throw new IllegalArgumentException("'extensionValue' cannot be null");
+ }
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(extensionValue);
+
+ short length = TlsUtils.readUint8(buf);
+ if (length < 1)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ short[] ecPointFormats = TlsUtils.readUint8Array(length, buf);
+
+ TlsProtocol.assertEmpty(buf);
+
+ if (!TlsProtocol.arrayContains(ecPointFormats, ECPointFormat.uncompressed))
+ {
+ /*
+ * RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST
+ * contain the value 0 (uncompressed) as one of the items in the list of point formats.
+ */
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ return ecPointFormats;
+ }
+
+ public static String getNameOfNamedCurve(int namedCurve)
+ {
+ return isSupportedNamedCurve(namedCurve) ? curveNames[namedCurve - 1] : null;
+ }
+
+ public static ECDomainParameters getParametersForNamedCurve(int namedCurve)
+ {
+ String curveName = getNameOfNamedCurve(namedCurve);
+ if (curveName == null)
+ {
+ return null;
+ }
+
+ // Lazily created the first time a particular curve is accessed
+ X9ECParameters ecP = SECNamedCurves.getByName(curveName);
+
+ if (ecP == null)
+ {
+ return null;
+ }
+
+ // It's a bit inefficient to do this conversion every time
+ return new ECDomainParameters(ecP.getCurve(), ecP.getG(), ecP.getN(), ecP.getH(), ecP.getSeed());
+ }
+
+ public static boolean hasAnySupportedNamedCurves()
+ {
+ return curveNames.length > 0;
+ }
+
+ public static boolean containsECCCipherSuites(int[] cipherSuites)
+ {
+ for (int i = 0; i < cipherSuites.length; ++i)
+ {
+ if (isECCCipherSuite(cipherSuites[i]))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean isECCCipherSuite(int cipherSuite)
+ {
+ switch (cipherSuite)
+ {
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_anon_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDH_anon_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_anon_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_anon_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public static boolean areOnSameCurve(ECDomainParameters a, ECDomainParameters b)
+ {
+ // TODO Move to ECDomainParameters.equals() or other utility method?
+ return a.getCurve().equals(b.getCurve()) && a.getG().equals(b.getG()) && a.getN().equals(b.getN())
+ && a.getH().equals(b.getH());
+ }
+
+ public static boolean isSupportedNamedCurve(int namedCurve)
+ {
+ return (namedCurve > 0 && namedCurve <= curveNames.length);
+ }
+
+ public static boolean isCompressionPreferred(short[] ecPointFormats, short compressionFormat)
+ {
+ if (ecPointFormats == null)
+ {
+ return false;
+ }
+ for (int i = 0; i < ecPointFormats.length; ++i)
+ {
+ short ecPointFormat = ecPointFormats[i];
+ if (ecPointFormat == ECPointFormat.uncompressed)
+ {
+ return false;
+ }
+ if (ecPointFormat == compressionFormat)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static byte[] serializeECFieldElement(int fieldSize, BigInteger x)
+ throws IOException
+ {
+ int requiredLength = (fieldSize + 7) / 8;
+ return BigIntegers.asUnsignedByteArray(requiredLength, x);
+ }
+
+ public static byte[] serializeECPoint(short[] ecPointFormats, ECPoint point)
+ throws IOException
+ {
+
+ ECCurve curve = point.getCurve();
+
+ /*
+ * RFC 4492 5.7. ...an elliptic curve point in uncompressed or compressed format. Here, the
+ * format MUST conform to what the server has requested through a Supported Point Formats
+ * Extension if this extension was used, and MUST be uncompressed if this extension was not
+ * used.
+ */
+ boolean compressed = false;
+ if (curve instanceof ECCurve.F2m)
+ {
+ compressed = isCompressionPreferred(ecPointFormats, ECPointFormat.ansiX962_compressed_char2);
+ }
+ else if (curve instanceof ECCurve.Fp)
+ {
+ compressed = isCompressionPreferred(ecPointFormats, ECPointFormat.ansiX962_compressed_prime);
+ }
+ return point.getEncoded(compressed);
+ }
+
+ public static byte[] serializeECPublicKey(short[] ecPointFormats, ECPublicKeyParameters keyParameters)
+ throws IOException
+ {
+
+ return serializeECPoint(ecPointFormats, keyParameters.getQ());
+ }
+
+ public static BigInteger deserializeECFieldElement(int fieldSize, byte[] encoding)
+ throws IOException
+ {
+ int requiredLength = (fieldSize + 7) / 8;
+ if (encoding.length != requiredLength)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+ return new BigInteger(1, encoding);
+ }
+
+ public static ECPoint deserializeECPoint(short[] ecPointFormats, ECCurve curve, byte[] encoding)
+ throws IOException
+ {
+ /*
+ * NOTE: Here we implicitly decode compressed or uncompressed encodings. DefaultTlsClient by
+ * default is set up to advertise that we can parse any encoding so this works fine, but
+ * extra checks might be needed here if that were changed.
+ */
+ return curve.decodePoint(encoding);
+ }
+
+ public static ECPublicKeyParameters deserializeECPublicKey(short[] ecPointFormats, ECDomainParameters curve_params,
+ byte[] encoding)
+ throws IOException
+ {
+
+ try
+ {
+ ECPoint Y = deserializeECPoint(ecPointFormats, curve_params.getCurve(), encoding);
+ return new ECPublicKeyParameters(Y, curve_params);
+ }
+ catch (RuntimeException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+
+ public static byte[] calculateECDHBasicAgreement(ECPublicKeyParameters publicKey, ECPrivateKeyParameters privateKey)
+ {
+
+ ECDHBasicAgreement basicAgreement = new ECDHBasicAgreement();
+ basicAgreement.init(privateKey);
+ BigInteger agreementValue = basicAgreement.calculateAgreement(publicKey);
+
+ /*
+ * RFC 4492 5.10. Note that this octet string (Z in IEEE 1363 terminology) as output by
+ * FE2OSP, the Field Element to Octet String Conversion Primitive, has constant length for
+ * any given field; leading zeros found in this octet string MUST NOT be truncated.
+ */
+ return BigIntegers.asUnsignedByteArray(basicAgreement.getFieldSize(), agreementValue);
+ }
+
+ public static AsymmetricCipherKeyPair generateECKeyPair(SecureRandom random, ECDomainParameters ecParams)
+ {
+
+ ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
+ ECKeyGenerationParameters keyGenerationParameters = new ECKeyGenerationParameters(ecParams, random);
+ keyPairGenerator.init(keyGenerationParameters);
+ return keyPairGenerator.generateKeyPair();
+ }
+
+ public static ECPublicKeyParameters validateECPublicKey(ECPublicKeyParameters key)
+ throws IOException
+ {
+ // TODO Check RFC 4492 for validation
+ return key;
+ }
+
+ public static int readECExponent(int fieldSize, InputStream input)
+ throws IOException
+ {
+ BigInteger K = readECParameter(input);
+ if (K.bitLength() < 32)
+ {
+ int k = K.intValue();
+ if (k > 0 && k < fieldSize)
+ {
+ return k;
+ }
+ }
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ public static BigInteger readECFieldElement(int fieldSize, InputStream input)
+ throws IOException
+ {
+ return deserializeECFieldElement(fieldSize, TlsUtils.readOpaque8(input));
+ }
+
+ public static BigInteger readECParameter(InputStream input)
+ throws IOException
+ {
+ // TODO Are leading zeroes okay here?
+ return new BigInteger(1, TlsUtils.readOpaque8(input));
+ }
+
+ public static ECDomainParameters readECParameters(int[] namedCurves, short[] ecPointFormats, InputStream input)
+ throws IOException
+ {
+
+ try
+ {
+ short curveType = TlsUtils.readUint8(input);
+
+ switch (curveType)
+ {
+ case ECCurveType.explicit_prime:
+ {
+ BigInteger prime_p = readECParameter(input);
+ BigInteger a = readECFieldElement(prime_p.bitLength(), input);
+ BigInteger b = readECFieldElement(prime_p.bitLength(), input);
+ ECCurve curve = new ECCurve.Fp(prime_p, a, b);
+ ECPoint base = deserializeECPoint(ecPointFormats, curve, TlsUtils.readOpaque8(input));
+ BigInteger order = readECParameter(input);
+ BigInteger cofactor = readECParameter(input);
+ return new ECDomainParameters(curve, base, order, cofactor);
+ }
+ case ECCurveType.explicit_char2:
+ {
+ int m = TlsUtils.readUint16(input);
+ short basis = TlsUtils.readUint8(input);
+ ECCurve curve;
+ switch (basis)
+ {
+ case ECBasisType.ec_basis_trinomial:
+ {
+ int k = readECExponent(m, input);
+ BigInteger a = readECFieldElement(m, input);
+ BigInteger b = readECFieldElement(m, input);
+ curve = new ECCurve.F2m(m, k, a, b);
+ break;
+ }
+ case ECBasisType.ec_basis_pentanomial:
+ {
+ int k1 = readECExponent(m, input);
+ int k2 = readECExponent(m, input);
+ int k3 = readECExponent(m, input);
+ BigInteger a = readECFieldElement(m, input);
+ BigInteger b = readECFieldElement(m, input);
+ curve = new ECCurve.F2m(m, k1, k2, k3, a, b);
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ ECPoint base = deserializeECPoint(ecPointFormats, curve, TlsUtils.readOpaque8(input));
+ BigInteger order = readECParameter(input);
+ BigInteger cofactor = readECParameter(input);
+ return new ECDomainParameters(curve, base, order, cofactor);
+ }
+ case ECCurveType.named_curve:
+ {
+ int namedCurve = TlsUtils.readUint16(input);
+ if (!NamedCurve.refersToASpecificNamedCurve(namedCurve))
+ {
+ /*
+ * RFC 4492 5.4. All those values of NamedCurve are allowed that refer to a
+ * specific curve. Values of NamedCurve that indicate support for a class of
+ * explicitly defined curves are not allowed here [...].
+ */
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ if (!TlsProtocol.arrayContains(namedCurves, namedCurve))
+ {
+ /*
+ * RFC 4492 4. [...] servers MUST NOT negotiate the use of an ECC cipher suite
+ * unless they can complete the handshake while respecting the choice of curves
+ * and compression techniques specified by the client.
+ */
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ return TlsECCUtils.getParametersForNamedCurve(namedCurve);
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+ catch (RuntimeException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+
+ public static void writeECExponent(int k, OutputStream output)
+ throws IOException
+ {
+ BigInteger K = BigInteger.valueOf(k);
+ writeECParameter(K, output);
+ }
+
+ public static void writeECFieldElement(int fieldSize, BigInteger x, OutputStream output)
+ throws IOException
+ {
+ TlsUtils.writeOpaque8(serializeECFieldElement(fieldSize, x), output);
+ }
+
+ public static void writeECParameter(BigInteger x, OutputStream output)
+ throws IOException
+ {
+ TlsUtils.writeOpaque8(BigIntegers.asUnsignedByteArray(x), output);
+ }
+
+ public static void writeExplicitECParameters(short[] ecPointFormats, ECDomainParameters ecParameters,
+ OutputStream output)
+ throws IOException
+ {
+
+ ECCurve curve = ecParameters.getCurve();
+ if (curve instanceof ECCurve.Fp)
+ {
+
+ TlsUtils.writeUint8(ECCurveType.explicit_prime, output);
+
+ ECCurve.Fp fp = (ECCurve.Fp)curve;
+ writeECParameter(fp.getQ(), output);
+
+ }
+ else if (curve instanceof ECCurve.F2m)
+ {
+
+ TlsUtils.writeUint8(ECCurveType.explicit_char2, output);
+
+ ECCurve.F2m f2m = (ECCurve.F2m)curve;
+ TlsUtils.writeUint16(f2m.getM(), output);
+
+ if (f2m.isTrinomial())
+ {
+ TlsUtils.writeUint8(ECBasisType.ec_basis_trinomial, output);
+ writeECExponent(f2m.getK1(), output);
+ }
+ else
+ {
+ TlsUtils.writeUint8(ECBasisType.ec_basis_pentanomial, output);
+ writeECExponent(f2m.getK1(), output);
+ writeECExponent(f2m.getK2(), output);
+ writeECExponent(f2m.getK3(), output);
+ }
+
+ }
+ else
+ {
+ throw new IllegalArgumentException("'ecParameters' not a known curve type");
+ }
+
+ writeECFieldElement(curve.getFieldSize(), curve.getA().toBigInteger(), output);
+ writeECFieldElement(curve.getFieldSize(), curve.getB().toBigInteger(), output);
+ TlsUtils.writeOpaque8(serializeECPoint(ecPointFormats, ecParameters.getG()), output);
+ writeECParameter(ecParameters.getN(), output);
+ writeECParameter(ecParameters.getH(), output);
+ }
+
+ public static void writeNamedECParameters(int namedCurve, OutputStream output)
+ throws IOException
+ {
+
+ if (!NamedCurve.refersToASpecificNamedCurve(namedCurve))
+ {
+ /*
+ * RFC 4492 5.4. All those values of NamedCurve are allowed that refer to a specific
+ * curve. Values of NamedCurve that indicate support for a class of explicitly defined
+ * curves are not allowed here [...].
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ TlsUtils.writeUint8(ECCurveType.named_curve, output);
+ TlsUtils.writeUint16(namedCurve, output);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsECDHEKeyExchange.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsECDHEKeyExchange.java
new file mode 100644
index 00000000..1124560b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsECDHEKeyExchange.java
@@ -0,0 +1,206 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Vector;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.io.SignerInputStream;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+
+/**
+ * ECDHE key exchange (see RFC 4492)
+ */
+public class TlsECDHEKeyExchange
+ extends TlsECDHKeyExchange
+{
+
+ protected TlsSignerCredentials serverCredentials = null;
+
+ public TlsECDHEKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, int[] namedCurves,
+ short[] clientECPointFormats, short[] serverECPointFormats)
+ {
+ super(keyExchange, supportedSignatureAlgorithms, namedCurves, clientECPointFormats, serverECPointFormats);
+ }
+
+ public void processServerCredentials(TlsCredentials serverCredentials)
+ throws IOException
+ {
+
+ if (!(serverCredentials instanceof TlsSignerCredentials))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ processServerCertificate(serverCredentials.getCertificate());
+
+ this.serverCredentials = (TlsSignerCredentials)serverCredentials;
+ }
+
+ public byte[] generateServerKeyExchange()
+ throws IOException
+ {
+
+ /*
+ * First we try to find a supported named curve from the client's list.
+ */
+ int namedCurve = -1;
+ if (namedCurves == null)
+ {
+ namedCurve = NamedCurve.secp256r1;
+ }
+ else
+ {
+ for (int i = 0; i < namedCurves.length; ++i)
+ {
+ int entry = namedCurves[i];
+ if (TlsECCUtils.isSupportedNamedCurve(entry))
+ {
+ namedCurve = entry;
+ break;
+ }
+ }
+ }
+
+ ECDomainParameters curve_params = null;
+ if (namedCurve >= 0)
+ {
+ curve_params = TlsECCUtils.getParametersForNamedCurve(namedCurve);
+ }
+ else
+ {
+ /*
+ * If no named curves are suitable, check if the client supports explicit curves.
+ */
+ if (TlsProtocol.arrayContains(namedCurves, NamedCurve.arbitrary_explicit_prime_curves))
+ {
+ curve_params = TlsECCUtils.getParametersForNamedCurve(NamedCurve.secp256r1);
+ }
+ else if (TlsProtocol.arrayContains(namedCurves, NamedCurve.arbitrary_explicit_char2_curves))
+ {
+ curve_params = TlsECCUtils.getParametersForNamedCurve(NamedCurve.sect233r1);
+ }
+ }
+
+ if (curve_params == null)
+ {
+ /*
+ * NOTE: We shouldn't have negotiated ECDHE key exchange since we apparently can't find
+ * a suitable curve.
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ AsymmetricCipherKeyPair kp = TlsECCUtils.generateECKeyPair(context.getSecureRandom(), curve_params);
+ this.ecAgreeServerPrivateKey = (ECPrivateKeyParameters)kp.getPrivate();
+
+ byte[] publicBytes = TlsECCUtils.serializeECPublicKey(clientECPointFormats,
+ (ECPublicKeyParameters)kp.getPublic());
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+
+ if (namedCurve < 0)
+ {
+ TlsECCUtils.writeExplicitECParameters(clientECPointFormats, curve_params, buf);
+ }
+ else
+ {
+ TlsECCUtils.writeNamedECParameters(namedCurve, buf);
+ }
+
+ TlsUtils.writeOpaque8(publicBytes, buf);
+
+ byte[] digestInput = buf.toByteArray();
+
+ Digest d = new CombinedHash();
+ SecurityParameters securityParameters = context.getSecurityParameters();
+ d.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length);
+ d.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length);
+ d.update(digestInput, 0, digestInput.length);
+
+ byte[] hash = new byte[d.getDigestSize()];
+ d.doFinal(hash, 0);
+
+ byte[] sigBytes = serverCredentials.generateCertificateSignature(hash);
+ /*
+ * TODO RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm prepended
+ * from TLS 1.2
+ */
+ TlsUtils.writeOpaque16(sigBytes, buf);
+
+ return buf.toByteArray();
+ }
+
+ public void processServerKeyExchange(InputStream input)
+ throws IOException
+ {
+
+ SecurityParameters securityParameters = context.getSecurityParameters();
+
+ Signer signer = initVerifyer(tlsSigner, securityParameters);
+ InputStream sigIn = new SignerInputStream(input, signer);
+
+ ECDomainParameters curve_params = TlsECCUtils.readECParameters(namedCurves, clientECPointFormats, sigIn);
+
+ byte[] point = TlsUtils.readOpaque8(sigIn);
+
+ byte[] sigByte = TlsUtils.readOpaque16(input);
+ if (!signer.verifySignature(sigByte))
+ {
+ throw new TlsFatalAlert(AlertDescription.decrypt_error);
+ }
+
+ this.ecAgreeServerPublicKey = TlsECCUtils.validateECPublicKey(TlsECCUtils.deserializeECPublicKey(
+ clientECPointFormats, curve_params, point));
+ }
+
+ public void validateCertificateRequest(CertificateRequest certificateRequest)
+ throws IOException
+ {
+ /*
+ * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable with
+ * ECDH_ECDSA and ECDH_RSA. Their use with ECDHE_ECDSA and ECDHE_RSA is prohibited because
+ * the use of a long-term ECDH client key would jeopardize the forward secrecy property of
+ * these algorithms.
+ */
+ short[] types = certificateRequest.getCertificateTypes();
+ for (int i = 0; i < types.length; ++i)
+ {
+ switch (types[i])
+ {
+ case ClientCertificateType.rsa_sign:
+ case ClientCertificateType.dss_sign:
+ case ClientCertificateType.ecdsa_sign:
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+ }
+
+ public void processClientCredentials(TlsCredentials clientCredentials)
+ throws IOException
+ {
+ if (clientCredentials instanceof TlsSignerCredentials)
+ {
+ // OK
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ protected Signer initVerifyer(TlsSigner tlsSigner, SecurityParameters securityParameters)
+ {
+ Signer signer = tlsSigner.createVerifyer(this.serverPublicKey);
+ signer.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length);
+ signer.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length);
+ return signer;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java
new file mode 100644
index 00000000..26c09759
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java
@@ -0,0 +1,250 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.util.PublicKeyFactory;
+
+/**
+ * ECDH key exchange (see RFC 4492)
+ */
+public class TlsECDHKeyExchange
+ extends AbstractTlsKeyExchange
+{
+
+ protected TlsSigner tlsSigner;
+ protected int[] namedCurves;
+ protected short[] clientECPointFormats, serverECPointFormats;
+
+ protected AsymmetricKeyParameter serverPublicKey;
+ protected ECPublicKeyParameters ecAgreeServerPublicKey;
+ protected TlsAgreementCredentials agreementCredentials;
+ protected ECPrivateKeyParameters ecAgreeClientPrivateKey;
+
+ protected ECPrivateKeyParameters ecAgreeServerPrivateKey;
+ protected ECPublicKeyParameters ecAgreeClientPublicKey;
+
+ public TlsECDHKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, int[] namedCurves,
+ short[] clientECPointFormats, short[] serverECPointFormats)
+ {
+
+ super(keyExchange, supportedSignatureAlgorithms);
+
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.ECDHE_RSA:
+ this.tlsSigner = new TlsRSASigner();
+ break;
+ case KeyExchangeAlgorithm.ECDHE_ECDSA:
+ this.tlsSigner = new TlsECDSASigner();
+ break;
+ case KeyExchangeAlgorithm.ECDH_RSA:
+ case KeyExchangeAlgorithm.ECDH_ECDSA:
+ this.tlsSigner = null;
+ break;
+ default:
+ throw new IllegalArgumentException("unsupported key exchange algorithm");
+ }
+
+ this.keyExchange = keyExchange;
+ this.namedCurves = namedCurves;
+ this.clientECPointFormats = clientECPointFormats;
+ this.serverECPointFormats = serverECPointFormats;
+ }
+
+ public void init(TlsContext context)
+ {
+ super.init(context);
+
+ if (this.tlsSigner != null)
+ {
+ this.tlsSigner.init(context);
+ }
+ }
+
+ public void skipServerCredentials()
+ throws IOException
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public void processServerCertificate(Certificate serverCertificate)
+ throws IOException
+ {
+
+ if (serverCertificate.isEmpty())
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+ }
+
+ org.bouncycastle.asn1.x509.Certificate x509Cert = serverCertificate.getCertificateAt(0);
+
+ SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo();
+ try
+ {
+ this.serverPublicKey = PublicKeyFactory.createKey(keyInfo);
+ }
+ catch (RuntimeException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ }
+
+ if (tlsSigner == null)
+ {
+ try
+ {
+ this.ecAgreeServerPublicKey = TlsECCUtils
+ .validateECPublicKey((ECPublicKeyParameters)this.serverPublicKey);
+ }
+ catch (ClassCastException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ }
+
+ TlsUtils.validateKeyUsage(x509Cert, KeyUsage.keyAgreement);
+ }
+ else
+ {
+ if (!tlsSigner.isValidPublicKey(this.serverPublicKey))
+ {
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ }
+
+ TlsUtils.validateKeyUsage(x509Cert, KeyUsage.digitalSignature);
+ }
+
+ super.processServerCertificate(serverCertificate);
+ }
+
+ public boolean requiresServerKeyExchange()
+ {
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.ECDHE_ECDSA:
+ case KeyExchangeAlgorithm.ECDHE_RSA:
+ case KeyExchangeAlgorithm.ECDH_anon:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public void validateCertificateRequest(CertificateRequest certificateRequest)
+ throws IOException
+ {
+ /*
+ * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable with
+ * ECDH_ECDSA and ECDH_RSA. Their use with ECDHE_ECDSA and ECDHE_RSA is prohibited because
+ * the use of a long-term ECDH client key would jeopardize the forward secrecy property of
+ * these algorithms.
+ */
+ short[] types = certificateRequest.getCertificateTypes();
+ for (int i = 0; i < types.length; ++i)
+ {
+ switch (types[i])
+ {
+ case ClientCertificateType.rsa_sign:
+ case ClientCertificateType.dss_sign:
+ case ClientCertificateType.ecdsa_sign:
+ case ClientCertificateType.rsa_fixed_ecdh:
+ case ClientCertificateType.ecdsa_fixed_ecdh:
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+ }
+
+ public void processClientCredentials(TlsCredentials clientCredentials)
+ throws IOException
+ {
+ if (clientCredentials instanceof TlsAgreementCredentials)
+ {
+ // TODO Validate client cert has matching parameters (see 'TlsECCUtils.areOnSameCurve')?
+
+ this.agreementCredentials = (TlsAgreementCredentials)clientCredentials;
+ }
+ else if (clientCredentials instanceof TlsSignerCredentials)
+ {
+ // OK
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public void generateClientKeyExchange(OutputStream output)
+ throws IOException
+ {
+ if (agreementCredentials != null)
+ {
+ return;
+ }
+
+ AsymmetricCipherKeyPair ecAgreeClientKeyPair = TlsECCUtils.generateECKeyPair(context.getSecureRandom(),
+ ecAgreeServerPublicKey.getParameters());
+ this.ecAgreeClientPrivateKey = (ECPrivateKeyParameters)ecAgreeClientKeyPair.getPrivate();
+
+ byte[] point = TlsECCUtils.serializeECPublicKey(serverECPointFormats,
+ (ECPublicKeyParameters)ecAgreeClientKeyPair.getPublic());
+
+ TlsUtils.writeOpaque8(point, output);
+ }
+
+ public void processClientCertificate(Certificate clientCertificate)
+ throws IOException
+ {
+
+ // TODO Extract the public key
+ // TODO If the certificate is 'fixed', take the public key as ecAgreeClientPublicKey
+ }
+
+ public void processClientKeyExchange(InputStream input)
+ throws IOException
+ {
+
+ if (ecAgreeClientPublicKey != null)
+ {
+ // For ecdsa_fixed_ecdh and rsa_fixed_ecdh, the key arrived in the client certificate
+ return;
+ }
+
+ byte[] point = TlsUtils.readOpaque8(input);
+
+ ECDomainParameters curve_params = this.ecAgreeServerPrivateKey.getParameters();
+
+ this.ecAgreeClientPublicKey = TlsECCUtils.validateECPublicKey(TlsECCUtils.deserializeECPublicKey(
+ serverECPointFormats, curve_params, point));
+ }
+
+ public byte[] generatePremasterSecret()
+ throws IOException
+ {
+ if (agreementCredentials != null)
+ {
+ return agreementCredentials.generateAgreement(ecAgreeServerPublicKey);
+ }
+
+ if (ecAgreeServerPrivateKey != null)
+ {
+ return TlsECCUtils.calculateECDHBasicAgreement(ecAgreeClientPublicKey, ecAgreeServerPrivateKey);
+ }
+
+ if (ecAgreeClientPrivateKey != null)
+ {
+ return TlsECCUtils.calculateECDHBasicAgreement(ecAgreeServerPublicKey, ecAgreeClientPrivateKey);
+ }
+
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsECDSASigner.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsECDSASigner.java
new file mode 100644
index 00000000..6809815c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsECDSASigner.java
@@ -0,0 +1,21 @@
+package org.bouncycastle.crypto.tls;
+
+import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.signers.ECDSASigner;
+
+public class TlsECDSASigner
+ extends TlsDSASigner
+{
+
+ public boolean isValidPublicKey(AsymmetricKeyParameter publicKey)
+ {
+ return publicKey instanceof ECPublicKeyParameters;
+ }
+
+ protected DSA createDSAImpl()
+ {
+ return new ECDSASigner();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsEncryptionCredentials.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsEncryptionCredentials.java
new file mode 100644
index 00000000..2680136d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsEncryptionCredentials.java
@@ -0,0 +1,11 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+public interface TlsEncryptionCredentials
+ extends TlsCredentials
+{
+
+ byte[] decryptPreMasterSecret(byte[] encryptedPreMasterSecret)
+ throws IOException;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsFatalAlert.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsFatalAlert.java
new file mode 100644
index 00000000..61cec318
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsFatalAlert.java
@@ -0,0 +1,21 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+public class TlsFatalAlert
+ extends IOException
+{
+ private static final long serialVersionUID = 3584313123679111168L;
+
+ private short alertDescription;
+
+ public TlsFatalAlert(short alertDescription)
+ {
+ this.alertDescription = alertDescription;
+ }
+
+ public short getAlertDescription()
+ {
+ return alertDescription;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java
new file mode 100644
index 00000000..b17b8d7c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java
@@ -0,0 +1,14 @@
+package org.bouncycastle.crypto.tls;
+
+import org.bouncycastle.crypto.Digest;
+
+interface TlsHandshakeHash
+ extends Digest
+{
+
+ void init(TlsContext context);
+
+ TlsHandshakeHash commit();
+
+ TlsHandshakeHash fork();
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsInputStream.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsInputStream.java
new file mode 100644
index 00000000..9509dc4f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsInputStream.java
@@ -0,0 +1,41 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * An InputStream for an TLS 1.0 connection.
+ */
+class TlsInputStream
+ extends InputStream
+{
+ private byte[] buf = new byte[1];
+ private TlsProtocol handler = null;
+
+ TlsInputStream(TlsProtocol handler)
+ {
+ this.handler = handler;
+ }
+
+ public int read(byte[] buf, int offset, int len)
+ throws IOException
+ {
+ return this.handler.readApplicationData(buf, offset, len);
+ }
+
+ public int read()
+ throws IOException
+ {
+ if (this.read(buf) < 0)
+ {
+ return -1;
+ }
+ return buf[0] & 0xff;
+ }
+
+ public void close()
+ throws IOException
+ {
+ handler.close();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsKeyExchange.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsKeyExchange.java
new file mode 100644
index 00000000..91590cec
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsKeyExchange.java
@@ -0,0 +1,55 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * A generic interface for key exchange implementations in TLS 1.0/1.1.
+ */
+public interface TlsKeyExchange
+{
+
+ void init(TlsContext context);
+
+ void skipServerCredentials()
+ throws IOException;
+
+ void processServerCredentials(TlsCredentials serverCredentials)
+ throws IOException;
+
+ void processServerCertificate(Certificate serverCertificate)
+ throws IOException;
+
+ boolean requiresServerKeyExchange();
+
+ byte[] generateServerKeyExchange()
+ throws IOException;
+
+ void skipServerKeyExchange()
+ throws IOException;
+
+ void processServerKeyExchange(InputStream input)
+ throws IOException;
+
+ void validateCertificateRequest(CertificateRequest certificateRequest)
+ throws IOException;
+
+ void skipClientCredentials()
+ throws IOException;
+
+ void processClientCredentials(TlsCredentials clientCredentials)
+ throws IOException;
+
+ void processClientCertificate(Certificate clientCertificate)
+ throws IOException;
+
+ void generateClientKeyExchange(OutputStream output)
+ throws IOException;
+
+ void processClientKeyExchange(InputStream input)
+ throws IOException;
+
+ byte[] generatePremasterSecret()
+ throws IOException;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsMac.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsMac.java
new file mode 100644
index 00000000..ec111302
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsMac.java
@@ -0,0 +1,172 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.digests.LongDigest;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A generic TLS MAC implementation, acting as an HMAC based on some underlying Digest.
+ */
+public class TlsMac
+{
+
+ protected TlsContext context;
+ protected byte[] secret;
+ protected Mac mac;
+ protected int digestBlockSize;
+ protected int digestOverhead;
+
+ /**
+ * Generate a new instance of an TlsMac.
+ *
+ * @param context the TLS client context
+ * @param digest The digest to use.
+ * @param key A byte-array where the key for this mac is located.
+ * @param keyOff The number of bytes to skip, before the key starts in the buffer.
+ * @param len The length of the key.
+ */
+ public TlsMac(TlsContext context, Digest digest, byte[] key, int keyOff, int keyLen)
+ {
+ this.context = context;
+
+ KeyParameter keyParameter = new KeyParameter(key, keyOff, keyLen);
+
+ this.secret = Arrays.clone(keyParameter.getKey());
+
+ // TODO This should check the actual algorithm, not rely on the engine type
+ if (digest instanceof LongDigest)
+ {
+ this.digestBlockSize = 128;
+ this.digestOverhead = 16;
+ }
+ else
+ {
+ this.digestBlockSize = 64;
+ this.digestOverhead = 8;
+ }
+
+ if (context.getServerVersion().isSSL())
+ {
+ this.mac = new SSL3Mac(digest);
+
+ // TODO This should check the actual algorithm, not assume based on the digest size
+ if (digest.getDigestSize() == 20)
+ {
+ /*
+ * NOTE: When SHA-1 is used with the SSL 3.0 MAC, the secret + input pad is not
+ * digest block-aligned.
+ */
+ this.digestOverhead = 4;
+ }
+ }
+ else
+ {
+ this.mac = new HMac(digest);
+
+ // NOTE: The input pad for HMAC is always a full digest block
+ }
+
+ this.mac.init(keyParameter);
+ }
+
+ /**
+ * @return the MAC write secret
+ */
+ public byte[] getMACSecret()
+ {
+ return this.secret;
+ }
+
+ /**
+ * @return The Keysize of the mac.
+ */
+ public int getSize()
+ {
+ return mac.getMacSize();
+ }
+
+ /**
+ * Calculate the MAC for some given data.
+ *
+ * @param type The message type of the message.
+ * @param message A byte-buffer containing the message.
+ * @param offset The number of bytes to skip, before the message starts.
+ * @param length The length of the message.
+ * @return A new byte-buffer containing the MAC value.
+ */
+ public byte[] calculateMac(long seqNo, short type, byte[] message, int offset, int length)
+ {
+
+ ProtocolVersion serverVersion = context.getServerVersion();
+ boolean isSSL = serverVersion.isSSL();
+
+ ByteArrayOutputStream bosMac = new ByteArrayOutputStream(isSSL ? 11 : 13);
+ try
+ {
+ TlsUtils.writeUint64(seqNo, bosMac);
+ TlsUtils.writeUint8(type, bosMac);
+
+ if (!isSSL)
+ {
+ TlsUtils.writeVersion(serverVersion, bosMac);
+ }
+
+ TlsUtils.writeUint16(length, bosMac);
+ }
+ catch (IOException e)
+ {
+ // This should never happen
+ throw new IllegalStateException("Internal error during mac calculation");
+ }
+
+ byte[] macHeader = bosMac.toByteArray();
+ mac.update(macHeader, 0, macHeader.length);
+ mac.update(message, offset, length);
+
+ byte[] result = new byte[mac.getMacSize()];
+ mac.doFinal(result, 0);
+ return result;
+ }
+
+ public byte[] calculateMacConstantTime(long seqNo, short type, byte[] message, int offset, int length,
+ int fullLength, byte[] dummyData)
+ {
+
+ /*
+ * Actual MAC only calculated on 'length' bytes...
+ */
+ byte[] result = calculateMac(seqNo, type, message, offset, length);
+
+ /*
+ * ...but ensure a constant number of complete digest blocks are processed (as many as would
+ * be needed for 'fullLength' bytes of input).
+ */
+ int headerLength = context.getServerVersion().isSSL() ? 11 : 13;
+
+ // How many extra full blocks do we need to calculate?
+ int extra = getDigestBlockCount(headerLength + fullLength) - getDigestBlockCount(headerLength + length);
+
+ while (--extra >= 0)
+ {
+ mac.update(dummyData, 0, digestBlockSize);
+ }
+
+ // One more byte in case the implementation is "lazy" about processing blocks
+ mac.update(dummyData[0]);
+ mac.reset();
+
+ return result;
+ }
+
+ private int getDigestBlockCount(int inputLength)
+ {
+ // NOTE: This calculation assumes a minimum of 1 pad byte
+ return (inputLength + digestOverhead) / digestBlockSize;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsNullCipher.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsNullCipher.java
new file mode 100644
index 00000000..d5b2b98e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsNullCipher.java
@@ -0,0 +1,127 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A NULL CipherSuite with optional MAC
+ */
+public class TlsNullCipher
+ implements TlsCipher
+{
+ protected TlsContext context;
+
+ protected TlsMac writeMac;
+ protected TlsMac readMac;
+
+ public TlsNullCipher(TlsContext context)
+ {
+ this.context = context;
+ this.writeMac = null;
+ this.readMac = null;
+ }
+
+ public TlsNullCipher(TlsContext context, Digest clientWriteDigest, Digest serverWriteDigest)
+ throws IOException
+ {
+
+ if ((clientWriteDigest == null) != (serverWriteDigest == null))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ this.context = context;
+
+ TlsMac clientWriteMac = null, serverWriteMac = null;
+
+ if (clientWriteDigest != null)
+ {
+
+ int key_block_size = clientWriteDigest.getDigestSize()
+ + serverWriteDigest.getDigestSize();
+ byte[] key_block = TlsUtils.calculateKeyBlock(context, key_block_size);
+
+ int offset = 0;
+
+ clientWriteMac = new TlsMac(context, clientWriteDigest, key_block, offset,
+ clientWriteDigest.getDigestSize());
+ offset += clientWriteDigest.getDigestSize();
+
+ serverWriteMac = new TlsMac(context, serverWriteDigest, key_block, offset,
+ serverWriteDigest.getDigestSize());
+ offset += serverWriteDigest.getDigestSize();
+
+ if (offset != key_block_size)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ if (context.isServer())
+ {
+ writeMac = serverWriteMac;
+ readMac = clientWriteMac;
+ }
+ else
+ {
+ writeMac = clientWriteMac;
+ readMac = serverWriteMac;
+ }
+ }
+
+ public int getPlaintextLimit(int ciphertextLimit)
+ {
+ int result = ciphertextLimit;
+ if (writeMac != null)
+ {
+ result -= writeMac.getSize();
+ }
+ return result;
+ }
+
+ public byte[] encodePlaintext(long seqNo, short type, byte[] plaintext, int offset, int len)
+ throws IOException
+ {
+
+ if (writeMac == null)
+ {
+ return Arrays.copyOfRange(plaintext, offset, offset + len);
+ }
+
+ byte[] mac = writeMac.calculateMac(seqNo, type, plaintext, offset, len);
+ byte[] ciphertext = new byte[len + mac.length];
+ System.arraycopy(plaintext, offset, ciphertext, 0, len);
+ System.arraycopy(mac, 0, ciphertext, len, mac.length);
+ return ciphertext;
+ }
+
+ public byte[] decodeCiphertext(long seqNo, short type, byte[] ciphertext, int offset, int len)
+ throws IOException
+ {
+
+ if (readMac == null)
+ {
+ return Arrays.copyOfRange(ciphertext, offset, offset + len);
+ }
+
+ int macSize = readMac.getSize();
+ if (len < macSize)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ int macInputLen = len - macSize;
+
+ byte[] receivedMac = Arrays.copyOfRange(ciphertext, offset + macInputLen, offset + len);
+ byte[] computedMac = readMac.calculateMac(seqNo, type, ciphertext, offset, macInputLen);
+
+ if (!Arrays.constantTimeAreEqual(receivedMac, computedMac))
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+ }
+
+ return Arrays.copyOfRange(ciphertext, offset, offset + macInputLen);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsNullCompression.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsNullCompression.java
new file mode 100644
index 00000000..13a85abf
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsNullCompression.java
@@ -0,0 +1,17 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.OutputStream;
+
+public class TlsNullCompression
+ implements TlsCompression
+{
+ public OutputStream compress(OutputStream output)
+ {
+ return output;
+ }
+
+ public OutputStream decompress(OutputStream output)
+ {
+ return output;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsOutputStream.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsOutputStream.java
new file mode 100644
index 00000000..d9532414
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsOutputStream.java
@@ -0,0 +1,44 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * An OutputStream for an TLS connection.
+ */
+class TlsOutputStream
+ extends OutputStream
+{
+ private byte[] buf = new byte[1];
+ private TlsProtocol handler;
+
+ TlsOutputStream(TlsProtocol handler)
+ {
+ this.handler = handler;
+ }
+
+ public void write(byte buf[], int offset, int len)
+ throws IOException
+ {
+ this.handler.writeData(buf, offset, len);
+ }
+
+ public void write(int arg0)
+ throws IOException
+ {
+ buf[0] = (byte)arg0;
+ this.write(buf, 0, 1);
+ }
+
+ public void close()
+ throws IOException
+ {
+ handler.close();
+ }
+
+ public void flush()
+ throws IOException
+ {
+ handler.flush();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsPSKIdentity.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsPSKIdentity.java
new file mode 100644
index 00000000..2f6eea26
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsPSKIdentity.java
@@ -0,0 +1,12 @@
+package org.bouncycastle.crypto.tls;
+
+public interface TlsPSKIdentity
+{
+ void skipIdentityHint();
+
+ void notifyIdentityHint(byte[] psk_identity_hint);
+
+ byte[] getPSKIdentity();
+
+ byte[] getPSK();
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java
new file mode 100644
index 00000000..cfabb76c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java
@@ -0,0 +1,210 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
+import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.util.PublicKeyFactory;
+
+/**
+ * TLS 1.0 PSK key exchange (RFC 4279).
+ */
+public class TlsPSKKeyExchange
+ extends AbstractTlsKeyExchange
+{
+
+ protected TlsPSKIdentity pskIdentity;
+
+ protected byte[] psk_identity_hint = null;
+
+ protected DHPublicKeyParameters dhAgreeServerPublicKey = null;
+ protected DHPrivateKeyParameters dhAgreeClientPrivateKey = null;
+
+ protected AsymmetricKeyParameter serverPublicKey = null;
+ protected RSAKeyParameters rsaServerPublicKey = null;
+ protected byte[] premasterSecret;
+
+ public TlsPSKKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, TlsPSKIdentity pskIdentity)
+ {
+ super(keyExchange, supportedSignatureAlgorithms);
+
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.PSK:
+ case KeyExchangeAlgorithm.RSA_PSK:
+ case KeyExchangeAlgorithm.DHE_PSK:
+ break;
+ default:
+ throw new IllegalArgumentException("unsupported key exchange algorithm");
+ }
+
+ this.pskIdentity = pskIdentity;
+ }
+
+ public void skipServerCredentials()
+ throws IOException
+ {
+ if (keyExchange == KeyExchangeAlgorithm.RSA_PSK)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+
+ public void processServerCertificate(Certificate serverCertificate)
+ throws IOException
+ {
+
+ if (keyExchange != KeyExchangeAlgorithm.RSA_PSK)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ if (serverCertificate.isEmpty())
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+ }
+
+ org.bouncycastle.asn1.x509.Certificate x509Cert = serverCertificate.getCertificateAt(0);
+
+ SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo();
+ try
+ {
+ this.serverPublicKey = PublicKeyFactory.createKey(keyInfo);
+ }
+ catch (RuntimeException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ }
+
+ // Sanity check the PublicKeyFactory
+ if (this.serverPublicKey.isPrivate())
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ this.rsaServerPublicKey = validateRSAPublicKey((RSAKeyParameters)this.serverPublicKey);
+
+ TlsUtils.validateKeyUsage(x509Cert, KeyUsage.keyEncipherment);
+
+ super.processServerCertificate(serverCertificate);
+ }
+
+ public boolean requiresServerKeyExchange()
+ {
+ return keyExchange == KeyExchangeAlgorithm.DHE_PSK;
+ }
+
+ public void processServerKeyExchange(InputStream input)
+ throws IOException
+ {
+
+ this.psk_identity_hint = TlsUtils.readOpaque16(input);
+
+ if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+ {
+ byte[] pBytes = TlsUtils.readOpaque16(input);
+ byte[] gBytes = TlsUtils.readOpaque16(input);
+ byte[] YsBytes = TlsUtils.readOpaque16(input);
+
+ BigInteger p = new BigInteger(1, pBytes);
+ BigInteger g = new BigInteger(1, gBytes);
+ BigInteger Ys = new BigInteger(1, YsBytes);
+
+ this.dhAgreeServerPublicKey = TlsDHUtils.validateDHPublicKey(new DHPublicKeyParameters(Ys,
+ new DHParameters(p, g)));
+ }
+ }
+
+ public void validateCertificateRequest(CertificateRequest certificateRequest)
+ throws IOException
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public void processClientCredentials(TlsCredentials clientCredentials)
+ throws IOException
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public void generateClientKeyExchange(OutputStream output)
+ throws IOException
+ {
+
+ if (psk_identity_hint == null)
+ {
+ pskIdentity.skipIdentityHint();
+ }
+ else
+ {
+ pskIdentity.notifyIdentityHint(psk_identity_hint);
+ }
+
+ byte[] psk_identity = pskIdentity.getPSKIdentity();
+
+ TlsUtils.writeOpaque16(psk_identity, output);
+
+ if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK)
+ {
+ this.premasterSecret = TlsRSAUtils.generateEncryptedPreMasterSecret(context, this.rsaServerPublicKey,
+ output);
+ }
+ else if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+ {
+ this.dhAgreeClientPrivateKey = TlsDHUtils.generateEphemeralClientKeyExchange(context.getSecureRandom(),
+ dhAgreeServerPublicKey.getParameters(), output);
+ }
+ }
+
+ public byte[] generatePremasterSecret()
+ throws IOException
+ {
+
+ byte[] psk = pskIdentity.getPSK();
+ byte[] other_secret = generateOtherSecret(psk.length);
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream(4 + other_secret.length + psk.length);
+ TlsUtils.writeOpaque16(other_secret, buf);
+ TlsUtils.writeOpaque16(psk, buf);
+ return buf.toByteArray();
+ }
+
+ protected byte[] generateOtherSecret(int pskLength)
+ {
+
+ if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+ {
+ return TlsDHUtils.calculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey);
+ }
+
+ if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK)
+ {
+ return this.premasterSecret;
+ }
+
+ return new byte[pskLength];
+ }
+
+ protected RSAKeyParameters validateRSAPublicKey(RSAKeyParameters key)
+ throws IOException
+ {
+ // TODO What is the minimum bit length required?
+ // key.getModulus().bitLength();
+
+ if (!key.getExponent().isProbablePrime(2))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ return key;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java
new file mode 100644
index 00000000..e4080022
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.crypto.tls;
+
+public interface TlsPeer
+{
+
+ /**
+ * This method will be called when an alert is raised by the protocol.
+ *
+ * @param alertLevel {@link AlertLevel}
+ * @param alertDescription {@link AlertDescription}
+ * @param message A human-readable message explaining what caused this alert. May be null.
+ * @param cause The exception that caused this alert to be raised. May be null.
+ */
+ void notifyAlertRaised(short alertLevel, short alertDescription, String message, Exception cause);
+
+ /**
+ * This method will be called when an alert is received from the remote peer.
+ *
+ * @param alertLevel {@link AlertLevel}
+ * @param alertDescription {@link AlertDescription}
+ */
+ void notifyAlertReceived(short alertLevel, short alertDescription);
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java
new file mode 100644
index 00000000..6d8e3d3c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java
@@ -0,0 +1,943 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Integers;
+
+/**
+ * An implementation of all high level protocols in TLS 1.0/1.1.
+ */
+public abstract class TlsProtocol
+{
+
+ protected static final Integer EXT_RenegotiationInfo = Integers.valueOf(ExtensionType.renegotiation_info);
+ protected static final Integer EXT_SessionTicket = Integers.valueOf(ExtensionType.session_ticket);
+
+ private static final String TLS_ERROR_MESSAGE = "Internal TLS error, this could be an attack";
+
+ /*
+ * Our Connection states
+ */
+ protected static final short CS_START = 0;
+ protected static final short CS_CLIENT_HELLO = 1;
+ protected static final short CS_SERVER_HELLO = 2;
+ protected static final short CS_SERVER_SUPPLEMENTAL_DATA = 3;
+ protected static final short CS_SERVER_CERTIFICATE = 4;
+ protected static final short CS_SERVER_KEY_EXCHANGE = 5;
+ protected static final short CS_CERTIFICATE_REQUEST = 6;
+ protected static final short CS_SERVER_HELLO_DONE = 7;
+ protected static final short CS_CLIENT_SUPPLEMENTAL_DATA = 8;
+ protected static final short CS_CLIENT_CERTIFICATE = 9;
+ protected static final short CS_CLIENT_KEY_EXCHANGE = 10;
+ protected static final short CS_CERTIFICATE_VERIFY = 11;
+ protected static final short CS_CLIENT_CHANGE_CIPHER_SPEC = 12;
+ protected static final short CS_CLIENT_FINISHED = 13;
+ protected static final short CS_SERVER_SESSION_TICKET = 14;
+ protected static final short CS_SERVER_CHANGE_CIPHER_SPEC = 15;
+ protected static final short CS_SERVER_FINISHED = 16;
+
+ /*
+ * Queues for data from some protocols.
+ */
+ private ByteQueue applicationDataQueue = new ByteQueue();
+ private ByteQueue changeCipherSpecQueue = new ByteQueue();
+ private ByteQueue alertQueue = new ByteQueue();
+ private ByteQueue handshakeQueue = new ByteQueue();
+
+ /*
+ * The Record Stream we use
+ */
+ protected RecordStream recordStream;
+ protected SecureRandom secureRandom;
+
+ private TlsInputStream tlsInputStream = null;
+ private TlsOutputStream tlsOutputStream = null;
+
+ private volatile boolean closed = false;
+ private volatile boolean failedWithError = false;
+ private volatile boolean appDataReady = false;
+ private volatile boolean writeExtraEmptyRecords = true;
+ private byte[] expected_verify_data = null;
+
+ protected SecurityParameters securityParameters = null;
+
+ protected short connection_state = CS_START;
+ protected boolean secure_renegotiation = false;
+ protected boolean expectSessionTicket = false;
+
+ public TlsProtocol(InputStream input, OutputStream output, SecureRandom secureRandom)
+ {
+ this.recordStream = new RecordStream(this, input, output);
+ this.secureRandom = secureRandom;
+ }
+
+ protected abstract AbstractTlsContext getContext();
+
+ protected abstract TlsPeer getPeer();
+
+ protected abstract void handleChangeCipherSpecMessage()
+ throws IOException;
+
+ protected abstract void handleHandshakeMessage(short type, byte[] buf)
+ throws IOException;
+
+ protected void handleWarningMessage(short description)
+ throws IOException
+ {
+
+ }
+
+ protected void completeHandshake()
+ throws IOException
+ {
+
+ this.expected_verify_data = null;
+
+ /*
+ * We will now read data, until we have completed the handshake.
+ */
+ while (this.connection_state != CS_SERVER_FINISHED)
+ {
+ safeReadRecord();
+ }
+
+ this.recordStream.finaliseHandshake();
+
+ ProtocolVersion version = getContext().getServerVersion();
+ this.writeExtraEmptyRecords = version.isEqualOrEarlierVersionOf(ProtocolVersion.TLSv10);
+
+ /*
+ * If this was an initial handshake, we are now ready to send and receive application data.
+ */
+ if (!appDataReady)
+ {
+ this.appDataReady = true;
+
+ this.tlsInputStream = new TlsInputStream(this);
+ this.tlsOutputStream = new TlsOutputStream(this);
+ }
+ }
+
+ protected void processRecord(short protocol, byte[] buf, int offset, int len)
+ throws IOException
+ {
+ /*
+ * Have a look at the protocol type, and add it to the correct queue.
+ */
+ switch (protocol)
+ {
+ case ContentType.change_cipher_spec:
+ changeCipherSpecQueue.addData(buf, offset, len);
+ processChangeCipherSpec();
+ break;
+ case ContentType.alert:
+ alertQueue.addData(buf, offset, len);
+ processAlert();
+ break;
+ case ContentType.handshake:
+ handshakeQueue.addData(buf, offset, len);
+ processHandshake();
+ break;
+ case ContentType.application_data:
+ if (!appDataReady)
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+ applicationDataQueue.addData(buf, offset, len);
+ processApplicationData();
+ break;
+ default:
+ /*
+ * Uh, we don't know this protocol.
+ *
+ * RFC2246 defines on page 13, that we should ignore this.
+ */
+ }
+ }
+
+ private void processHandshake()
+ throws IOException
+ {
+ boolean read;
+ do
+ {
+ read = false;
+ /*
+ * We need the first 4 bytes, they contain type and length of the message.
+ */
+ if (handshakeQueue.size() >= 4)
+ {
+ byte[] beginning = new byte[4];
+ handshakeQueue.read(beginning, 0, 4, 0);
+ ByteArrayInputStream bis = new ByteArrayInputStream(beginning);
+ short type = TlsUtils.readUint8(bis);
+ int len = TlsUtils.readUint24(bis);
+
+ /*
+ * Check if we have enough bytes in the buffer to read the full message.
+ */
+ if (handshakeQueue.size() >= (len + 4))
+ {
+ /*
+ * Read the message.
+ */
+ byte[] buf = new byte[len];
+ handshakeQueue.read(buf, 0, len, 4);
+ handshakeQueue.removeData(len + 4);
+
+ /*
+ * RFC 2246 7.4.9. The value handshake_messages includes all handshake messages
+ * starting at client hello up to, but not including, this finished message.
+ * [..] Note: [Also,] Hello Request messages are omitted from handshake hashes.
+ */
+ switch (type)
+ {
+ case HandshakeType.hello_request:
+ break;
+ case HandshakeType.finished:
+ {
+
+ if (this.expected_verify_data == null)
+ {
+ this.expected_verify_data = createVerifyData(!getContext().isServer());
+ }
+
+ // NB: Fall through to next case label
+ }
+ default:
+ recordStream.updateHandshakeData(beginning, 0, 4);
+ recordStream.updateHandshakeData(buf, 0, len);
+ break;
+ }
+
+ /*
+ * Now, parse the message.
+ */
+ handleHandshakeMessage(type, buf);
+ read = true;
+ }
+ }
+ }
+ while (read);
+ }
+
+ private void processApplicationData()
+ {
+ /*
+ * There is nothing we need to do here.
+ *
+ * This function could be used for callbacks when application data arrives in the future.
+ */
+ }
+
+ private void processAlert()
+ throws IOException
+ {
+ while (alertQueue.size() >= 2)
+ {
+ /*
+ * An alert is always 2 bytes. Read the alert.
+ */
+ byte[] tmp = new byte[2];
+ alertQueue.read(tmp, 0, 2, 0);
+ alertQueue.removeData(2);
+ short level = tmp[0];
+ short description = tmp[1];
+
+ getPeer().notifyAlertReceived(level, description);
+
+ if (level == AlertLevel.fatal)
+ {
+
+ this.failedWithError = true;
+ this.closed = true;
+ /*
+ * Now try to close the stream, ignore errors.
+ */
+ try
+ {
+ recordStream.close();
+ }
+ catch (Exception e)
+ {
+
+ }
+ throw new IOException(TLS_ERROR_MESSAGE);
+ }
+ else
+ {
+
+ /*
+ * RFC 5246 7.2.1. The other party MUST respond with a close_notify alert of its own
+ * and close down the connection immediately, discarding any pending writes.
+ */
+ // TODO Can close_notify be a fatal alert?
+ if (description == AlertDescription.close_notify)
+ {
+ handleClose(false);
+ }
+
+ /*
+ * If it is just a warning, we continue.
+ */
+ handleWarningMessage(description);
+ }
+ }
+ }
+
+ /**
+ * This method is called, when a change cipher spec message is received.
+ *
+ * @throws IOException If the message has an invalid content or the handshake is not in the correct
+ * state.
+ */
+ private void processChangeCipherSpec()
+ throws IOException
+ {
+ while (changeCipherSpecQueue.size() > 0)
+ {
+ /*
+ * A change cipher spec message is only one byte with the value 1.
+ */
+ byte[] b = new byte[1];
+ changeCipherSpecQueue.read(b, 0, 1, 0);
+ changeCipherSpecQueue.removeData(1);
+ if (b[0] != 1)
+ {
+ /*
+ * This should never happen.
+ */
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+
+ recordStream.receivedReadCipherSpec();
+
+ handleChangeCipherSpecMessage();
+ }
+ }
+
+ /**
+ * Read data from the network. The method will return immediately, if there is still some data
+ * left in the buffer, or block until some application data has been read from the network.
+ *
+ * @param buf The buffer where the data will be copied to.
+ * @param offset The position where the data will be placed in the buffer.
+ * @param len The maximum number of bytes to read.
+ * @return The number of bytes read.
+ * @throws IOException If something goes wrong during reading data.
+ */
+ protected int readApplicationData(byte[] buf, int offset, int len)
+ throws IOException
+ {
+
+ if (len < 1)
+ {
+ return 0;
+ }
+
+ while (applicationDataQueue.size() == 0)
+ {
+ /*
+ * We need to read some data.
+ */
+ if (this.closed)
+ {
+ if (this.failedWithError)
+ {
+ /*
+ * Something went terribly wrong, we should throw an IOException
+ */
+ throw new IOException(TLS_ERROR_MESSAGE);
+ }
+
+ /*
+ * Connection has been closed, there is no more data to read.
+ */
+ return -1;
+ }
+
+ safeReadRecord();
+ }
+ len = Math.min(len, applicationDataQueue.size());
+ applicationDataQueue.read(buf, offset, len, 0);
+ applicationDataQueue.removeData(len);
+ return len;
+ }
+
+ protected void safeReadRecord()
+ throws IOException
+ {
+ try
+ {
+ recordStream.readRecord();
+ }
+ catch (TlsFatalAlert e)
+ {
+ if (!this.closed)
+ {
+ this.failWithError(AlertLevel.fatal, e.getAlertDescription());
+ }
+ throw e;
+ }
+ catch (IOException e)
+ {
+ if (!this.closed)
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+ }
+ throw e;
+ }
+ catch (RuntimeException e)
+ {
+ if (!this.closed)
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+ }
+ throw e;
+ }
+ }
+
+ protected void safeWriteRecord(short type, byte[] buf, int offset, int len)
+ throws IOException
+ {
+ try
+ {
+ recordStream.writeRecord(type, buf, offset, len);
+ }
+ catch (TlsFatalAlert e)
+ {
+ if (!this.closed)
+ {
+ this.failWithError(AlertLevel.fatal, e.getAlertDescription());
+ }
+ throw e;
+ }
+ catch (IOException e)
+ {
+ if (!closed)
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+ }
+ throw e;
+ }
+ catch (RuntimeException e)
+ {
+ if (!closed)
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * Send some application data to the remote system.
+ * <p/>
+ * The method will handle fragmentation internally.
+ *
+ * @param buf The buffer with the data.
+ * @param offset The position in the buffer where the data is placed.
+ * @param len The length of the data.
+ * @throws IOException If something goes wrong during sending.
+ */
+ protected void writeData(byte[] buf, int offset, int len)
+ throws IOException
+ {
+ if (this.closed)
+ {
+ if (this.failedWithError)
+ {
+ throw new IOException(TLS_ERROR_MESSAGE);
+ }
+
+ throw new IOException("Sorry, connection has been closed, you cannot write more data");
+ }
+
+ while (len > 0)
+ {
+ /*
+ * RFC 5246 6.2.1. Zero-length fragments of Application data MAY be sent as they are
+ * potentially useful as a traffic analysis countermeasure.
+ */
+ if (this.writeExtraEmptyRecords)
+ {
+ /*
+ * Protect against known IV attack!
+ *
+ * DO NOT REMOVE THIS LINE, EXCEPT YOU KNOW EXACTLY WHAT YOU ARE DOING HERE.
+ */
+ safeWriteRecord(ContentType.application_data, TlsUtils.EMPTY_BYTES, 0, 0);
+ }
+
+ /*
+ * We are only allowed to write fragments up to 2^14 bytes.
+ */
+ int toWrite = Math.min(len, 1 << 14);
+
+ safeWriteRecord(ContentType.application_data, buf, offset, toWrite);
+
+ offset += toWrite;
+ len -= toWrite;
+ }
+ }
+
+ /**
+ * @return An OutputStream which can be used to send data.
+ */
+ public OutputStream getOutputStream()
+ {
+ return this.tlsOutputStream;
+ }
+
+ /**
+ * @return An InputStream which can be used to read data.
+ */
+ public InputStream getInputStream()
+ {
+ return this.tlsInputStream;
+ }
+
+ /**
+ * Terminate this connection with an alert.
+ * <p/>
+ * Can be used for normal closure too.
+ *
+ * @param alertLevel The level of the alert, an be AlertLevel.fatal or AL_warning.
+ * @param alertDescription The exact alert message.
+ * @throws IOException If alert was fatal.
+ */
+ protected void failWithError(short alertLevel, short alertDescription)
+ throws IOException
+ {
+ /*
+ * Check if the connection is still open.
+ */
+ if (!closed)
+ {
+ /*
+ * Prepare the message
+ */
+ this.closed = true;
+
+ if (alertLevel == AlertLevel.fatal)
+ {
+ /*
+ * This is a fatal message.
+ */
+ this.failedWithError = true;
+ }
+ raiseAlert(alertLevel, alertDescription, null, null);
+ recordStream.close();
+ if (alertLevel == AlertLevel.fatal)
+ {
+ throw new IOException(TLS_ERROR_MESSAGE);
+ }
+ }
+ else
+ {
+ throw new IOException(TLS_ERROR_MESSAGE);
+ }
+ }
+
+ protected void processFinishedMessage(ByteArrayInputStream buf)
+ throws IOException
+ {
+
+ byte[] verify_data = TlsUtils.readFully(expected_verify_data.length, buf);
+
+ assertEmpty(buf);
+
+ /*
+ * Compare both checksums.
+ */
+ if (!Arrays.constantTimeAreEqual(expected_verify_data, verify_data))
+ {
+ /*
+ * Wrong checksum in the finished message.
+ */
+ this.failWithError(AlertLevel.fatal, AlertDescription.decrypt_error);
+ }
+ }
+
+ protected void raiseAlert(short alertLevel, short alertDescription, String message, Exception cause)
+ throws IOException
+ {
+
+ getPeer().notifyAlertRaised(alertLevel, alertDescription, message, cause);
+
+ byte[] error = new byte[2];
+ error[0] = (byte)alertLevel;
+ error[1] = (byte)alertDescription;
+
+ safeWriteRecord(ContentType.alert, error, 0, 2);
+ }
+
+ protected void raiseWarning(short alertDescription, String message)
+ throws IOException
+ {
+ raiseAlert(AlertLevel.warning, alertDescription, message, null);
+ }
+
+ protected void sendCertificateMessage(Certificate certificate)
+ throws IOException
+ {
+
+ if (certificate == null)
+ {
+ certificate = Certificate.EMPTY_CHAIN;
+ }
+
+ if (certificate.getLength() == 0)
+ {
+ TlsContext context = getContext();
+ if (!context.isServer())
+ {
+ ProtocolVersion serverVersion = getContext().getServerVersion();
+ if (serverVersion.isSSL())
+ {
+ String message = serverVersion.toString() + " client didn't provide credentials";
+ raiseWarning(AlertDescription.no_certificate, message);
+ return;
+ }
+ }
+ }
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ TlsUtils.writeUint8(HandshakeType.certificate, bos);
+
+ // Reserve space for length
+ TlsUtils.writeUint24(0, bos);
+
+ certificate.encode(bos);
+ byte[] message = bos.toByteArray();
+
+ // Patch actual length back in
+ TlsUtils.writeUint24(message.length - 4, message, 1);
+
+ safeWriteRecord(ContentType.handshake, message, 0, message.length);
+ }
+
+ protected void sendChangeCipherSpecMessage()
+ throws IOException
+ {
+ byte[] message = new byte[]{1};
+ safeWriteRecord(ContentType.change_cipher_spec, message, 0, message.length);
+ recordStream.sentWriteCipherSpec();
+ }
+
+ protected void sendFinishedMessage()
+ throws IOException
+ {
+ byte[] verify_data = createVerifyData(getContext().isServer());
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ TlsUtils.writeUint8(HandshakeType.finished, bos);
+ TlsUtils.writeUint24(verify_data.length, bos);
+ bos.write(verify_data);
+ byte[] message = bos.toByteArray();
+
+ safeWriteRecord(ContentType.handshake, message, 0, message.length);
+ }
+
+ protected void sendSupplementalDataMessage(Vector supplementalData)
+ throws IOException
+ {
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ TlsUtils.writeUint8(HandshakeType.supplemental_data, buf);
+
+ // Reserve space for length
+ TlsUtils.writeUint24(0, buf);
+
+ writeSupplementalData(buf, supplementalData);
+
+ byte[] message = buf.toByteArray();
+
+ // Patch actual length back in
+ TlsUtils.writeUint24(message.length - 4, message, 1);
+
+ safeWriteRecord(ContentType.handshake, message, 0, message.length);
+ }
+
+ protected byte[] createVerifyData(boolean isServer)
+ {
+ TlsContext context = getContext();
+
+ if (isServer)
+ {
+ return TlsUtils.calculateVerifyData(context, "server finished",
+ recordStream.getCurrentHash(TlsUtils.SSL_SERVER));
+ }
+
+ return TlsUtils.calculateVerifyData(context, "client finished",
+ recordStream.getCurrentHash(TlsUtils.SSL_CLIENT));
+ }
+
+ /**
+ * Closes this connection.
+ *
+ * @throws IOException If something goes wrong during closing.
+ */
+ public void close()
+ throws IOException
+ {
+ handleClose(true);
+ }
+
+ protected void handleClose(boolean user_canceled)
+ throws IOException
+ {
+ if (!closed)
+ {
+ if (user_canceled && !appDataReady)
+ {
+ raiseWarning(AlertDescription.user_canceled, "User canceled handshake");
+ }
+ this.failWithError(AlertLevel.warning, AlertDescription.close_notify);
+ }
+ }
+
+ protected void flush()
+ throws IOException
+ {
+ recordStream.flush();
+ }
+
+ protected static boolean arrayContains(short[] a, short n)
+ {
+ for (int i = 0; i < a.length; ++i)
+ {
+ if (a[i] == n)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected static boolean arrayContains(int[] a, int n)
+ {
+ for (int i = 0; i < a.length; ++i)
+ {
+ if (a[i] == n)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Make sure the InputStream 'buf' now empty. Fail otherwise.
+ *
+ * @param buf The InputStream to check.
+ * @throws IOException If 'buf' is not empty.
+ */
+ protected static void assertEmpty(ByteArrayInputStream buf)
+ throws IOException
+ {
+ if (buf.available() > 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+ }
+
+ protected static byte[] createRandomBlock(SecureRandom random)
+ {
+ byte[] result = new byte[32];
+ random.nextBytes(result);
+ TlsUtils.writeGMTUnixTime(result, 0);
+ return result;
+ }
+
+ protected static byte[] createRenegotiationInfo(byte[] renegotiated_connection)
+ throws IOException
+ {
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ TlsUtils.writeOpaque8(renegotiated_connection, buf);
+ return buf.toByteArray();
+ }
+
+ protected static void establishMasterSecret(TlsContext context, TlsKeyExchange keyExchange)
+ throws IOException
+ {
+
+ byte[] pre_master_secret = keyExchange.generatePremasterSecret();
+
+ try
+ {
+ context.getSecurityParameters().masterSecret = TlsUtils.calculateMasterSecret(context, pre_master_secret);
+ }
+ finally
+ {
+ // TODO Is there a way to ensure the data is really overwritten?
+ /*
+ * RFC 2246 8.1. The pre_master_secret should be deleted from memory once the
+ * master_secret has been computed.
+ */
+ if (pre_master_secret != null)
+ {
+ Arrays.fill(pre_master_secret, (byte)0);
+ }
+ }
+ }
+
+ protected static Hashtable readExtensions(ByteArrayInputStream input)
+ throws IOException
+ {
+
+ if (input.available() < 1)
+ {
+ return null;
+ }
+
+ byte[] extBytes = TlsUtils.readOpaque16(input);
+
+ assertEmpty(input);
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(extBytes);
+
+ // Integer -> byte[]
+ Hashtable extensions = new Hashtable();
+
+ while (buf.available() > 0)
+ {
+ Integer extType = Integers.valueOf(TlsUtils.readUint16(buf));
+ byte[] extValue = TlsUtils.readOpaque16(buf);
+
+ /*
+ * RFC 3546 2.3 There MUST NOT be more than one extension of the same type.
+ */
+ if (null != extensions.put(extType, extValue))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+
+ return extensions;
+ }
+
+ protected static Vector readSupplementalDataMessage(ByteArrayInputStream input)
+ throws IOException
+ {
+
+ byte[] supp_data = TlsUtils.readOpaque24(input);
+
+ assertEmpty(input);
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(supp_data);
+
+ Vector supplementalData = new Vector();
+
+ while (buf.available() > 0)
+ {
+ int supp_data_type = TlsUtils.readUint16(buf);
+ byte[] data = TlsUtils.readOpaque16(buf);
+
+ supplementalData.addElement(new SupplementalDataEntry(supp_data_type, data));
+ }
+
+ return supplementalData;
+ }
+
+ protected static void writeExtensions(OutputStream output, Hashtable extensions)
+ throws IOException
+ {
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+
+ Enumeration keys = extensions.keys();
+ while (keys.hasMoreElements())
+ {
+ Integer extType = (Integer)keys.nextElement();
+ byte[] extValue = (byte[])extensions.get(extType);
+
+ TlsUtils.writeUint16(extType.intValue(), buf);
+ TlsUtils.writeOpaque16(extValue, buf);
+ }
+
+ byte[] extBytes = buf.toByteArray();
+
+ TlsUtils.writeOpaque16(extBytes, output);
+ }
+
+ protected static void writeSupplementalData(OutputStream output, Vector supplementalData)
+ throws IOException
+ {
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+
+ for (int i = 0; i < supplementalData.size(); ++i)
+ {
+ SupplementalDataEntry entry = (SupplementalDataEntry)supplementalData.elementAt(i);
+
+ TlsUtils.writeUint16(entry.getDataType(), buf);
+ TlsUtils.writeOpaque16(entry.getData(), buf);
+ }
+
+ byte[] supp_data = buf.toByteArray();
+
+ TlsUtils.writeOpaque24(supp_data, output);
+ }
+
+ protected static int getPRFAlgorithm(int ciphersuite)
+ {
+
+ switch (ciphersuite)
+ {
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+ return PRFAlgorithm.tls_prf_sha256;
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
+ return PRFAlgorithm.tls_prf_sha384;
+
+ default:
+ return PRFAlgorithm.tls_prf_legacy;
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsProtocolHandler.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsProtocolHandler.java
new file mode 100644
index 00000000..e4fcf285
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsProtocolHandler.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+
+/**
+ * @deprecated use TlsClientProtocol instead
+ */
+public class TlsProtocolHandler
+ extends TlsClientProtocol
+{
+
+ public TlsProtocolHandler(InputStream is, OutputStream os)
+ {
+ super(is, os);
+ }
+
+ public TlsProtocolHandler(InputStream is, OutputStream os, SecureRandom sr)
+ {
+ super(is, os, sr);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsRSAKeyExchange.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsRSAKeyExchange.java
new file mode 100644
index 00000000..24eec539
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsRSAKeyExchange.java
@@ -0,0 +1,255 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.util.PublicKeyFactory;
+import org.bouncycastle.util.io.Streams;
+
+/**
+ * TLS 1.0/1.1 and SSLv3 RSA key exchange.
+ */
+public class TlsRSAKeyExchange
+ extends AbstractTlsKeyExchange
+{
+ protected AsymmetricKeyParameter serverPublicKey = null;
+
+ protected RSAKeyParameters rsaServerPublicKey = null;
+
+ protected TlsEncryptionCredentials serverCredentials = null;
+
+ protected byte[] premasterSecret;
+
+ public TlsRSAKeyExchange(Vector supportedSignatureAlgorithms)
+ {
+ super(KeyExchangeAlgorithm.RSA, supportedSignatureAlgorithms);
+ }
+
+ public void skipServerCredentials()
+ throws IOException
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public void processServerCredentials(TlsCredentials serverCredentials)
+ throws IOException
+ {
+
+ if (!(serverCredentials instanceof TlsEncryptionCredentials))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ processServerCertificate(serverCredentials.getCertificate());
+
+ this.serverCredentials = (TlsEncryptionCredentials)serverCredentials;
+ }
+
+ public void processServerCertificate(Certificate serverCertificate)
+ throws IOException
+ {
+
+ if (serverCertificate.isEmpty())
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+ }
+
+ org.bouncycastle.asn1.x509.Certificate x509Cert = serverCertificate.getCertificateAt(0);
+
+ SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo();
+ try
+ {
+ this.serverPublicKey = PublicKeyFactory.createKey(keyInfo);
+ }
+ catch (RuntimeException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ }
+
+ // Sanity check the PublicKeyFactory
+ if (this.serverPublicKey.isPrivate())
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ this.rsaServerPublicKey = validateRSAPublicKey((RSAKeyParameters)this.serverPublicKey);
+
+ TlsUtils.validateKeyUsage(x509Cert, KeyUsage.keyEncipherment);
+
+ super.processServerCertificate(serverCertificate);
+ }
+
+ public void validateCertificateRequest(CertificateRequest certificateRequest)
+ throws IOException
+ {
+ short[] types = certificateRequest.getCertificateTypes();
+ for (int i = 0; i < types.length; ++i)
+ {
+ switch (types[i])
+ {
+ case ClientCertificateType.rsa_sign:
+ case ClientCertificateType.dss_sign:
+ case ClientCertificateType.ecdsa_sign:
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+ }
+
+ public void processClientCredentials(TlsCredentials clientCredentials)
+ throws IOException
+ {
+ if (!(clientCredentials instanceof TlsSignerCredentials))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public void generateClientKeyExchange(OutputStream output)
+ throws IOException
+ {
+ this.premasterSecret = TlsRSAUtils.generateEncryptedPreMasterSecret(context, this.rsaServerPublicKey, output);
+ }
+
+ public void processClientKeyExchange(InputStream input)
+ throws IOException
+ {
+
+ byte[] encryptedPreMasterSecret;
+ if (context.getServerVersion().isSSL())
+ {
+ // TODO Do any SSLv3 clients actually include the length?
+ encryptedPreMasterSecret = Streams.readAll(input);
+ }
+ else
+ {
+ encryptedPreMasterSecret = TlsUtils.readOpaque16(input);
+ }
+
+ ProtocolVersion clientVersion = context.getClientVersion();
+
+ /*
+ * RFC 5246 7.4.7.1.
+ */
+ {
+ // TODO Provide as configuration option?
+ boolean versionNumberCheckDisabled = false;
+
+ /*
+ * See notes regarding Bleichenbacher/Klima attack. The code here implements the first
+ * construction proposed there, which is RECOMMENDED.
+ */
+ byte[] R = new byte[48];
+ this.context.getSecureRandom().nextBytes(R);
+
+ byte[] M = TlsUtils.EMPTY_BYTES;
+ try
+ {
+ M = serverCredentials.decryptPreMasterSecret(encryptedPreMasterSecret);
+ }
+ catch (Exception e)
+ {
+ /*
+ * In any case, a TLS server MUST NOT generate an alert if processing an
+ * RSA-encrypted premaster secret message fails, or the version number is not as
+ * expected. Instead, it MUST continue the handshake with a randomly generated
+ * premaster secret.
+ */
+ }
+
+ if (M.length != 48)
+ {
+ TlsUtils.writeVersion(clientVersion, R, 0);
+ this.premasterSecret = R;
+ }
+ else
+ {
+ /*
+ * If ClientHello.client_version is TLS 1.1 or higher, server implementations MUST
+ * check the version number [..].
+ */
+ if (versionNumberCheckDisabled && clientVersion.isEqualOrEarlierVersionOf(ProtocolVersion.TLSv10))
+ {
+ /*
+ * If the version number is TLS 1.0 or earlier, server implementations SHOULD
+ * check the version number, but MAY have a configuration option to disable the
+ * check.
+ */
+ }
+ else
+ {
+ /*
+ * Note that explicitly constructing the pre_master_secret with the
+ * ClientHello.client_version produces an invalid master_secret if the client
+ * has sent the wrong version in the original pre_master_secret.
+ */
+ TlsUtils.writeVersion(clientVersion, M, 0);
+ }
+ this.premasterSecret = M;
+ }
+ }
+ }
+
+ public byte[] generatePremasterSecret()
+ throws IOException
+ {
+ if (this.premasterSecret == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ byte[] tmp = this.premasterSecret;
+ this.premasterSecret = null;
+ return tmp;
+ }
+
+ // Would be needed to process RSA_EXPORT server key exchange
+ // protected void processRSAServerKeyExchange(InputStream is, Signer signer) throws IOException
+ // {
+ // InputStream sigIn = is;
+ // if (signer != null)
+ // {
+ // sigIn = new SignerInputStream(is, signer);
+ // }
+ //
+ // byte[] modulusBytes = TlsUtils.readOpaque16(sigIn);
+ // byte[] exponentBytes = TlsUtils.readOpaque16(sigIn);
+ //
+ // if (signer != null)
+ // {
+ // byte[] sigByte = TlsUtils.readOpaque16(is);
+ //
+ // if (!signer.verifySignature(sigByte))
+ // {
+ // handler.failWithError(AlertLevel.fatal, AlertDescription.bad_certificate);
+ // }
+ // }
+ //
+ // BigInteger modulus = new BigInteger(1, modulusBytes);
+ // BigInteger exponent = new BigInteger(1, exponentBytes);
+ //
+ // this.rsaServerPublicKey = validateRSAPublicKey(new RSAKeyParameters(false, modulus,
+ // exponent));
+ // }
+
+ protected RSAKeyParameters validateRSAPublicKey(RSAKeyParameters key)
+ throws IOException
+ {
+ // TODO What is the minimum bit length required?
+ // key.getModulus().bitLength();
+
+ if (!key.getExponent().isProbablePrime(2))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ return key;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsRSASigner.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsRSASigner.java
new file mode 100644
index 00000000..d9f7975a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsRSASigner.java
@@ -0,0 +1,89 @@
+package org.bouncycastle.crypto.tls;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.encodings.PKCS1Encoding;
+import org.bouncycastle.crypto.engines.RSABlindedEngine;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.signers.GenericSigner;
+import org.bouncycastle.crypto.signers.RSADigestSigner;
+import org.bouncycastle.util.Arrays;
+
+public class TlsRSASigner
+ extends AbstractTlsSigner
+{
+
+ public byte[] generateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1)
+ throws CryptoException
+ {
+
+ AsymmetricBlockCipher engine = createRSAImpl();
+ engine.init(true, new ParametersWithRandom(privateKey, this.context.getSecureRandom()));
+ return engine.processBlock(md5AndSha1, 0, md5AndSha1.length);
+ }
+
+ public boolean verifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1)
+ throws CryptoException
+ {
+
+ AsymmetricBlockCipher engine = createRSAImpl();
+ engine.init(false, publicKey);
+ byte[] signed = engine.processBlock(sigBytes, 0, sigBytes.length);
+ return Arrays.constantTimeAreEqual(signed, md5AndSha1);
+ }
+
+ public Signer createSigner(AsymmetricKeyParameter privateKey)
+ {
+ return makeSigner(new CombinedHash(), true,
+ new ParametersWithRandom(privateKey, this.context.getSecureRandom()));
+ }
+
+ public Signer createVerifyer(AsymmetricKeyParameter publicKey)
+ {
+ return makeSigner(new CombinedHash(), false, publicKey);
+ }
+
+ public boolean isValidPublicKey(AsymmetricKeyParameter publicKey)
+ {
+ return publicKey instanceof RSAKeyParameters && !publicKey.isPrivate();
+ }
+
+ protected Signer makeSigner(Digest d, boolean forSigning, CipherParameters cp)
+ {
+ Signer s;
+ if (ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(context.getServerVersion().getEquivalentTLSVersion()))
+ {
+ /*
+ * RFC 5246 4.7. In RSA signing, the opaque vector contains the signature generated
+ * using the RSASSA-PKCS1-v1_5 signature scheme defined in [PKCS1].
+ */
+ s = new RSADigestSigner(d);
+ }
+ else
+ {
+ /*
+ * RFC 5246 4.7. Note that earlier versions of TLS used a different RSA signature scheme
+ * that did not include a DigestInfo encoding.
+ */
+ s = new GenericSigner(createRSAImpl(), d);
+ }
+ s.init(forSigning, cp);
+ return s;
+ }
+
+ protected AsymmetricBlockCipher createRSAImpl()
+ {
+ /*
+ * RFC 5264 7.4.7.1. Implementation note: It is now known that remote timing-based attacks
+ * on TLS are possible, at least when the client and server are on the same LAN.
+ * Accordingly, implementations that use static RSA keys MUST use RSA blinding or some other
+ * anti-timing technique, as described in [TIMING].
+ */
+ return new PKCS1Encoding(new RSABlindedEngine());
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsRSAUtils.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsRSAUtils.java
new file mode 100644
index 00000000..f67e572f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsRSAUtils.java
@@ -0,0 +1,52 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.encodings.PKCS1Encoding;
+import org.bouncycastle.crypto.engines.RSABlindedEngine;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+
+public class TlsRSAUtils
+{
+ public static byte[] generateEncryptedPreMasterSecret(TlsContext context, RSAKeyParameters rsaServerPublicKey,
+ OutputStream output)
+ throws IOException
+ {
+ /*
+ * Choose a PremasterSecret and send it encrypted to the server
+ */
+ byte[] premasterSecret = new byte[48];
+ context.getSecureRandom().nextBytes(premasterSecret);
+ TlsUtils.writeVersion(context.getClientVersion(), premasterSecret, 0);
+
+ PKCS1Encoding encoding = new PKCS1Encoding(new RSABlindedEngine());
+ encoding.init(true, new ParametersWithRandom(rsaServerPublicKey, context.getSecureRandom()));
+
+ try
+ {
+ byte[] encryptedPreMasterSecret = encoding.processBlock(premasterSecret, 0, premasterSecret.length);
+
+ if (context.getServerVersion().isSSL())
+ {
+ // TODO Do any SSLv3 servers actually expect the length?
+ output.write(encryptedPreMasterSecret);
+ }
+ else
+ {
+ TlsUtils.writeOpaque16(encryptedPreMasterSecret, output);
+ }
+ }
+ catch (InvalidCipherTextException e)
+ {
+ /*
+ * This should never happen, only during decryption.
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ return premasterSecret;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsRuntimeException.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsRuntimeException.java
new file mode 100644
index 00000000..3340e490
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsRuntimeException.java
@@ -0,0 +1,26 @@
+package org.bouncycastle.crypto.tls;
+
+public class TlsRuntimeException
+ extends RuntimeException
+{
+ private static final long serialVersionUID = 1928023487348344086L;
+
+ Throwable e;
+
+ public TlsRuntimeException(String message, Throwable e)
+ {
+ super(message);
+
+ this.e = e;
+ }
+
+ public TlsRuntimeException(String message)
+ {
+ super(message);
+ }
+
+ public Throwable getCause()
+ {
+ return e;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsSRPKeyExchange.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsSRPKeyExchange.java
new file mode 100644
index 00000000..b928b91a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsSRPKeyExchange.java
@@ -0,0 +1,217 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.agreement.srp.SRP6Client;
+import org.bouncycastle.crypto.agreement.srp.SRP6Util;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.io.SignerInputStream;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.util.PublicKeyFactory;
+import org.bouncycastle.util.BigIntegers;
+
+/**
+ * TLS 1.1 SRP key exchange (RFC 5054).
+ */
+public class TlsSRPKeyExchange
+ extends AbstractTlsKeyExchange
+{
+
+ protected TlsSigner tlsSigner;
+ protected byte[] identity;
+ protected byte[] password;
+
+ protected AsymmetricKeyParameter serverPublicKey = null;
+
+ protected byte[] s = null;
+ protected BigInteger B = null;
+ protected SRP6Client srpClient = new SRP6Client();
+
+ public TlsSRPKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, byte[] identity, byte[] password)
+ {
+
+ super(keyExchange, supportedSignatureAlgorithms);
+
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.SRP:
+ this.tlsSigner = null;
+ break;
+ case KeyExchangeAlgorithm.SRP_RSA:
+ this.tlsSigner = new TlsRSASigner();
+ break;
+ case KeyExchangeAlgorithm.SRP_DSS:
+ this.tlsSigner = new TlsDSSSigner();
+ break;
+ default:
+ throw new IllegalArgumentException("unsupported key exchange algorithm");
+ }
+
+ this.keyExchange = keyExchange;
+ this.identity = identity;
+ this.password = password;
+ }
+
+ public void init(TlsContext context)
+ {
+ super.init(context);
+
+ if (this.tlsSigner != null)
+ {
+ this.tlsSigner.init(context);
+ }
+ }
+
+ public void skipServerCredentials()
+ throws IOException
+ {
+ if (tlsSigner != null)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+
+ public void processServerCertificate(Certificate serverCertificate)
+ throws IOException
+ {
+
+ if (tlsSigner == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ if (serverCertificate.isEmpty())
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+ }
+
+ org.bouncycastle.asn1.x509.Certificate x509Cert = serverCertificate.getCertificateAt(0);
+
+ SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo();
+ try
+ {
+ this.serverPublicKey = PublicKeyFactory.createKey(keyInfo);
+ }
+ catch (RuntimeException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ }
+
+ if (!tlsSigner.isValidPublicKey(this.serverPublicKey))
+ {
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ }
+
+ TlsUtils.validateKeyUsage(x509Cert, KeyUsage.digitalSignature);
+
+ super.processServerCertificate(serverCertificate);
+ }
+
+ public boolean requiresServerKeyExchange()
+ {
+ return true;
+ }
+
+ public void processServerKeyExchange(InputStream input)
+ throws IOException
+ {
+
+ SecurityParameters securityParameters = context.getSecurityParameters();
+
+ InputStream sigIn = input;
+ Signer signer = null;
+
+ if (tlsSigner != null)
+ {
+ signer = initVerifyer(tlsSigner, securityParameters);
+ sigIn = new SignerInputStream(input, signer);
+ }
+
+ byte[] NBytes = TlsUtils.readOpaque16(sigIn);
+ byte[] gBytes = TlsUtils.readOpaque16(sigIn);
+ byte[] sBytes = TlsUtils.readOpaque8(sigIn);
+ byte[] BBytes = TlsUtils.readOpaque16(sigIn);
+
+ if (signer != null)
+ {
+ byte[] sigByte = TlsUtils.readOpaque16(input);
+
+ if (!signer.verifySignature(sigByte))
+ {
+ throw new TlsFatalAlert(AlertDescription.decrypt_error);
+ }
+ }
+
+ BigInteger N = new BigInteger(1, NBytes);
+ BigInteger g = new BigInteger(1, gBytes);
+
+ // TODO Validate group parameters (see RFC 5054)
+ // handler.failWithError(AlertLevel.fatal, AlertDescription.insufficient_security);
+
+ this.s = sBytes;
+
+ /*
+ * RFC 5054 2.5.3: The client MUST abort the handshake with an "illegal_parameter" alert if
+ * B % N = 0.
+ */
+ try
+ {
+ this.B = SRP6Util.validatePublicValue(N, new BigInteger(1, BBytes));
+ }
+ catch (CryptoException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ this.srpClient.init(N, g, new SHA1Digest(), context.getSecureRandom());
+ }
+
+ public void validateCertificateRequest(CertificateRequest certificateRequest)
+ throws IOException
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public void processClientCredentials(TlsCredentials clientCredentials)
+ throws IOException
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public void generateClientKeyExchange(OutputStream output)
+ throws IOException
+ {
+ byte[] keData = BigIntegers.asUnsignedByteArray(srpClient.generateClientCredentials(s, this.identity,
+ this.password));
+ TlsUtils.writeOpaque16(keData, output);
+ }
+
+ public byte[] generatePremasterSecret()
+ throws IOException
+ {
+ try
+ {
+ // TODO Check if this needs to be a fixed size
+ return BigIntegers.asUnsignedByteArray(srpClient.calculateSecret(B));
+ }
+ catch (CryptoException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+
+ protected Signer initVerifyer(TlsSigner tlsSigner, SecurityParameters securityParameters)
+ {
+ Signer signer = tlsSigner.createVerifyer(this.serverPublicKey);
+ signer.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length);
+ signer.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length);
+ return signer;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsSRTPUtils.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsSRTPUtils.java
new file mode 100644
index 00000000..f82f94df
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsSRTPUtils.java
@@ -0,0 +1,89 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Hashtable;
+
+import org.bouncycastle.util.Integers;
+
+/**
+ * RFC 5764 DTLS Extension to Establish Keys for SRTP.
+ */
+public class TlsSRTPUtils
+{
+
+ public static final Integer EXT_use_srtp = Integers.valueOf(ExtensionType.use_srtp);
+
+ public static void addUseSRTPExtension(Hashtable extensions, UseSRTPData useSRTPData)
+ throws IOException
+ {
+
+ extensions.put(EXT_use_srtp, createUseSRTPExtension(useSRTPData));
+ }
+
+ public static UseSRTPData getUseSRTPExtension(Hashtable extensions)
+ throws IOException
+ {
+
+ if (extensions == null)
+ {
+ return null;
+ }
+ byte[] extensionValue = (byte[])extensions.get(EXT_use_srtp);
+ if (extensionValue == null)
+ {
+ return null;
+ }
+ return readUseSRTPExtension(extensionValue);
+ }
+
+ public static byte[] createUseSRTPExtension(UseSRTPData useSRTPData)
+ throws IOException
+ {
+
+ if (useSRTPData == null)
+ {
+ throw new IllegalArgumentException("'useSRTPData' cannot be null");
+ }
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+
+ // SRTPProtectionProfiles
+ int[] protectionProfiles = useSRTPData.getProtectionProfiles();
+ TlsUtils.writeUint16(2 * protectionProfiles.length, buf);
+ TlsUtils.writeUint16Array(protectionProfiles, buf);
+
+ // srtp_mki
+ TlsUtils.writeOpaque8(useSRTPData.getMki(), buf);
+
+ return buf.toByteArray();
+ }
+
+ public static UseSRTPData readUseSRTPExtension(byte[] extensionValue)
+ throws IOException
+ {
+
+ if (extensionValue == null)
+ {
+ throw new IllegalArgumentException("'extensionValue' cannot be null");
+ }
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(extensionValue);
+
+ // SRTPProtectionProfiles
+ int length = TlsUtils.readUint16(buf);
+ if (length < 2 || (length & 1) != 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+ int[] protectionProfiles = TlsUtils.readUint16Array(length / 2, buf);
+
+ // srtp_mki
+ byte[] mki = TlsUtils.readOpaque8(buf);
+
+ TlsProtocol.assertEmpty(buf);
+
+ return new UseSRTPData(protectionProfiles, mki);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsServer.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsServer.java
new file mode 100644
index 00000000..0b463912
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsServer.java
@@ -0,0 +1,89 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Vector;
+
+public interface TlsServer
+ extends TlsPeer
+{
+
+ void init(TlsServerContext context);
+
+ void notifyClientVersion(ProtocolVersion clientVersion)
+ throws IOException;
+
+ void notifyOfferedCipherSuites(int[] offeredCipherSuites)
+ throws IOException;
+
+ void notifyOfferedCompressionMethods(short[] offeredCompressionMethods)
+ throws IOException;
+
+ void notifySecureRenegotiation(boolean secureNegotiation)
+ throws IOException;
+
+ // Hashtable is (Integer -> byte[])
+ void processClientExtensions(Hashtable clientExtensions)
+ throws IOException;
+
+ ProtocolVersion getServerVersion()
+ throws IOException;
+
+ int getSelectedCipherSuite()
+ throws IOException;
+
+ short getSelectedCompressionMethod()
+ throws IOException;
+
+ // Hashtable is (Integer -> byte[])
+ Hashtable getServerExtensions()
+ throws IOException;
+
+ // Vector is (SupplementalDataEntry)
+ Vector getServerSupplementalData()
+ throws IOException;
+
+ TlsCredentials getCredentials()
+ throws IOException;
+
+ TlsKeyExchange getKeyExchange()
+ throws IOException;
+
+ CertificateRequest getCertificateRequest();
+
+ // Vector is (SupplementalDataEntry)
+ void processClientSupplementalData(Vector clientSupplementalData)
+ throws IOException;
+
+ /**
+ * Called by the protocol handler to report the client certificate, only if a Certificate
+ * {@link #getCertificateRequest()} returned non-null. Note: this method is responsible for
+ * certificate verification and validation.
+ *
+ * @param clientCertificate the effective client certificate (may be an empty chain).
+ * @throws IOException
+ */
+ void notifyClientCertificate(Certificate clientCertificate)
+ throws IOException;
+
+ TlsCompression getCompression()
+ throws IOException;
+
+ TlsCipher getCipher()
+ throws IOException;
+
+ /**
+ * RFC 5077 3.3. NewSessionTicket Handshake Message.
+ * <p/>
+ * This method will be called (only) if a NewSessionTicket extension was sent by the server. See
+ * <i>RFC 5077 4. Recommended Ticket Construction</i> for recommended format and protection.
+ *
+ * @return The ticket.
+ * @throws IOException
+ */
+ NewSessionTicket getNewSessionTicket()
+ throws IOException;
+
+ void notifyHandshakeComplete()
+ throws IOException;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsServerContext.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsServerContext.java
new file mode 100644
index 00000000..37a0c952
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsServerContext.java
@@ -0,0 +1,6 @@
+package org.bouncycastle.crypto.tls;
+
+public interface TlsServerContext
+ extends TlsContext
+{
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsServerContextImpl.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsServerContextImpl.java
new file mode 100644
index 00000000..2fa40293
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsServerContextImpl.java
@@ -0,0 +1,19 @@
+package org.bouncycastle.crypto.tls;
+
+import java.security.SecureRandom;
+
+class TlsServerContextImpl
+ extends AbstractTlsContext
+ implements TlsServerContext
+{
+
+ TlsServerContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters)
+ {
+ super(secureRandom, securityParameters);
+ }
+
+ public boolean isServer()
+ {
+ return true;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java
new file mode 100644
index 00000000..961669f7
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java
@@ -0,0 +1,772 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.util.PublicKeyFactory;
+import org.bouncycastle.util.Arrays;
+
+public class TlsServerProtocol
+ extends TlsProtocol
+{
+
+ protected TlsServer tlsServer = null;
+ protected TlsServerContextImpl tlsServerContext = null;
+
+ protected int[] offeredCipherSuites;
+ protected short[] offeredCompressionMethods;
+ protected Hashtable clientExtensions;
+
+ protected int selectedCipherSuite;
+ protected short selectedCompressionMethod;
+ protected Hashtable serverExtensions;
+
+ protected TlsKeyExchange keyExchange = null;
+ protected TlsCredentials serverCredentials = null;
+ protected CertificateRequest certificateRequest = null;
+
+ protected short clientCertificateType = -1;
+ protected Certificate clientCertificate = null;
+ protected byte[] certificateVerifyHash = null;
+
+ public TlsServerProtocol(InputStream input, OutputStream output, SecureRandom secureRandom)
+ {
+ super(input, output, secureRandom);
+ }
+
+ /**
+ * Receives a TLS handshake in the role of server
+ *
+ * @param tlsServer
+ * @throws IOException If handshake was not successful.
+ */
+ public void accept(TlsServer tlsServer)
+ throws IOException
+ {
+
+ if (tlsServer == null)
+ {
+ throw new IllegalArgumentException("'tlsServer' cannot be null");
+ }
+ if (this.tlsServer != null)
+ {
+ throw new IllegalStateException("accept can only be called once");
+ }
+
+ this.tlsServer = tlsServer;
+
+ this.securityParameters = new SecurityParameters();
+ this.securityParameters.entity = ConnectionEnd.server;
+ this.securityParameters.serverRandom = createRandomBlock(secureRandom);
+
+ this.tlsServerContext = new TlsServerContextImpl(secureRandom, securityParameters);
+ this.tlsServer.init(tlsServerContext);
+ this.recordStream.init(tlsServerContext);
+
+ this.recordStream.setRestrictReadVersion(false);
+
+ completeHandshake();
+
+ this.tlsServer.notifyHandshakeComplete();
+ }
+
+ protected AbstractTlsContext getContext()
+ {
+ return tlsServerContext;
+ }
+
+ protected TlsPeer getPeer()
+ {
+ return tlsServer;
+ }
+
+ protected void handleChangeCipherSpecMessage()
+ throws IOException
+ {
+
+ switch (this.connection_state)
+ {
+ case CS_CLIENT_KEY_EXCHANGE:
+ {
+ if (this.certificateVerifyHash != null)
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+ // NB: Fall through to next case label
+ }
+ case CS_CERTIFICATE_VERIFY:
+ {
+ this.connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC;
+ break;
+ }
+ default:
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
+ }
+ }
+ }
+
+ protected void handleHandshakeMessage(short type, byte[] data)
+ throws IOException
+ {
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(data);
+
+ switch (type)
+ {
+ case HandshakeType.client_hello:
+ {
+ switch (this.connection_state)
+ {
+ case CS_START:
+ {
+ receiveClientHelloMessage(buf);
+ this.connection_state = CS_CLIENT_HELLO;
+
+ sendServerHelloMessage();
+ this.connection_state = CS_SERVER_HELLO;
+
+ // TODO This block could really be done before actually sending the hello
+ {
+ securityParameters.prfAlgorithm = getPRFAlgorithm(selectedCipherSuite);
+ securityParameters.compressionAlgorithm = this.selectedCompressionMethod;
+
+ /*
+ * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify
+ * verify_data_length has a verify_data_length equal to 12. This includes all
+ * existing cipher suites.
+ */
+ securityParameters.verifyDataLength = 12;
+
+ recordStream.notifyHelloComplete();
+ }
+
+ Vector serverSupplementalData = tlsServer.getServerSupplementalData();
+ if (serverSupplementalData != null)
+ {
+ sendSupplementalDataMessage(serverSupplementalData);
+ }
+ this.connection_state = CS_SERVER_SUPPLEMENTAL_DATA;
+
+ this.keyExchange = tlsServer.getKeyExchange();
+ this.keyExchange.init(getContext());
+
+ this.serverCredentials = tlsServer.getCredentials();
+ if (this.serverCredentials == null)
+ {
+ this.keyExchange.skipServerCredentials();
+ }
+ else
+ {
+ this.keyExchange.processServerCredentials(this.serverCredentials);
+ sendCertificateMessage(this.serverCredentials.getCertificate());
+ }
+ this.connection_state = CS_SERVER_CERTIFICATE;
+
+ byte[] serverKeyExchange = this.keyExchange.generateServerKeyExchange();
+ if (serverKeyExchange != null)
+ {
+ sendServerKeyExchangeMessage(serverKeyExchange);
+ }
+ this.connection_state = CS_SERVER_KEY_EXCHANGE;
+
+ if (this.serverCredentials != null)
+ {
+ this.certificateRequest = tlsServer.getCertificateRequest();
+ if (this.certificateRequest != null)
+ {
+ this.keyExchange.validateCertificateRequest(certificateRequest);
+ sendCertificateRequestMessage(certificateRequest);
+ }
+ }
+ this.connection_state = CS_CERTIFICATE_REQUEST;
+
+ sendServerHelloDoneMessage();
+ this.connection_state = CS_SERVER_HELLO_DONE;
+
+ break;
+ }
+ default:
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+ }
+ break;
+ }
+ case HandshakeType.supplemental_data:
+ {
+ switch (this.connection_state)
+ {
+ case CS_SERVER_HELLO_DONE:
+ {
+ tlsServer.processClientSupplementalData(readSupplementalDataMessage(buf));
+ this.connection_state = CS_CLIENT_SUPPLEMENTAL_DATA;
+ break;
+ }
+ default:
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+ }
+ break;
+ }
+ case HandshakeType.certificate:
+ {
+ switch (this.connection_state)
+ {
+ case CS_SERVER_HELLO_DONE:
+ {
+ tlsServer.processClientSupplementalData(null);
+ // NB: Fall through to next case label
+ }
+ case CS_CLIENT_SUPPLEMENTAL_DATA:
+ {
+ if (this.certificateRequest == null)
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+ receiveCertificateMessage(buf);
+ this.connection_state = CS_CLIENT_CERTIFICATE;
+ break;
+ }
+ default:
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+ }
+ break;
+ }
+ case HandshakeType.client_key_exchange:
+ {
+ switch (this.connection_state)
+ {
+ case CS_SERVER_HELLO_DONE:
+ {
+ tlsServer.processClientSupplementalData(null);
+ // NB: Fall through to next case label
+ }
+ case CS_CLIENT_SUPPLEMENTAL_DATA:
+ {
+ if (this.certificateRequest == null)
+ {
+ this.keyExchange.skipClientCredentials();
+ }
+ else
+ {
+
+ ProtocolVersion equivalentTLSVersion = getContext().getServerVersion().getEquivalentTLSVersion();
+
+ if (ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(equivalentTLSVersion))
+ {
+ /*
+ * RFC 5246 If no suitable certificate is available, the client MUST send a
+ * certificate message containing no certificates.
+ *
+ * NOTE: In previous RFCs, this was SHOULD instead of MUST.
+ */
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+ else if (equivalentTLSVersion.isSSL())
+ {
+ if (clientCertificate == null)
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+ }
+ else
+ {
+ notifyClientCertificate(Certificate.EMPTY_CHAIN);
+ }
+ }
+ // NB: Fall through to next case label
+ }
+ case CS_CLIENT_CERTIFICATE:
+ {
+ receiveClientKeyExchangeMessage(buf);
+ this.connection_state = CS_CLIENT_KEY_EXCHANGE;
+ break;
+ }
+ default:
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+ }
+ break;
+ }
+ case HandshakeType.certificate_verify:
+ {
+ switch (this.connection_state)
+ {
+ case CS_CLIENT_KEY_EXCHANGE:
+ {
+ /*
+ * RFC 5246 7.4.8 This message is only sent following a client certificate that has
+ * signing capability (i.e., all certificates except those containing fixed
+ * Diffie-Hellman parameters).
+ */
+ if (this.certificateVerifyHash == null)
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+ receiveCertificateVerifyMessage(buf);
+ this.connection_state = CS_CERTIFICATE_VERIFY;
+ break;
+ }
+ default:
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+ }
+ break;
+ }
+ case HandshakeType.finished:
+ {
+ switch (this.connection_state)
+ {
+ case CS_CLIENT_CHANGE_CIPHER_SPEC:
+ processFinishedMessage(buf);
+ this.connection_state = CS_CLIENT_FINISHED;
+
+ if (expectSessionTicket)
+ {
+ sendNewSessionTicketMessage(tlsServer.getNewSessionTicket());
+ }
+ this.connection_state = CS_SERVER_SESSION_TICKET;
+
+ sendChangeCipherSpecMessage();
+ this.connection_state = CS_SERVER_CHANGE_CIPHER_SPEC;
+
+ sendFinishedMessage();
+ this.connection_state = CS_SERVER_FINISHED;
+ break;
+ default:
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.hello_request:
+ case HandshakeType.hello_verify_request:
+ case HandshakeType.server_hello:
+ case HandshakeType.server_key_exchange:
+ case HandshakeType.certificate_request:
+ case HandshakeType.server_hello_done:
+ case HandshakeType.session_ticket:
+ default:
+ // We do not support this!
+ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ break;
+ }
+ }
+
+ protected void handleWarningMessage(short description)
+ throws IOException
+ {
+ switch (description)
+ {
+ case AlertDescription.no_certificate:
+ {
+ /*
+ * SSL 3.0 If the server has sent a certificate request Message, the client must send
+ * either the certificate message or a no_certificate alert.
+ */
+ if (getContext().getServerVersion().isSSL() && certificateRequest != null)
+ {
+ notifyClientCertificate(Certificate.EMPTY_CHAIN);
+ }
+ break;
+ }
+ default:
+ {
+ super.handleWarningMessage(description);
+ }
+ }
+ }
+
+ protected void notifyClientCertificate(Certificate clientCertificate)
+ throws IOException
+ {
+
+ if (certificateRequest == null)
+ {
+ throw new IllegalStateException();
+ }
+
+ if (this.clientCertificate != null)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ this.clientCertificate = clientCertificate;
+
+ if (clientCertificate.isEmpty())
+ {
+ this.keyExchange.skipClientCredentials();
+ }
+ else
+ {
+
+ /*
+ * TODO RFC 5246 7.4.6. If the certificate_authorities list in the certificate request
+ * message was non-empty, one of the certificates in the certificate chain SHOULD be
+ * issued by one of the listed CAs.
+ */
+
+ this.clientCertificateType = TlsUtils.getClientCertificateType(clientCertificate,
+ this.serverCredentials.getCertificate());
+
+ this.keyExchange.processClientCertificate(clientCertificate);
+ }
+
+ /*
+ * RFC 5246 7.4.6. If the client does not send any certificates, the server MAY at its
+ * discretion either continue the handshake without client authentication, or respond with a
+ * fatal handshake_failure alert. Also, if some aspect of the certificate chain was
+ * unacceptable (e.g., it was not signed by a known, trusted CA), the server MAY at its
+ * discretion either continue the handshake (considering the client unauthenticated) or send
+ * a fatal alert.
+ */
+ this.tlsServer.notifyClientCertificate(clientCertificate);
+ }
+
+ protected void receiveCertificateMessage(ByteArrayInputStream buf)
+ throws IOException
+ {
+
+ Certificate clientCertificate = Certificate.parse(buf);
+
+ assertEmpty(buf);
+
+ notifyClientCertificate(clientCertificate);
+ }
+
+ protected void receiveCertificateVerifyMessage(ByteArrayInputStream buf)
+ throws IOException
+ {
+
+ byte[] clientCertificateSignature = TlsUtils.readOpaque16(buf);
+
+ assertEmpty(buf);
+
+ // Verify the CertificateVerify message contains a correct signature.
+ try
+ {
+ TlsSigner tlsSigner = TlsUtils.createTlsSigner(this.clientCertificateType);
+ tlsSigner.init(getContext());
+
+ org.bouncycastle.asn1.x509.Certificate x509Cert = this.clientCertificate.getCertificateAt(0);
+ SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo();
+ AsymmetricKeyParameter publicKey = PublicKeyFactory.createKey(keyInfo);
+
+ tlsSigner.verifyRawSignature(clientCertificateSignature, publicKey, this.certificateVerifyHash);
+ }
+ catch (Exception e)
+ {
+ throw new TlsFatalAlert(AlertDescription.decrypt_error);
+ }
+ }
+
+ protected void receiveClientHelloMessage(ByteArrayInputStream buf)
+ throws IOException
+ {
+
+ ProtocolVersion client_version = TlsUtils.readVersion(buf);
+ if (client_version.isDTLS())
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+ }
+
+ /*
+ * Read the client random
+ */
+ byte[] client_random = TlsUtils.readFully(32, buf);
+
+ byte[] sessionID = TlsUtils.readOpaque8(buf);
+ if (sessionID.length > 32)
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+ }
+
+ int cipher_suites_length = TlsUtils.readUint16(buf);
+ if (cipher_suites_length < 2 || (cipher_suites_length & 1) != 0)
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.decode_error);
+ }
+
+ /*
+ * NOTE: "If the session_id field is not empty (implying a session resumption request) this
+ * vector must include at least the cipher_suite from that session."
+ */
+ this.offeredCipherSuites = TlsUtils.readUint16Array(cipher_suites_length / 2, buf);
+
+ int compression_methods_length = TlsUtils.readUint8(buf);
+ if (compression_methods_length < 1)
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+ }
+
+ this.offeredCompressionMethods = TlsUtils.readUint8Array(compression_methods_length, buf);
+
+ /*
+ * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore
+ * extensions appearing in the client hello, and send a server hello containing no
+ * extensions.
+ */
+ this.clientExtensions = readExtensions(buf);
+
+ getContext().setClientVersion(client_version);
+
+ tlsServer.notifyClientVersion(client_version);
+
+ securityParameters.clientRandom = client_random;
+
+ tlsServer.notifyOfferedCipherSuites(offeredCipherSuites);
+ tlsServer.notifyOfferedCompressionMethods(offeredCompressionMethods);
+
+ /*
+ * RFC 5746 3.6. Server Behavior: Initial Handshake
+ */
+ {
+ /*
+ * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension,
+ * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the
+ * ClientHello. Including both is NOT RECOMMENDED.
+ */
+
+ /*
+ * When a ClientHello is received, the server MUST check if it includes the
+ * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, set the secure_renegotiation flag
+ * to TRUE.
+ */
+ if (arrayContains(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV))
+ {
+ this.secure_renegotiation = true;
+ }
+
+ /*
+ * The server MUST check if the "renegotiation_info" extension is included in the
+ * ClientHello.
+ */
+ if (clientExtensions != null)
+ {
+ byte[] renegExtValue = (byte[])clientExtensions.get(EXT_RenegotiationInfo);
+ if (renegExtValue != null)
+ {
+ /*
+ * If the extension is present, set secure_renegotiation flag to TRUE. The
+ * server MUST then verify that the length of the "renegotiated_connection"
+ * field is zero, and if it is not, MUST abort the handshake.
+ */
+ this.secure_renegotiation = true;
+
+ if (!Arrays.constantTimeAreEqual(renegExtValue, createRenegotiationInfo(TlsUtils.EMPTY_BYTES)))
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
+ }
+ }
+ }
+ }
+
+ tlsServer.notifySecureRenegotiation(this.secure_renegotiation);
+
+ if (clientExtensions != null)
+ {
+ tlsServer.processClientExtensions(clientExtensions);
+ }
+ }
+
+ protected void receiveClientKeyExchangeMessage(ByteArrayInputStream buf)
+ throws IOException
+ {
+
+ this.keyExchange.processClientKeyExchange(buf);
+
+ assertEmpty(buf);
+
+ establishMasterSecret(getContext(), keyExchange);
+
+ /*
+ * Initialize our cipher suite
+ */
+ recordStream.setPendingConnectionState(tlsServer.getCompression(), tlsServer.getCipher());
+
+ if (expectCertificateVerifyMessage())
+ {
+ this.certificateVerifyHash = recordStream.getCurrentHash(null);
+ }
+ }
+
+ protected void sendCertificateRequestMessage(CertificateRequest certificateRequest)
+ throws IOException
+ {
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ TlsUtils.writeUint8(HandshakeType.certificate_request, buf);
+
+ // Reserve space for length
+ TlsUtils.writeUint24(0, buf);
+
+ certificateRequest.encode(buf);
+ byte[] message = buf.toByteArray();
+
+ // Patch actual length back in
+ TlsUtils.writeUint24(message.length - 4, message, 1);
+
+ safeWriteRecord(ContentType.handshake, message, 0, message.length);
+ }
+
+ protected void sendNewSessionTicketMessage(NewSessionTicket newSessionTicket)
+ throws IOException
+ {
+
+ if (newSessionTicket == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ TlsUtils.writeUint8(HandshakeType.session_ticket, buf);
+
+ // Reserve space for length
+ TlsUtils.writeUint24(0, buf);
+
+ newSessionTicket.encode(buf);
+ byte[] message = buf.toByteArray();
+
+ // Patch actual length back in
+ TlsUtils.writeUint24(message.length - 4, message, 1);
+
+ safeWriteRecord(ContentType.handshake, message, 0, message.length);
+ }
+
+ protected void sendServerHelloMessage()
+ throws IOException
+ {
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ TlsUtils.writeUint8(HandshakeType.server_hello, buf);
+
+ // Reserve space for length
+ TlsUtils.writeUint24(0, buf);
+
+ ProtocolVersion server_version = tlsServer.getServerVersion();
+ if (!server_version.isEqualOrEarlierVersionOf(getContext().getClientVersion()))
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+ }
+
+ recordStream.setReadVersion(server_version);
+ recordStream.setWriteVersion(server_version);
+ recordStream.setRestrictReadVersion(true);
+ getContext().setServerVersion(server_version);
+
+ TlsUtils.writeVersion(server_version, buf);
+
+ buf.write(this.securityParameters.serverRandom);
+
+ /*
+ * The server may return an empty session_id to indicate that the session will not be cached
+ * and therefore cannot be resumed.
+ */
+ TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf);
+
+ this.selectedCipherSuite = tlsServer.getSelectedCipherSuite();
+ if (!arrayContains(this.offeredCipherSuites, this.selectedCipherSuite)
+ || this.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
+ || this.selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+ }
+
+ this.selectedCompressionMethod = tlsServer.getSelectedCompressionMethod();
+ if (!arrayContains(this.offeredCompressionMethods, this.selectedCompressionMethod))
+ {
+ this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+ }
+
+ TlsUtils.writeUint16(this.selectedCipherSuite, buf);
+ TlsUtils.writeUint8(this.selectedCompressionMethod, buf);
+
+ this.serverExtensions = tlsServer.getServerExtensions();
+
+ /*
+ * RFC 5746 3.6. Server Behavior: Initial Handshake
+ */
+ if (this.secure_renegotiation)
+ {
+
+ boolean noRenegExt = this.serverExtensions == null
+ || !this.serverExtensions.containsKey(EXT_RenegotiationInfo);
+
+ if (noRenegExt)
+ {
+ /*
+ * Note that sending a "renegotiation_info" extension in response to a ClientHello
+ * containing only the SCSV is an explicit exception to the prohibition in RFC 5246,
+ * Section 7.4.1.4, on the server sending unsolicited extensions and is only allowed
+ * because the client is signaling its willingness to receive the extension via the
+ * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.
+ */
+ if (this.serverExtensions == null)
+ {
+ this.serverExtensions = new Hashtable();
+ }
+
+ /*
+ * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty
+ * "renegotiation_info" extension in the ServerHello message.
+ */
+ this.serverExtensions.put(EXT_RenegotiationInfo, createRenegotiationInfo(TlsUtils.EMPTY_BYTES));
+ }
+ }
+
+ if (this.serverExtensions != null)
+ {
+ this.expectSessionTicket = serverExtensions.containsKey(EXT_SessionTicket);
+ writeExtensions(buf, this.serverExtensions);
+ }
+
+ byte[] message = buf.toByteArray();
+
+ // Patch actual length back in
+ TlsUtils.writeUint24(message.length - 4, message, 1);
+
+ safeWriteRecord(ContentType.handshake, message, 0, message.length);
+ }
+
+ protected void sendServerHelloDoneMessage()
+ throws IOException
+ {
+
+ byte[] message = new byte[4];
+ TlsUtils.writeUint8(HandshakeType.server_hello_done, message, 0);
+ TlsUtils.writeUint24(0, message, 1);
+
+ safeWriteRecord(ContentType.handshake, message, 0, message.length);
+ }
+
+ protected void sendServerKeyExchangeMessage(byte[] serverKeyExchange)
+ throws IOException
+ {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+ TlsUtils.writeUint8(HandshakeType.server_key_exchange, bos);
+ TlsUtils.writeUint24(serverKeyExchange.length, bos);
+ bos.write(serverKeyExchange);
+ byte[] message = bos.toByteArray();
+
+ safeWriteRecord(ContentType.handshake, message, 0, message.length);
+ }
+
+ protected boolean expectCertificateVerifyMessage()
+ {
+ return this.clientCertificateType >= 0 && TlsUtils.hasSigningCapability(this.clientCertificateType);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsSigner.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsSigner.java
new file mode 100644
index 00000000..2b615076
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsSigner.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.crypto.tls;
+
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+
+public interface TlsSigner
+{
+
+ void init(TlsContext context);
+
+ byte[] generateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1)
+ throws CryptoException;
+
+ boolean verifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1)
+ throws CryptoException;
+
+ Signer createSigner(AsymmetricKeyParameter privateKey);
+
+ Signer createVerifyer(AsymmetricKeyParameter publicKey);
+
+ boolean isValidPublicKey(AsymmetricKeyParameter publicKey);
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsSignerCredentials.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsSignerCredentials.java
new file mode 100644
index 00000000..7067fa21
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsSignerCredentials.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+public interface TlsSignerCredentials
+ extends TlsCredentials
+{
+ byte[] generateCertificateSignature(byte[] md5andsha1)
+ throws IOException;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsStreamCipher.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsStreamCipher.java
new file mode 100644
index 00000000..1755c2d4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsStreamCipher.java
@@ -0,0 +1,126 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+
+public class TlsStreamCipher
+ implements TlsCipher
+{
+ protected TlsContext context;
+
+ protected StreamCipher encryptCipher;
+ protected StreamCipher decryptCipher;
+
+ protected TlsMac writeMac;
+ protected TlsMac readMac;
+
+ public TlsStreamCipher(TlsContext context, StreamCipher clientWriteCipher,
+ StreamCipher serverWriteCipher, Digest clientWriteDigest, Digest serverWriteDigest,
+ int cipherKeySize)
+ throws IOException
+ {
+
+ boolean isServer = context.isServer();
+
+ this.context = context;
+
+ this.encryptCipher = clientWriteCipher;
+ this.decryptCipher = serverWriteCipher;
+
+ int key_block_size = (2 * cipherKeySize) + clientWriteDigest.getDigestSize()
+ + serverWriteDigest.getDigestSize();
+
+ byte[] key_block = TlsUtils.calculateKeyBlock(context, key_block_size);
+
+ int offset = 0;
+
+ // Init MACs
+ TlsMac clientWriteMac = new TlsMac(context, clientWriteDigest, key_block, offset,
+ clientWriteDigest.getDigestSize());
+ offset += clientWriteDigest.getDigestSize();
+ TlsMac serverWriteMac = new TlsMac(context, serverWriteDigest, key_block, offset,
+ serverWriteDigest.getDigestSize());
+ offset += serverWriteDigest.getDigestSize();
+
+ // Build keys
+ KeyParameter clientWriteKey = new KeyParameter(key_block, offset, cipherKeySize);
+ offset += cipherKeySize;
+ KeyParameter serverWriteKey = new KeyParameter(key_block, offset, cipherKeySize);
+ offset += cipherKeySize;
+
+ if (offset != key_block_size)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ CipherParameters encryptParams, decryptParams;
+ if (isServer)
+ {
+ this.writeMac = serverWriteMac;
+ this.readMac = clientWriteMac;
+ this.encryptCipher = serverWriteCipher;
+ this.decryptCipher = clientWriteCipher;
+ encryptParams = serverWriteKey;
+ decryptParams = clientWriteKey;
+ }
+ else
+ {
+ this.writeMac = clientWriteMac;
+ this.readMac = serverWriteMac;
+ this.encryptCipher = clientWriteCipher;
+ this.decryptCipher = serverWriteCipher;
+ encryptParams = clientWriteKey;
+ decryptParams = serverWriteKey;
+ }
+
+ this.encryptCipher.init(true, encryptParams);
+ this.decryptCipher.init(false, decryptParams);
+ }
+
+ public int getPlaintextLimit(int ciphertextLimit)
+ {
+ return ciphertextLimit - writeMac.getSize();
+ }
+
+ public byte[] encodePlaintext(long seqNo, short type, byte[] plaintext, int offset, int len)
+ {
+ byte[] mac = writeMac.calculateMac(seqNo, type, plaintext, offset, len);
+
+ byte[] outbuf = new byte[len + mac.length];
+
+ encryptCipher.processBytes(plaintext, offset, len, outbuf, 0);
+ encryptCipher.processBytes(mac, 0, mac.length, outbuf, len);
+
+ return outbuf;
+ }
+
+ public byte[] decodeCiphertext(long seqNo, short type, byte[] ciphertext, int offset, int len)
+ throws IOException
+ {
+ int macSize = readMac.getSize();
+ if (len < macSize)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ byte[] deciphered = new byte[len];
+ decryptCipher.processBytes(ciphertext, offset, len, deciphered, 0);
+
+ int macInputLen = len - macSize;
+
+ byte[] receivedMac = Arrays.copyOfRange(deciphered, macInputLen, len);
+ byte[] computedMac = readMac.calculateMac(seqNo, type, deciphered, 0, macInputLen);
+
+ if (!Arrays.constantTimeAreEqual(receivedMac, computedMac))
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+ }
+
+ return Arrays.copyOfRange(deciphered, 0, macInputLen);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java
new file mode 100644
index 00000000..8b162104
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java
@@ -0,0 +1,968 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.MD5Digest;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA224Digest;
+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.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.util.PublicKeyFactory;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Integers;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.io.Streams;
+
+/**
+ * Some helper functions for MicroTLS.
+ */
+public class TlsUtils
+{
+ public static byte[] EMPTY_BYTES = new byte[0];
+
+ public static final Integer EXT_signature_algorithms = Integers.valueOf(ExtensionType.signature_algorithms);
+
+ public static boolean isValidUint8(short i)
+ {
+ return (i & 0xFF) == i;
+ }
+
+ public static boolean isValidUint16(int i)
+ {
+ return (i & 0xFFFF) == i;
+ }
+
+ public static boolean isValidUint24(int i)
+ {
+ return (i & 0xFFFFFF) == i;
+ }
+
+ public static boolean isValidUint32(long i)
+ {
+ return (i & 0xFFFFFFFFL) == i;
+ }
+
+ public static boolean isValidUint48(long i)
+ {
+ return (i & 0xFFFFFFFFFFFFL) == i;
+ }
+
+ public static boolean isValidUint64(long i)
+ {
+ return true;
+ }
+
+ public static void writeUint8(short i, OutputStream output)
+ throws IOException
+ {
+ output.write(i);
+ }
+
+ public static void writeUint8(short i, byte[] buf, int offset)
+ {
+ buf[offset] = (byte)i;
+ }
+
+ public static void writeUint16(int i, OutputStream output)
+ throws IOException
+ {
+ output.write(i >> 8);
+ output.write(i);
+ }
+
+ public static void writeUint16(int i, byte[] buf, int offset)
+ {
+ buf[offset] = (byte)(i >> 8);
+ buf[offset + 1] = (byte)i;
+ }
+
+ public static void writeUint24(int i, OutputStream output)
+ throws IOException
+ {
+ output.write(i >> 16);
+ output.write(i >> 8);
+ output.write(i);
+ }
+
+ public static void writeUint24(int i, byte[] buf, int offset)
+ {
+ buf[offset] = (byte)(i >> 16);
+ buf[offset + 1] = (byte)(i >> 8);
+ buf[offset + 2] = (byte)(i);
+ }
+
+ public static void writeUint32(long i, OutputStream output)
+ throws IOException
+ {
+ output.write((int)(i >> 24));
+ output.write((int)(i >> 16));
+ output.write((int)(i >> 8));
+ output.write((int)(i));
+ }
+
+ public static void writeUint32(long i, byte[] buf, int offset)
+ {
+ buf[offset] = (byte)(i >> 24);
+ buf[offset + 1] = (byte)(i >> 16);
+ buf[offset + 2] = (byte)(i >> 8);
+ buf[offset + 3] = (byte)(i);
+ }
+
+ public static void writeUint48(long i, byte[] buf, int offset)
+ {
+ buf[offset] = (byte)(i >> 40);
+ buf[offset + 1] = (byte)(i >> 32);
+ buf[offset + 2] = (byte)(i >> 24);
+ buf[offset + 3] = (byte)(i >> 16);
+ buf[offset + 4] = (byte)(i >> 8);
+ buf[offset + 5] = (byte)(i);
+ }
+
+ public static void writeUint64(long i, OutputStream output)
+ throws IOException
+ {
+ output.write((int)(i >> 56));
+ output.write((int)(i >> 48));
+ output.write((int)(i >> 40));
+ output.write((int)(i >> 32));
+ output.write((int)(i >> 24));
+ output.write((int)(i >> 16));
+ output.write((int)(i >> 8));
+ output.write((int)(i));
+ }
+
+ public static void writeUint64(long i, byte[] buf, int offset)
+ {
+ buf[offset] = (byte)(i >> 56);
+ buf[offset + 1] = (byte)(i >> 48);
+ buf[offset + 2] = (byte)(i >> 40);
+ buf[offset + 3] = (byte)(i >> 32);
+ buf[offset + 4] = (byte)(i >> 24);
+ buf[offset + 5] = (byte)(i >> 16);
+ buf[offset + 6] = (byte)(i >> 8);
+ buf[offset + 7] = (byte)(i);
+ }
+
+ public static void writeOpaque8(byte[] buf, OutputStream output)
+ throws IOException
+ {
+ writeUint8((short)buf.length, output);
+ output.write(buf);
+ }
+
+ public static void writeOpaque16(byte[] buf, OutputStream output)
+ throws IOException
+ {
+ writeUint16(buf.length, output);
+ output.write(buf);
+ }
+
+ public static void writeOpaque24(byte[] buf, OutputStream output)
+ throws IOException
+ {
+ writeUint24(buf.length, output);
+ output.write(buf);
+ }
+
+ public static void writeUint8Array(short[] uints, OutputStream output)
+ throws IOException
+ {
+ for (int i = 0; i < uints.length; ++i)
+ {
+ writeUint8(uints[i], output);
+ }
+ }
+
+ public static void writeUint16Array(int[] uints, OutputStream output)
+ throws IOException
+ {
+ for (int i = 0; i < uints.length; ++i)
+ {
+ writeUint16(uints[i], output);
+ }
+ }
+
+ public static short readUint8(InputStream input)
+ throws IOException
+ {
+ int i = input.read();
+ if (i < 0)
+ {
+ throw new EOFException();
+ }
+ return (short)i;
+ }
+
+ public static short readUint8(byte[] buf, int offset)
+ {
+ return (short)buf[offset];
+ }
+
+ public static int readUint16(InputStream input)
+ throws IOException
+ {
+ int i1 = input.read();
+ int i2 = input.read();
+ if (i2 < 0)
+ {
+ throw new EOFException();
+ }
+ return i1 << 8 | i2;
+ }
+
+ public static int readUint16(byte[] buf, int offset)
+ {
+ int n = (buf[offset] & 0xff) << 8;
+ n |= (buf[++offset] & 0xff);
+ return n;
+ }
+
+ public static int readUint24(InputStream input)
+ throws IOException
+ {
+ int i1 = input.read();
+ int i2 = input.read();
+ int i3 = input.read();
+ if (i3 < 0)
+ {
+ throw new EOFException();
+ }
+ return (i1 << 16) | (i2 << 8) | i3;
+ }
+
+ public static int readUint24(byte[] buf, int offset)
+ {
+ int n = (buf[offset] & 0xff) << 16;
+ n |= (buf[++offset] & 0xff) << 8;
+ n |= (buf[++offset] & 0xff);
+ return n;
+ }
+
+ public static long readUint32(InputStream input)
+ throws IOException
+ {
+ int i1 = input.read();
+ int i2 = input.read();
+ int i3 = input.read();
+ int i4 = input.read();
+ if (i4 < 0)
+ {
+ throw new EOFException();
+ }
+ return (((long)i1) << 24) | (((long)i2) << 16) | (((long)i3) << 8) | ((long)i4);
+ }
+
+ public static long readUint48(InputStream input)
+ throws IOException
+ {
+ int i1 = input.read();
+ int i2 = input.read();
+ int i3 = input.read();
+ int i4 = input.read();
+ int i5 = input.read();
+ int i6 = input.read();
+ if (i6 < 0)
+ {
+ throw new EOFException();
+ }
+ return (((long)i1) << 40) | (((long)i2) << 32) | (((long)i3) << 24) | (((long)i4) << 16) | (((long)i5) << 8) | ((long)i6);
+ }
+
+ public static long readUint48(byte[] buf, int offset)
+ {
+ int hi = readUint24(buf, offset);
+ int lo = readUint24(buf, offset + 3);
+ return ((long)(hi & 0xffffffffL) << 24) | (long)(lo & 0xffffffffL);
+ }
+
+ public static byte[] readFully(int length, InputStream input)
+ throws IOException
+ {
+ if (length < 1)
+ {
+ return EMPTY_BYTES;
+ }
+ byte[] buf = new byte[length];
+ if (length != Streams.readFully(input, buf))
+ {
+ throw new EOFException();
+ }
+ return buf;
+ }
+
+ public static void readFully(byte[] buf, InputStream input)
+ throws IOException
+ {
+ int length = buf.length;
+ if (length > 0 && length != Streams.readFully(input, buf))
+ {
+ throw new EOFException();
+ }
+ }
+
+ public static byte[] readOpaque8(InputStream input)
+ throws IOException
+ {
+ short length = readUint8(input);
+ return readFully(length, input);
+ }
+
+ public static byte[] readOpaque16(InputStream input)
+ throws IOException
+ {
+ int length = readUint16(input);
+ return readFully(length, input);
+ }
+
+ public static byte[] readOpaque24(InputStream input)
+ throws IOException
+ {
+ int length = readUint24(input);
+ return readFully(length, input);
+ }
+
+ public static short[] readUint8Array(int count, InputStream input)
+ throws IOException
+ {
+ short[] uints = new short[count];
+ for (int i = 0; i < count; ++i)
+ {
+ uints[i] = readUint8(input);
+ }
+ return uints;
+ }
+
+ public static int[] readUint16Array(int count, InputStream input)
+ throws IOException
+ {
+ int[] uints = new int[count];
+ for (int i = 0; i < count; ++i)
+ {
+ uints[i] = readUint16(input);
+ }
+ return uints;
+ }
+
+ public static ProtocolVersion readVersion(byte[] buf, int offset)
+ throws IOException
+ {
+ return ProtocolVersion.get(buf[offset] & 0xFF, buf[offset + 1] & 0xFF);
+ }
+
+ public static ProtocolVersion readVersion(InputStream input)
+ throws IOException
+ {
+ int i1 = input.read();
+ int i2 = input.read();
+ if (i2 < 0)
+ {
+ throw new EOFException();
+ }
+ return ProtocolVersion.get(i1, i2);
+ }
+
+ public static int readVersionRaw(InputStream input)
+ throws IOException
+ {
+ int i1 = input.read();
+ int i2 = input.read();
+ if (i2 < 0)
+ {
+ throw new EOFException();
+ }
+ return (i1 << 8) | i2;
+ }
+
+ public static void writeGMTUnixTime(byte[] buf, int offset)
+ {
+ int t = (int)(System.currentTimeMillis() / 1000L);
+ buf[offset] = (byte)(t >> 24);
+ buf[offset + 1] = (byte)(t >> 16);
+ buf[offset + 2] = (byte)(t >> 8);
+ buf[offset + 3] = (byte)t;
+ }
+
+ public static void writeVersion(ProtocolVersion version, OutputStream output)
+ throws IOException
+ {
+ output.write(version.getMajorVersion());
+ output.write(version.getMinorVersion());
+ }
+
+ public static void writeVersion(ProtocolVersion version, byte[] buf, int offset)
+ throws IOException
+ {
+ buf[offset] = (byte)version.getMajorVersion();
+ buf[offset + 1] = (byte)version.getMinorVersion();
+ }
+
+ public static Vector getDefaultDSSSignatureAlgorithms()
+ {
+ return vectorOfOne(new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.dsa));
+ }
+
+ public static Vector getDefaultECDSASignatureAlgorithms()
+ {
+ return vectorOfOne(new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.ecdsa));
+ }
+
+ public static Vector getDefaultRSASignatureAlgorithms()
+ {
+ return vectorOfOne(new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa));
+ }
+
+ public static boolean isSignatureAlgorithmsExtensionAllowed(ProtocolVersion clientVersion)
+ {
+ return ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(clientVersion.getEquivalentTLSVersion());
+ }
+
+ /**
+ * Add a 'signature_algorithms' extension to existing extensions.
+ *
+ * @param extensions A {@link Hashtable} to add the extension to.
+ * @param supportedSignatureAlgorithms {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}.
+ * @throws IOException
+ */
+ public static void addSignatureAlgorithmsExtension(Hashtable extensions, Vector supportedSignatureAlgorithms)
+ throws IOException
+ {
+ extensions.put(EXT_signature_algorithms, createSignatureAlgorithmsExtension(supportedSignatureAlgorithms));
+ }
+
+ /**
+ * Get a 'signature_algorithms' extension from extensions.
+ *
+ * @param extensions A {@link Hashtable} to get the extension from, if it is present.
+ * @return A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}, or null.
+ * @throws IOException
+ */
+ public static Vector getSignatureAlgorithmsExtension(Hashtable extensions)
+ throws IOException
+ {
+
+ if (extensions == null)
+ {
+ return null;
+ }
+ byte[] extensionValue = (byte[])extensions.get(EXT_signature_algorithms);
+ if (extensionValue == null)
+ {
+ return null;
+ }
+ return readSignatureAlgorithmsExtension(extensionValue);
+ }
+
+ /**
+ * Create a 'signature_algorithms' extension value.
+ *
+ * @param supportedSignatureAlgorithms A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}.
+ * @return A byte array suitable for use as an extension value.
+ * @throws IOException
+ */
+ public static byte[] createSignatureAlgorithmsExtension(Vector supportedSignatureAlgorithms)
+ throws IOException
+ {
+
+ if (supportedSignatureAlgorithms == null || supportedSignatureAlgorithms.size() < 1 || supportedSignatureAlgorithms.size() >= (1 << 15))
+ {
+ throw new IllegalArgumentException(
+ "'supportedSignatureAlgorithms' must have length from 1 to (2^15 - 1)");
+ }
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+
+ // supported_signature_algorithms
+ TlsUtils.writeUint16(2 * supportedSignatureAlgorithms.size(), buf);
+ for (int i = 0; i < supportedSignatureAlgorithms.size(); ++i)
+ {
+ SignatureAndHashAlgorithm entry = (SignatureAndHashAlgorithm)supportedSignatureAlgorithms.elementAt(i);
+ entry.encode(buf);
+ }
+
+ return buf.toByteArray();
+ }
+
+ /**
+ * Read a 'signature_algorithms' extension value.
+ *
+ * @param extensionValue The extension value.
+ * @return A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}.
+ * @throws IOException
+ */
+ public static Vector readSignatureAlgorithmsExtension(byte[] extensionValue)
+ throws IOException
+ {
+
+ if (extensionValue == null)
+ {
+ throw new IllegalArgumentException("'extensionValue' cannot be null");
+ }
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(extensionValue);
+
+ // supported_signature_algorithms
+ int length = TlsUtils.readUint16(buf);
+ if (length < 2 || (length & 1) != 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+ int count = length / 2;
+ Vector result = new Vector(count);
+ for (int i = 0; i < count; ++i)
+ {
+ SignatureAndHashAlgorithm entry = SignatureAndHashAlgorithm.parse(buf);
+ result.addElement(entry);
+ }
+
+ TlsProtocol.assertEmpty(buf);
+
+ return result;
+ }
+
+ public static byte[] PRF(TlsContext context, byte[] secret, String asciiLabel, byte[] seed, int size)
+ {
+ ProtocolVersion version = context.getServerVersion();
+
+ if (version.isSSL())
+ {
+ throw new IllegalStateException("No PRF available for SSLv3 session");
+ }
+
+ byte[] label = Strings.toByteArray(asciiLabel);
+ byte[] labelSeed = concat(label, seed);
+
+ int prfAlgorithm = context.getSecurityParameters().getPrfAlgorithm();
+
+ if (prfAlgorithm == PRFAlgorithm.tls_prf_legacy)
+ {
+ if (!ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(version.getEquivalentTLSVersion()))
+ {
+ return PRF_legacy(secret, label, labelSeed, size);
+ }
+
+ prfAlgorithm = PRFAlgorithm.tls_prf_sha256;
+ }
+
+ Digest prfDigest = createPRFHash(prfAlgorithm);
+ byte[] buf = new byte[size];
+ hmac_hash(prfDigest, secret, labelSeed, buf);
+ return buf;
+ }
+
+ static byte[] PRF_legacy(byte[] secret, byte[] label, byte[] labelSeed, int size)
+ {
+ int s_half = (secret.length + 1) / 2;
+ byte[] s1 = new byte[s_half];
+ byte[] s2 = new byte[s_half];
+ System.arraycopy(secret, 0, s1, 0, s_half);
+ System.arraycopy(secret, secret.length - s_half, s2, 0, s_half);
+
+ byte[] b1 = new byte[size];
+ byte[] b2 = new byte[size];
+ hmac_hash(new MD5Digest(), s1, labelSeed, b1);
+ hmac_hash(new SHA1Digest(), s2, labelSeed, b2);
+ for (int i = 0; i < size; i++)
+ {
+ b1[i] ^= b2[i];
+ }
+ return b1;
+ }
+
+ static byte[] concat(byte[] a, byte[] b)
+ {
+ byte[] c = new byte[a.length + b.length];
+ System.arraycopy(a, 0, c, 0, a.length);
+ System.arraycopy(b, 0, c, a.length, b.length);
+ return c;
+ }
+
+ static void hmac_hash(Digest digest, byte[] secret, byte[] seed, byte[] out)
+ {
+ HMac mac = new HMac(digest);
+ KeyParameter param = new KeyParameter(secret);
+ byte[] a = seed;
+ int size = digest.getDigestSize();
+ int iterations = (out.length + size - 1) / size;
+ byte[] buf = new byte[mac.getMacSize()];
+ byte[] buf2 = new byte[mac.getMacSize()];
+ for (int i = 0; i < iterations; i++)
+ {
+ mac.init(param);
+ mac.update(a, 0, a.length);
+ mac.doFinal(buf, 0);
+ a = buf;
+ mac.init(param);
+ mac.update(a, 0, a.length);
+ mac.update(seed, 0, seed.length);
+ mac.doFinal(buf2, 0);
+ System.arraycopy(buf2, 0, out, (size * i), Math.min(size, out.length - (size * i)));
+ }
+ }
+
+ static void validateKeyUsage(org.bouncycastle.asn1.x509.Certificate c, int keyUsageBits)
+ throws IOException
+ {
+ Extensions exts = c.getTBSCertificate().getExtensions();
+ if (exts != null)
+ {
+ KeyUsage ku = KeyUsage.fromExtensions(exts);
+ if (ku != null)
+ {
+ int bits = ku.getBytes()[0] & 0xff;
+ if ((bits & keyUsageBits) != keyUsageBits)
+ {
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ }
+ }
+ }
+ }
+
+ static byte[] calculateKeyBlock(TlsContext context, int size)
+ {
+ SecurityParameters securityParameters = context.getSecurityParameters();
+ byte[] master_secret = securityParameters.getMasterSecret();
+ byte[] seed = concat(securityParameters.getServerRandom(),
+ securityParameters.getClientRandom());
+
+ if (context.getServerVersion().isSSL())
+ {
+ return calculateKeyBlock_SSL(master_secret, seed, size);
+ }
+
+ return PRF(context, master_secret, ExporterLabel.key_expansion, seed, size);
+ }
+
+ static byte[] calculateKeyBlock_SSL(byte[] master_secret, byte[] random, int size)
+ {
+ Digest md5 = new MD5Digest();
+ Digest sha1 = new SHA1Digest();
+ int md5Size = md5.getDigestSize();
+ byte[] shatmp = new byte[sha1.getDigestSize()];
+ byte[] tmp = new byte[size + md5Size];
+
+ int i = 0, pos = 0;
+ while (pos < size)
+ {
+ byte[] ssl3Const = SSL3_CONST[i];
+
+ sha1.update(ssl3Const, 0, ssl3Const.length);
+ sha1.update(master_secret, 0, master_secret.length);
+ sha1.update(random, 0, random.length);
+ sha1.doFinal(shatmp, 0);
+
+ md5.update(master_secret, 0, master_secret.length);
+ md5.update(shatmp, 0, shatmp.length);
+ md5.doFinal(tmp, pos);
+
+ pos += md5Size;
+ ++i;
+ }
+
+ byte rval[] = new byte[size];
+ System.arraycopy(tmp, 0, rval, 0, size);
+ return rval;
+ }
+
+ static byte[] calculateMasterSecret(TlsContext context, byte[] pre_master_secret)
+ {
+ SecurityParameters securityParameters = context.getSecurityParameters();
+ byte[] seed = concat(securityParameters.getClientRandom(), securityParameters.getServerRandom());
+
+ if (context.getServerVersion().isSSL())
+ {
+ return calculateMasterSecret_SSL(pre_master_secret, seed);
+ }
+
+ return PRF(context, pre_master_secret, ExporterLabel.master_secret, seed, 48);
+ }
+
+ static byte[] calculateMasterSecret_SSL(byte[] pre_master_secret, byte[] random)
+ {
+ Digest md5 = new MD5Digest();
+ Digest sha1 = new SHA1Digest();
+ int md5Size = md5.getDigestSize();
+ byte[] shatmp = new byte[sha1.getDigestSize()];
+
+ byte[] rval = new byte[md5Size * 3];
+ int pos = 0;
+
+ for (int i = 0; i < 3; ++i)
+ {
+ byte[] ssl3Const = SSL3_CONST[i];
+
+ sha1.update(ssl3Const, 0, ssl3Const.length);
+ sha1.update(pre_master_secret, 0, pre_master_secret.length);
+ sha1.update(random, 0, random.length);
+ sha1.doFinal(shatmp, 0);
+
+ md5.update(pre_master_secret, 0, pre_master_secret.length);
+ md5.update(shatmp, 0, shatmp.length);
+ md5.doFinal(rval, pos);
+
+ pos += md5Size;
+ }
+
+ return rval;
+ }
+
+ static byte[] calculateVerifyData(TlsContext context, String asciiLabel, byte[] handshakeHash)
+ {
+ if (context.getServerVersion().isSSL())
+ {
+ return handshakeHash;
+ }
+
+ SecurityParameters securityParameters = context.getSecurityParameters();
+ byte[] master_secret = securityParameters.getMasterSecret();
+ int verify_data_length = securityParameters.getVerifyDataLength();
+
+ return PRF(context, master_secret, asciiLabel, handshakeHash, verify_data_length);
+ }
+
+ public static final Digest createHash(int hashAlgorithm)
+ {
+ switch (hashAlgorithm)
+ {
+ case HashAlgorithm.md5:
+ return new MD5Digest();
+ case HashAlgorithm.sha1:
+ return new SHA1Digest();
+ case HashAlgorithm.sha224:
+ return new SHA224Digest();
+ case HashAlgorithm.sha256:
+ return new SHA256Digest();
+ case HashAlgorithm.sha384:
+ return new SHA384Digest();
+ case HashAlgorithm.sha512:
+ return new SHA512Digest();
+ default:
+ throw new IllegalArgumentException("unknown HashAlgorithm");
+ }
+ }
+
+ public static final Digest cloneHash(int hashAlgorithm, Digest hash)
+ {
+ switch (hashAlgorithm)
+ {
+ case HashAlgorithm.md5:
+ return new MD5Digest((MD5Digest)hash);
+ case HashAlgorithm.sha1:
+ return new SHA1Digest((SHA1Digest)hash);
+ case HashAlgorithm.sha224:
+ return new SHA224Digest((SHA224Digest)hash);
+ case HashAlgorithm.sha256:
+ return new SHA256Digest((SHA256Digest)hash);
+ case HashAlgorithm.sha384:
+ return new SHA384Digest((SHA384Digest)hash);
+ case HashAlgorithm.sha512:
+ return new SHA512Digest((SHA512Digest)hash);
+ default:
+ throw new IllegalArgumentException("unknown HashAlgorithm");
+ }
+ }
+
+ public static final Digest createPRFHash(int prfAlgorithm)
+ {
+ switch (prfAlgorithm)
+ {
+ case PRFAlgorithm.tls_prf_legacy:
+ return new CombinedHash();
+ default:
+ return createHash(getHashAlgorithmForPRFAlgorithm(prfAlgorithm));
+ }
+ }
+
+ public static final Digest clonePRFHash(int prfAlgorithm, Digest hash)
+ {
+ switch (prfAlgorithm)
+ {
+ case PRFAlgorithm.tls_prf_legacy:
+ return new CombinedHash((CombinedHash)hash);
+ default:
+ return cloneHash(getHashAlgorithmForPRFAlgorithm(prfAlgorithm), hash);
+ }
+ }
+
+ public static final short getHashAlgorithmForPRFAlgorithm(int prfAlgorithm)
+ {
+ switch (prfAlgorithm)
+ {
+ case PRFAlgorithm.tls_prf_legacy:
+ throw new IllegalArgumentException("legacy PRF not a valid algorithm");
+ case PRFAlgorithm.tls_prf_sha256:
+ return HashAlgorithm.sha256;
+ case PRFAlgorithm.tls_prf_sha384:
+ return HashAlgorithm.sha384;
+ default:
+ throw new IllegalArgumentException("unknown PRFAlgorithm");
+ }
+ }
+
+ public static ASN1ObjectIdentifier getOIDForHashAlgorithm(int hashAlgorithm)
+ {
+ switch (hashAlgorithm)
+ {
+ case HashAlgorithm.md5:
+ return PKCSObjectIdentifiers.md5;
+ case HashAlgorithm.sha1:
+ return X509ObjectIdentifiers.id_SHA1;
+ case HashAlgorithm.sha224:
+ return NISTObjectIdentifiers.id_sha224;
+ case HashAlgorithm.sha256:
+ return NISTObjectIdentifiers.id_sha256;
+ case HashAlgorithm.sha384:
+ return NISTObjectIdentifiers.id_sha384;
+ case HashAlgorithm.sha512:
+ return NISTObjectIdentifiers.id_sha512;
+ default:
+ throw new IllegalArgumentException("unknown HashAlgorithm");
+ }
+ }
+
+ static short getClientCertificateType(Certificate clientCertificate, Certificate serverCertificate)
+ throws IOException
+ {
+ if (clientCertificate.isEmpty())
+ {
+ return -1;
+ }
+
+ org.bouncycastle.asn1.x509.Certificate x509Cert = clientCertificate.getCertificateAt(0);
+ SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo();
+ try
+ {
+ AsymmetricKeyParameter publicKey = PublicKeyFactory.createKey(keyInfo);
+ if (publicKey.isPrivate())
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ /*
+ * TODO RFC 5246 7.4.6. The certificates MUST be signed using an acceptable hash/
+ * signature algorithm pair, as described in Section 7.4.4. Note that this relaxes the
+ * constraints on certificate-signing algorithms found in prior versions of TLS.
+ */
+
+ /*
+ * RFC 5246 7.4.6. Client Certificate
+ */
+
+ /*
+ * RSA public key; the certificate MUST allow the key to be used for signing with the
+ * signature scheme and hash algorithm that will be employed in the certificate verify
+ * message.
+ */
+ if (publicKey instanceof RSAKeyParameters)
+ {
+ validateKeyUsage(x509Cert, KeyUsage.digitalSignature);
+ return ClientCertificateType.rsa_sign;
+ }
+
+ /*
+ * DSA public key; the certificate MUST allow the key to be used for signing with the
+ * hash algorithm that will be employed in the certificate verify message.
+ */
+ if (publicKey instanceof DSAPublicKeyParameters)
+ {
+ validateKeyUsage(x509Cert, KeyUsage.digitalSignature);
+ return ClientCertificateType.dss_sign;
+ }
+
+ /*
+ * ECDSA-capable public key; the certificate MUST allow the key to be used for signing
+ * with the hash algorithm that will be employed in the certificate verify message; the
+ * public key MUST use a curve and point format supported by the server.
+ */
+ if (publicKey instanceof ECPublicKeyParameters)
+ {
+ validateKeyUsage(x509Cert, KeyUsage.digitalSignature);
+ // TODO Check the curve and point format
+ return ClientCertificateType.ecdsa_sign;
+ }
+
+ // TODO Add support for ClientCertificateType.*_fixed_*
+
+ }
+ catch (Exception e)
+ {
+ }
+
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ }
+
+ public static boolean hasSigningCapability(short clientCertificateType)
+ {
+ switch (clientCertificateType)
+ {
+ case ClientCertificateType.dss_sign:
+ case ClientCertificateType.ecdsa_sign:
+ case ClientCertificateType.rsa_sign:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public static TlsSigner createTlsSigner(short clientCertificateType)
+ {
+ switch (clientCertificateType)
+ {
+ case ClientCertificateType.dss_sign:
+ return new TlsDSSSigner();
+ case ClientCertificateType.ecdsa_sign:
+ return new TlsECDSASigner();
+ case ClientCertificateType.rsa_sign:
+ return new TlsRSASigner();
+ default:
+ throw new IllegalArgumentException("'clientCertificateType' is not a type with signing capability");
+ }
+ }
+
+ static final byte[] SSL_CLIENT = {0x43, 0x4C, 0x4E, 0x54};
+ static final byte[] SSL_SERVER = {0x53, 0x52, 0x56, 0x52};
+
+ // SSL3 magic mix constants ("A", "BB", "CCC", ...)
+ static final byte[][] SSL3_CONST = genConst();
+
+ private static byte[][] genConst()
+ {
+ int n = 10;
+ byte[][] arr = new byte[n][];
+ for (int i = 0; i < n; i++)
+ {
+ byte[] b = new byte[i + 1];
+ Arrays.fill(b, (byte)('A' + i));
+ arr[i] = b;
+ }
+ return arr;
+ }
+
+ private static Vector vectorOfOne(Object obj)
+ {
+ Vector v = new Vector(1);
+ v.addElement(obj);
+ return v;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/UDPTransport.java b/core/src/main/java/org/bouncycastle/crypto/tls/UDPTransport.java
new file mode 100644
index 00000000..f3dd59ee
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/UDPTransport.java
@@ -0,0 +1,77 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+
+public class UDPTransport
+ implements DatagramTransport
+{
+
+ private final static int MIN_IP_OVERHEAD = 20;
+ private final static int MAX_IP_OVERHEAD = MIN_IP_OVERHEAD + 64;
+ private final static int UDP_OVERHEAD = 8;
+
+ private final DatagramSocket socket;
+ private final int receiveLimit, sendLimit;
+
+ public UDPTransport(DatagramSocket socket, int mtu)
+ throws IOException
+ {
+
+ if (!socket.isBound() || !socket.isConnected())
+ {
+ throw new IllegalArgumentException("'socket' must be bound and connected");
+ }
+
+ this.socket = socket;
+
+ // NOTE: As of JDK 1.6, can use NetworkInterface.getMTU
+
+ this.receiveLimit = mtu - MIN_IP_OVERHEAD - UDP_OVERHEAD;
+ this.sendLimit = mtu - MAX_IP_OVERHEAD - UDP_OVERHEAD;
+ }
+
+ public int getReceiveLimit()
+ {
+ return receiveLimit;
+ }
+
+ public int getSendLimit()
+ {
+ // TODO[DTLS] Implement Path-MTU discovery?
+ return sendLimit;
+ }
+
+ public int receive(byte[] buf, int off, int len, int waitMillis)
+ throws IOException
+ {
+ socket.setSoTimeout(waitMillis);
+ DatagramPacket packet = new DatagramPacket(buf, off, len);
+ socket.receive(packet);
+ return packet.getLength();
+ }
+
+ public void send(byte[] buf, int off, int len)
+ throws IOException
+ {
+ if (len > getSendLimit())
+ {
+ /*
+ * RFC 4347 4.1.1. "If the application attempts to send a record larger than the MTU,
+ * the DTLS implementation SHOULD generate an error, thus avoiding sending a packet
+ * which will be fragmented."
+ */
+ // TODO Exception
+ }
+
+ DatagramPacket packet = new DatagramPacket(buf, off, len);
+ socket.send(packet);
+ }
+
+ public void close()
+ throws IOException
+ {
+ socket.close();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/UseSRTPData.java b/core/src/main/java/org/bouncycastle/crypto/tls/UseSRTPData.java
new file mode 100644
index 00000000..8ecfce00
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/UseSRTPData.java
@@ -0,0 +1,54 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 5764 4.1.1
+ */
+public class UseSRTPData
+{
+
+ private int[] protectionProfiles;
+ private byte[] mki;
+
+ /**
+ * @param protectionProfiles see {@link SRTPProtectionProfile} for valid constants.
+ * @param mki valid lengths from 0 to 255.
+ */
+ public UseSRTPData(int[] protectionProfiles, byte[] mki)
+ {
+
+ if (protectionProfiles == null || protectionProfiles.length < 1
+ || protectionProfiles.length >= (1 << 15))
+ {
+ throw new IllegalArgumentException(
+ "'protectionProfiles' must have length from 1 to (2^15 - 1)");
+ }
+
+ if (mki == null)
+ {
+ mki = TlsUtils.EMPTY_BYTES;
+ }
+ else if (mki.length > 255)
+ {
+ throw new IllegalArgumentException("'mki' cannot be longer than 255 bytes");
+ }
+
+ this.protectionProfiles = protectionProfiles;
+ this.mki = mki;
+ }
+
+ /**
+ * @return see {@link SRTPProtectionProfile} for valid constants.
+ */
+ public int[] getProtectionProfiles()
+ {
+ return protectionProfiles;
+ }
+
+ /**
+ * @return valid lengths from 0 to 255.
+ */
+ public byte[] getMki()
+ {
+ return mki;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/UserMappingType.java b/core/src/main/java/org/bouncycastle/crypto/tls/UserMappingType.java
new file mode 100644
index 00000000..8f6ae7ba
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/tls/UserMappingType.java
@@ -0,0 +1,12 @@
+package org.bouncycastle.crypto.tls;
+
+/**
+ * RFC 4681
+ */
+public class UserMappingType
+{
+ /*
+ * RFC 4681
+ */
+ public static final short upn_domain_hint = 64;
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/util/Pack.java b/core/src/main/java/org/bouncycastle/crypto/util/Pack.java
new file mode 100644
index 00000000..f0da0bf0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/util/Pack.java
@@ -0,0 +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;
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java b/core/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java
new file mode 100644
index 00000000..bfa304b2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java
@@ -0,0 +1,170 @@
+package org.bouncycastle.crypto.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.nist.NISTNamedCurves;
+import org.bouncycastle.asn1.oiw.ElGamalParameter;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.DHParameter;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.pkcs.RSAPrivateKey;
+import org.bouncycastle.asn1.sec.ECPrivateKey;
+import org.bouncycastle.asn1.sec.SECNamedCurves;
+import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.DSAParameter;
+import org.bouncycastle.asn1.x9.X962NamedCurves;
+import org.bouncycastle.asn1.x9.X962Parameters;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
+import org.bouncycastle.crypto.params.DSAParameters;
+import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ElGamalParameters;
+import org.bouncycastle.crypto.params.ElGamalPrivateKeyParameters;
+import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+
+/**
+ * Factory for creating private key objects from PKCS8 PrivateKeyInfo objects.
+ */
+public class PrivateKeyFactory
+{
+ /**
+ * Create a private key parameter from a PKCS8 PrivateKeyInfo encoding.
+ *
+ * @param privateKeyInfoData the PrivateKeyInfo encoding
+ * @return a suitable private key parameter
+ * @throws IOException on an error decoding the key
+ */
+ public static AsymmetricKeyParameter createKey(byte[] privateKeyInfoData) throws IOException
+ {
+ return createKey(PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(privateKeyInfoData)));
+ }
+
+ /**
+ * Create a private key parameter from a PKCS8 PrivateKeyInfo encoding read from a
+ * stream.
+ *
+ * @param inStr the stream to read the PrivateKeyInfo encoding from
+ * @return a suitable private key parameter
+ * @throws IOException on an error decoding the key
+ */
+ public static AsymmetricKeyParameter createKey(InputStream inStr) throws IOException
+ {
+ return createKey(PrivateKeyInfo.getInstance(new ASN1InputStream(inStr).readObject()));
+ }
+
+ /**
+ * Create a private key parameter from the passed in PKCS8 PrivateKeyInfo object.
+ *
+ * @param keyInfo the PrivateKeyInfo object containing the key material
+ * @return a suitable private key parameter
+ * @throws IOException on an error decoding the key
+ */
+ public static AsymmetricKeyParameter createKey(PrivateKeyInfo keyInfo) throws IOException
+ {
+ AlgorithmIdentifier algId = keyInfo.getPrivateKeyAlgorithm();
+
+ if (algId.getAlgorithm().equals(PKCSObjectIdentifiers.rsaEncryption))
+ {
+ RSAPrivateKey keyStructure = RSAPrivateKey.getInstance(keyInfo.parsePrivateKey());
+
+ return new RSAPrivateCrtKeyParameters(keyStructure.getModulus(),
+ keyStructure.getPublicExponent(), keyStructure.getPrivateExponent(),
+ keyStructure.getPrime1(), keyStructure.getPrime2(), keyStructure.getExponent1(),
+ keyStructure.getExponent2(), keyStructure.getCoefficient());
+ }
+ // TODO?
+// else if (algId.getObjectId().equals(X9ObjectIdentifiers.dhpublicnumber))
+ else if (algId.getAlgorithm().equals(PKCSObjectIdentifiers.dhKeyAgreement))
+ {
+ DHParameter params = DHParameter.getInstance(algId.getParameters());
+ ASN1Integer derX = (ASN1Integer)keyInfo.parsePrivateKey();
+
+ BigInteger lVal = params.getL();
+ int l = lVal == null ? 0 : lVal.intValue();
+ DHParameters dhParams = new DHParameters(params.getP(), params.getG(), null, l);
+
+ return new DHPrivateKeyParameters(derX.getValue(), dhParams);
+ }
+ else if (algId.getAlgorithm().equals(OIWObjectIdentifiers.elGamalAlgorithm))
+ {
+ ElGamalParameter params = new ElGamalParameter((ASN1Sequence)algId.getParameters());
+ ASN1Integer derX = (ASN1Integer)keyInfo.parsePrivateKey();
+
+ return new ElGamalPrivateKeyParameters(derX.getValue(), new ElGamalParameters(
+ params.getP(), params.getG()));
+ }
+ else if (algId.getAlgorithm().equals(X9ObjectIdentifiers.id_dsa))
+ {
+ ASN1Integer derX = (ASN1Integer)keyInfo.parsePrivateKey();
+ ASN1Encodable de = algId.getParameters();
+
+ DSAParameters parameters = null;
+ if (de != null)
+ {
+ DSAParameter params = DSAParameter.getInstance(de.toASN1Primitive());
+ parameters = new DSAParameters(params.getP(), params.getQ(), params.getG());
+ }
+
+ return new DSAPrivateKeyParameters(derX.getValue(), parameters);
+ }
+ else if (algId.getAlgorithm().equals(X9ObjectIdentifiers.id_ecPublicKey))
+ {
+ X962Parameters params = new X962Parameters((ASN1Primitive)algId.getParameters());
+
+ X9ECParameters x9;
+ if (params.isNamedCurve())
+ {
+ ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters());
+ x9 = X962NamedCurves.getByOID(oid);
+
+ if (x9 == null)
+ {
+ x9 = SECNamedCurves.getByOID(oid);
+
+ if (x9 == null)
+ {
+ x9 = NISTNamedCurves.getByOID(oid);
+
+ if (x9 == null)
+ {
+ x9 = TeleTrusTNamedCurves.getByOID(oid);
+ }
+ }
+ }
+ }
+ else
+ {
+ x9 = X9ECParameters.getInstance(params.getParameters());
+ }
+
+ ECPrivateKey ec = ECPrivateKey.getInstance(keyInfo.parsePrivateKey());
+ BigInteger d = ec.getKey();
+
+ // TODO We lose any named parameters here
+
+ ECDomainParameters dParams = new ECDomainParameters(
+ x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
+
+ return new ECPrivateKeyParameters(d, dParams);
+ }
+ else
+ {
+ throw new RuntimeException("algorithm identifier in key not recognised");
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java b/core/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java
new file mode 100644
index 00000000..ab52802c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java
@@ -0,0 +1,51 @@
+package org.bouncycastle.crypto.util;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.pkcs.RSAPrivateKey;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.DSAParameter;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.DSAParameters;
+import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+
+/**
+ * Factory to create ASN.1 private key info objects from lightweight private keys.
+ */
+public class PrivateKeyInfoFactory
+{
+ /**
+ * Create a PrivateKeyInfo representation of a private key.
+ *
+ * @param privateKey the SubjectPublicKeyInfo encoding
+ * @return the appropriate key parameter
+ * @throws java.io.IOException on an error encoding the key
+ */
+ public static PrivateKeyInfo createPrivateKeyInfo(AsymmetricKeyParameter privateKey) throws IOException
+ {
+ if (privateKey instanceof RSAKeyParameters)
+ {
+ RSAPrivateCrtKeyParameters priv = (RSAPrivateCrtKeyParameters)privateKey;
+
+ return new PrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new RSAPrivateKey(priv.getModulus(), priv.getPublicExponent(), priv.getExponent(), priv.getP(), priv.getQ(), priv.getDP(), priv.getDQ(), priv.getQInv()));
+ }
+ else if (privateKey instanceof DSAPrivateKeyParameters)
+ {
+ DSAPrivateKeyParameters priv = (DSAPrivateKeyParameters)privateKey;
+ DSAParameters params = priv.getParameters();
+
+ return new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa, new DSAParameter(params.getP(), params.getQ(), params.getG())), new ASN1Integer(priv.getX()));
+ }
+ else
+ {
+ throw new IOException("key parameters not recognised.");
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java b/core/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java
new file mode 100644
index 00000000..343bbd3c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java
@@ -0,0 +1,207 @@
+package org.bouncycastle.crypto.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.nist.NISTNamedCurves;
+import org.bouncycastle.asn1.oiw.ElGamalParameter;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.DHParameter;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.RSAPublicKey;
+import org.bouncycastle.asn1.sec.SECNamedCurves;
+import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.DSAParameter;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
+import org.bouncycastle.asn1.x9.DHDomainParameters;
+import org.bouncycastle.asn1.x9.DHPublicKey;
+import org.bouncycastle.asn1.x9.DHValidationParms;
+import org.bouncycastle.asn1.x9.X962NamedCurves;
+import org.bouncycastle.asn1.x9.X962Parameters;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.asn1.x9.X9ECPoint;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+import org.bouncycastle.crypto.params.DHValidationParameters;
+import org.bouncycastle.crypto.params.DSAParameters;
+import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.ElGamalParameters;
+import org.bouncycastle.crypto.params.ElGamalPublicKeyParameters;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+
+/**
+ * Factory to create asymmetric public key parameters for asymmetric ciphers from range of
+ * ASN.1 encoded SubjectPublicKeyInfo objects.
+ */
+public class PublicKeyFactory
+{
+ /**
+ * Create a public key from a SubjectPublicKeyInfo encoding
+ *
+ * @param keyInfoData the SubjectPublicKeyInfo encoding
+ * @return the appropriate key parameter
+ * @throws IOException on an error decoding the key
+ */
+ public static AsymmetricKeyParameter createKey(byte[] keyInfoData) throws IOException
+ {
+ return createKey(SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(keyInfoData)));
+ }
+
+ /**
+ * Create a public key from a SubjectPublicKeyInfo encoding read from a stream
+ *
+ * @param inStr the stream to read the SubjectPublicKeyInfo encoding from
+ * @return the appropriate key parameter
+ * @throws IOException on an error decoding the key
+ */
+ public static AsymmetricKeyParameter createKey(InputStream inStr) throws IOException
+ {
+ return createKey(SubjectPublicKeyInfo.getInstance(new ASN1InputStream(inStr).readObject()));
+ }
+
+ /**
+ * Create a public key from the passed in SubjectPublicKeyInfo
+ *
+ * @param keyInfo the SubjectPublicKeyInfo containing the key data
+ * @return the appropriate key parameter
+ * @throws IOException on an error decoding the key
+ */
+ public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo) throws IOException
+ {
+ AlgorithmIdentifier algId = keyInfo.getAlgorithm();
+
+ if (algId.getAlgorithm().equals(PKCSObjectIdentifiers.rsaEncryption)
+ || algId.getAlgorithm().equals(X509ObjectIdentifiers.id_ea_rsa))
+ {
+ RSAPublicKey pubKey = RSAPublicKey.getInstance(keyInfo.parsePublicKey());
+
+ return new RSAKeyParameters(false, pubKey.getModulus(), pubKey.getPublicExponent());
+ }
+ else if (algId.getAlgorithm().equals(X9ObjectIdentifiers.dhpublicnumber))
+ {
+ DHPublicKey dhPublicKey = DHPublicKey.getInstance(keyInfo.parsePublicKey());
+
+ BigInteger y = dhPublicKey.getY().getValue();
+
+ DHDomainParameters dhParams = DHDomainParameters.getInstance(algId.getParameters());
+
+ BigInteger p = dhParams.getP().getValue();
+ BigInteger g = dhParams.getG().getValue();
+ BigInteger q = dhParams.getQ().getValue();
+
+ BigInteger j = null;
+ if (dhParams.getJ() != null)
+ {
+ j = dhParams.getJ().getValue();
+ }
+
+ DHValidationParameters validation = null;
+ DHValidationParms dhValidationParms = dhParams.getValidationParms();
+ if (dhValidationParms != null)
+ {
+ byte[] seed = dhValidationParms.getSeed().getBytes();
+ BigInteger pgenCounter = dhValidationParms.getPgenCounter().getValue();
+
+ // TODO Check pgenCounter size?
+
+ validation = new DHValidationParameters(seed, pgenCounter.intValue());
+ }
+
+ return new DHPublicKeyParameters(y, new DHParameters(p, g, q, j, validation));
+ }
+ else if (algId.getAlgorithm().equals(PKCSObjectIdentifiers.dhKeyAgreement))
+ {
+ DHParameter params = DHParameter.getInstance(algId.getParameters());
+ ASN1Integer derY = (ASN1Integer)keyInfo.parsePublicKey();
+
+ BigInteger lVal = params.getL();
+ int l = lVal == null ? 0 : lVal.intValue();
+ DHParameters dhParams = new DHParameters(params.getP(), params.getG(), null, l);
+
+ return new DHPublicKeyParameters(derY.getValue(), dhParams);
+ }
+ else if (algId.getAlgorithm().equals(OIWObjectIdentifiers.elGamalAlgorithm))
+ {
+ ElGamalParameter params = new ElGamalParameter((ASN1Sequence)algId.getParameters());
+ ASN1Integer derY = (ASN1Integer)keyInfo.parsePublicKey();
+
+ return new ElGamalPublicKeyParameters(derY.getValue(), new ElGamalParameters(
+ params.getP(), params.getG()));
+ }
+ else if (algId.getAlgorithm().equals(X9ObjectIdentifiers.id_dsa)
+ || algId.getAlgorithm().equals(OIWObjectIdentifiers.dsaWithSHA1))
+ {
+ ASN1Integer derY = (ASN1Integer)keyInfo.parsePublicKey();
+ ASN1Encodable de = algId.getParameters();
+
+ DSAParameters parameters = null;
+ if (de != null)
+ {
+ DSAParameter params = DSAParameter.getInstance(de.toASN1Primitive());
+ parameters = new DSAParameters(params.getP(), params.getQ(), params.getG());
+ }
+
+ return new DSAPublicKeyParameters(derY.getValue(), parameters);
+ }
+ else if (algId.getAlgorithm().equals(X9ObjectIdentifiers.id_ecPublicKey))
+ {
+ X962Parameters params = new X962Parameters(
+ (ASN1Primitive)algId.getParameters());
+
+ X9ECParameters x9;
+ if (params.isNamedCurve())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters();
+ x9 = X962NamedCurves.getByOID(oid);
+
+ if (x9 == null)
+ {
+ x9 = SECNamedCurves.getByOID(oid);
+
+ if (x9 == null)
+ {
+ x9 = NISTNamedCurves.getByOID(oid);
+
+ if (x9 == null)
+ {
+ x9 = TeleTrusTNamedCurves.getByOID(oid);
+ }
+ }
+ }
+ }
+ else
+ {
+ x9 = X9ECParameters.getInstance(params.getParameters());
+ }
+
+ ASN1OctetString key = new DEROctetString(keyInfo.getPublicKeyData().getBytes());
+ X9ECPoint derQ = new X9ECPoint(x9.getCurve(), key);
+
+ // TODO We lose any named parameters here
+
+ ECDomainParameters dParams = new ECDomainParameters(
+ x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
+
+ return new ECPublicKeyParameters(derQ.getPoint(), dParams);
+ }
+ else
+ {
+ throw new RuntimeException("algorithm identifier in key not recognised");
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java b/core/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java
new file mode 100644
index 00000000..bdc6cbd1
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java
@@ -0,0 +1,81 @@
+package org.bouncycastle.crypto.util;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.RSAPublicKey;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x9.X962Parameters;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.asn1.x9.X9ECPoint;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+
+/**
+ * Factory to create ASN.1 subject public key info objects from lightweight public keys.
+ */
+public class SubjectPublicKeyInfoFactory
+{
+ /**
+ * Create a SubjectPublicKeyInfo public key.
+ *
+ * @param publicKey the SubjectPublicKeyInfo encoding
+ * @return the appropriate key parameter
+ * @throws java.io.IOException on an error encoding the key
+ */
+ public static SubjectPublicKeyInfo createSubjectPublicKeyInfo(AsymmetricKeyParameter publicKey) throws IOException
+ {
+ if (publicKey instanceof RSAKeyParameters)
+ {
+ RSAKeyParameters pub = (RSAKeyParameters)publicKey;
+
+ return new SubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new RSAPublicKey(pub.getModulus(), pub.getExponent()));
+ }
+ else if (publicKey instanceof DSAPublicKeyParameters)
+ {
+ DSAPublicKeyParameters pub = (DSAPublicKeyParameters)publicKey;
+
+ return new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa), new ASN1Integer(pub.getY()));
+ }
+ else if (publicKey instanceof ECPublicKeyParameters)
+ {
+ ECPublicKeyParameters pub = (ECPublicKeyParameters)publicKey;
+ ECDomainParameters domainParams = pub.getParameters();
+ ASN1Encodable params;
+
+ // TODO: need to handle named curves
+ if (domainParams == null)
+ {
+ params = new X962Parameters(DERNull.INSTANCE); // Implicitly CA
+ }
+ else
+ {
+ X9ECParameters ecP = new X9ECParameters(
+ domainParams.getCurve(),
+ domainParams.getG(),
+ domainParams.getN(),
+ domainParams.getH(),
+ domainParams.getSeed());
+
+ params = new X962Parameters(ecP);
+ }
+
+ ASN1OctetString p = (ASN1OctetString)new X9ECPoint(pub.getQ()).toASN1Primitive();
+
+ return new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets());
+ }
+ else
+ {
+ throw new IOException("key parameters not recognised.");
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/i18n/ErrorBundle.java b/core/src/main/java/org/bouncycastle/i18n/ErrorBundle.java
new file mode 100644
index 00000000..415b5e57
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/i18n/ErrorBundle.java
@@ -0,0 +1,120 @@
+package org.bouncycastle.i18n;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Locale;
+import java.util.TimeZone;
+
+public class ErrorBundle extends MessageBundle
+{
+
+ /**
+ * summary entry key
+ */
+ public static final String SUMMARY_ENTRY = "summary";
+
+ /**
+ * detail entry key
+ */
+ public static final String DETAIL_ENTRY = "details";
+
+ /**
+ * Constructs a new ErrorBundle using <code>resource</code> as the base name for the
+ * RessourceBundle and <code>id</code> as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
+ */
+ public ErrorBundle(String resource, String id) throws NullPointerException
+ {
+ super(resource, id);
+ }
+
+ /**
+ * Constructs a new ErrorBundle using <code>resource</code> as the base name for the
+ * RessourceBundle and <code>id</code> as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param encoding the encoding of the resource file
+ * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ public ErrorBundle(String resource, String id, String encoding) throws NullPointerException, UnsupportedEncodingException
+ {
+ super(resource, id, encoding);
+ }
+
+ /**
+ * Constructs a new ErrorBundle using <code>resource</code> as the base name for the
+ * RessourceBundle and <code>id</code> as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param arguments an array containing the arguments for the message
+ * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
+ */
+ public ErrorBundle(String resource, String id, Object[] arguments) throws NullPointerException
+ {
+ super(resource, id, arguments);
+ }
+
+ /**
+ * Constructs a new ErrorBundle using <code>resource</code> as the base name for the
+ * RessourceBundle and <code>id</code> as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param encoding the encoding of the resource file
+ * @param arguments an array containing the arguments for the message
+ * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ public ErrorBundle(String resource, String id, String encoding, Object[] arguments) throws NullPointerException, UnsupportedEncodingException
+ {
+ super(resource, id, encoding, arguments);
+ }
+
+ /**
+ * Returns the summary message in the given locale and timezone.
+ * @param loc the {@link Locale}
+ * @param timezone the {@link TimeZone}
+ * @return the summary message.
+ * @throws MissingEntryException if the message is not available
+ */
+ public String getSummary(Locale loc, TimeZone timezone) throws MissingEntryException
+ {
+ return getEntry(SUMMARY_ENTRY,loc,timezone);
+ }
+
+ /**
+ * Returns the summary message in the given locale and the default timezone.
+ * @param loc the {@link Locale}
+ * @return the summary message.
+ * @throws MissingEntryException if the message is not available
+ */
+ public String getSummary(Locale loc) throws MissingEntryException
+ {
+ return getEntry(SUMMARY_ENTRY,loc,TimeZone.getDefault());
+ }
+
+ /**
+ * Returns the detail message in the given locale and timezone.
+ * @param loc the {@link Locale}
+ * @param timezone the {@link TimeZone}
+ * @return the detail message.
+ * @throws MissingEntryException if the message is not available
+ */
+ public String getDetail(Locale loc, TimeZone timezone) throws MissingEntryException
+ {
+ return getEntry(DETAIL_ENTRY,loc,timezone);
+ }
+
+ /**
+ * Returns the detail message in the given locale and the default timezone.
+ * @param loc the {@link Locale}
+ * @return the detail message.
+ * @throws MissingEntryException if the message is not available
+ */
+ public String getDetail(Locale loc) throws MissingEntryException
+ {
+ return getEntry(DETAIL_ENTRY,loc,TimeZone.getDefault());
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/i18n/LocaleString.java b/core/src/main/java/org/bouncycastle/i18n/LocaleString.java
new file mode 100644
index 00000000..b9e22320
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/i18n/LocaleString.java
@@ -0,0 +1,24 @@
+package org.bouncycastle.i18n;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Locale;
+
+public class LocaleString extends LocalizedMessage
+{
+
+ public LocaleString(String resource, String id)
+ {
+ super(resource, id);
+ }
+
+ public LocaleString(String resource, String id, String encoding) throws NullPointerException, UnsupportedEncodingException
+ {
+ super(resource, id, encoding);
+ }
+
+ public String getLocaleString(Locale locale)
+ {
+ return this.getEntry(null, locale, null);
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/i18n/LocalizedException.java b/core/src/main/java/org/bouncycastle/i18n/LocalizedException.java
new file mode 100644
index 00000000..373fd6ce
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/i18n/LocalizedException.java
@@ -0,0 +1,49 @@
+package org.bouncycastle.i18n;
+
+import java.util.Locale;
+
+/**
+ * Base class for all Exceptions with localized messages.
+ */
+public class LocalizedException extends Exception
+{
+
+ protected ErrorBundle message;
+ private Throwable cause;
+
+ /**
+ * Constructs a new LocalizedException with the specified localized message.
+ * @param message the {@link ErrorBundle} that contains the message for the exception
+ */
+ public LocalizedException(ErrorBundle message)
+ {
+ super(message.getText(Locale.getDefault()));
+ this.message = message;
+ }
+
+ /**
+ * Constructs a new LocalizedException with the specified localized message and cause.
+ * @param message the {@link ErrorBundle} that contains the message for the exception
+ * @param throwable the cause
+ */
+ public LocalizedException(ErrorBundle message, Throwable throwable)
+ {
+ super(message.getText(Locale.getDefault()));
+ this.message = message;
+ this.cause = throwable;
+ }
+
+ /**
+ * Returns the localized error message of the exception.
+ * @return the localized error message as {@link ErrorBundle}
+ */
+ public ErrorBundle getErrorMessage()
+ {
+ return message;
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/i18n/LocalizedMessage.java b/core/src/main/java/org/bouncycastle/i18n/LocalizedMessage.java
new file mode 100644
index 00000000..d88c2291
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/i18n/LocalizedMessage.java
@@ -0,0 +1,476 @@
+package org.bouncycastle.i18n;
+
+import org.bouncycastle.i18n.filter.Filter;
+import org.bouncycastle.i18n.filter.TrustedInput;
+import org.bouncycastle.i18n.filter.UntrustedInput;
+import org.bouncycastle.i18n.filter.UntrustedUrlInput;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.text.DateFormat;
+import java.text.Format;
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.TimeZone;
+
+public class LocalizedMessage
+{
+
+ protected final String id;
+ protected final String resource;
+
+ // ISO-8859-1 is the default encoding
+ public static final String DEFAULT_ENCODING = "ISO-8859-1";
+ protected String encoding = DEFAULT_ENCODING;
+
+ protected FilteredArguments arguments;
+ protected FilteredArguments extraArgs = null;
+
+ protected Filter filter = null;
+
+ protected ClassLoader loader = null;
+
+ /**
+ * Constructs a new LocalizedMessage using <code>resource</code> as the base name for the
+ * RessourceBundle and <code>id</code> as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
+ */
+ public LocalizedMessage(String resource,String id) throws NullPointerException
+ {
+ if (resource == null || id == null)
+ {
+ throw new NullPointerException();
+ }
+ this.id = id;
+ this.resource = resource;
+ arguments = new FilteredArguments();
+ }
+
+ /**
+ * Constructs a new LocalizedMessage using <code>resource</code> as the base name for the
+ * RessourceBundle and <code>id</code> as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param encoding the encoding of the resource file
+ * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ public LocalizedMessage(String resource,String id, String encoding) throws NullPointerException, UnsupportedEncodingException
+ {
+ if (resource == null || id == null)
+ {
+ throw new NullPointerException();
+ }
+ this.id = id;
+ this.resource = resource;
+ arguments = new FilteredArguments();
+ if (!Charset.isSupported(encoding))
+ {
+ throw new UnsupportedEncodingException("The encoding \"" + encoding + "\" is not supported.");
+ }
+ this.encoding = encoding;
+ }
+
+ /**
+ * Constructs a new LocalizedMessage using <code>resource</code> as the base name for the
+ * RessourceBundle and <code>id</code> as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param arguments an array containing the arguments for the message
+ * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
+ */
+ public LocalizedMessage(String resource, String id, Object[] arguments) throws NullPointerException
+ {
+ if (resource == null || id == null || arguments == null)
+ {
+ throw new NullPointerException();
+ }
+ this.id = id;
+ this.resource = resource;
+ this.arguments = new FilteredArguments(arguments);
+ }
+
+ /**
+ * Constructs a new LocalizedMessage using <code>resource</code> as the base name for the
+ * RessourceBundle and <code>id</code> as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param encoding the encoding of the resource file
+ * @param arguments an array containing the arguments for the message
+ * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ public LocalizedMessage(String resource, String id, String encoding, Object[] arguments) throws NullPointerException, UnsupportedEncodingException
+ {
+ if (resource == null || id == null || arguments == null)
+ {
+ throw new NullPointerException();
+ }
+ this.id = id;
+ this.resource = resource;
+ this.arguments = new FilteredArguments(arguments);
+ if (!Charset.isSupported(encoding))
+ {
+ throw new UnsupportedEncodingException("The encoding \"" + encoding + "\" is not supported.");
+ }
+ this.encoding = encoding;
+ }
+
+ /**
+ * Reads the entry <code>id + "." + key</code> from the resource file and returns a
+ * formated message for the given Locale and TimeZone.
+ * @param key second part of the entry id
+ * @param loc the used {@link Locale}
+ * @param timezone the used {@link TimeZone}
+ * @return a Strng containing the localized message
+ * @throws MissingEntryException if the resource file is not available or the entry does not exist.
+ */
+ public String getEntry(String key,Locale loc, TimeZone timezone) throws MissingEntryException
+ {
+ String entry = id;
+ if (key != null)
+ {
+ entry += "." + key;
+ }
+
+ try
+ {
+ ResourceBundle bundle;
+ if (loader == null)
+ {
+ bundle = ResourceBundle.getBundle(resource,loc);
+ }
+ else
+ {
+ bundle = ResourceBundle.getBundle(resource, loc, loader);
+ }
+ String result = bundle.getString(entry);
+ if (!encoding.equals(DEFAULT_ENCODING))
+ {
+ result = new String(result.getBytes(DEFAULT_ENCODING), encoding);
+ }
+ if (!arguments.isEmpty())
+ {
+ result = formatWithTimeZone(result,arguments.getFilteredArgs(loc),loc,timezone);
+ }
+ result = addExtraArgs(result, loc);
+ return result;
+ }
+ catch (MissingResourceException mre)
+ {
+ throw new MissingEntryException("Can't find entry " + entry + " in resource file " + resource + ".",
+ resource,
+ entry,
+ loc,
+ loader != null ? loader : this.getClassLoader());
+ }
+ catch (UnsupportedEncodingException use)
+ {
+ // should never occur - cause we already test this in the constructor
+ throw new RuntimeException(use);
+ }
+ }
+
+ protected String formatWithTimeZone(
+ String template,
+ Object[] arguments,
+ Locale locale,
+ TimeZone timezone)
+ {
+ MessageFormat mf = new MessageFormat(" ");
+ mf.setLocale(locale);
+ mf.applyPattern(template);
+ if (!timezone.equals(TimeZone.getDefault()))
+ {
+ Format[] formats = mf.getFormats();
+ for (int i = 0; i < formats.length; i++)
+ {
+ if (formats[i] instanceof DateFormat)
+ {
+ DateFormat temp = (DateFormat) formats[i];
+ temp.setTimeZone(timezone);
+ mf.setFormat(i,temp);
+ }
+ }
+ }
+ return mf.format(arguments);
+ }
+
+ protected String addExtraArgs(String msg, Locale locale)
+ {
+ if (extraArgs != null)
+ {
+ StringBuffer sb = new StringBuffer(msg);
+ Object[] filteredArgs = extraArgs.getFilteredArgs(locale);
+ for (int i = 0; i < filteredArgs.length; i++)
+ {
+ sb.append(filteredArgs[i]);
+ }
+ msg = sb.toString();
+ }
+ return msg;
+ }
+
+ /**
+ * Sets the {@link Filter} that is used to filter the arguments of this message
+ * @param filter the {@link Filter} to use. <code>null</code> to disable filtering.
+ */
+ public void setFilter(Filter filter)
+ {
+ arguments.setFilter(filter);
+ if (extraArgs != null)
+ {
+ extraArgs.setFilter(filter);
+ }
+ this.filter = filter;
+ }
+
+ /**
+ * Returns the current filter.
+ * @return the current filter
+ */
+ public Filter getFilter()
+ {
+ return filter;
+ }
+
+ /**
+ * Set the {@link ClassLoader} which loads the resource files. If it is set to <code>null</code>
+ * then the default {@link ClassLoader} is used.
+ * @param loader the {@link ClassLoader} which loads the resource files
+ */
+ public void setClassLoader(ClassLoader loader)
+ {
+ this.loader = loader;
+ }
+
+ /**
+ * Returns the {@link ClassLoader} which loads the resource files or <code>null</code>
+ * if the default ClassLoader is used.
+ * @return the {@link ClassLoader} which loads the resource files
+ */
+ public ClassLoader getClassLoader()
+ {
+ return loader;
+ }
+
+ /**
+ * Returns the id of the message in the resource bundle.
+ * @return the id of the message
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+ /**
+ * Returns the name of the resource bundle for this message
+ * @return name of the resource file
+ */
+ public String getResource()
+ {
+ return resource;
+ }
+
+ /**
+ * Returns an <code>Object[]</code> containing the message arguments.
+ * @return the message arguments
+ */
+ public Object[] getArguments()
+ {
+ return arguments.getArguments();
+ }
+
+ /**
+ *
+ * @param extraArg
+ */
+ public void setExtraArgument(Object extraArg)
+ {
+ setExtraArguments(new Object[] {extraArg});
+ }
+
+ /**
+ *
+ * @param extraArgs
+ */
+ public void setExtraArguments(Object[] extraArgs)
+ {
+ if (extraArgs != null)
+ {
+ this.extraArgs = new FilteredArguments(extraArgs);
+ this.extraArgs.setFilter(filter);
+ }
+ else
+ {
+ this.extraArgs = null;
+ }
+ }
+
+ /**
+ *
+ * @return
+ */
+ public Object[] getExtraArgs()
+ {
+ return (extraArgs == null) ? null : extraArgs.getArguments();
+ }
+
+ protected class FilteredArguments
+ {
+ protected static final int NO_FILTER = 0;
+ protected static final int FILTER = 1;
+ protected static final int FILTER_URL = 2;
+
+ protected Filter filter = null;
+
+ protected boolean[] isLocaleSpecific;
+ protected int[] argFilterType;
+ protected Object[] arguments;
+ protected Object[] unpackedArgs;
+ protected Object[] filteredArgs;
+
+ FilteredArguments()
+ {
+ this(new Object[0]);
+ }
+
+ FilteredArguments(Object[] args)
+ {
+ this.arguments = args;
+ this.unpackedArgs = new Object[args.length];
+ this.filteredArgs = new Object[args.length];
+ this.isLocaleSpecific = new boolean[args.length];
+ this.argFilterType = new int[args.length];
+ for (int i = 0; i < args.length; i++)
+ {
+ if (args[i] instanceof TrustedInput)
+ {
+ this.unpackedArgs[i] = ((TrustedInput) args[i]).getInput();
+ argFilterType[i] = NO_FILTER;
+ }
+ else if (args[i] instanceof UntrustedInput)
+ {
+ this.unpackedArgs[i] = ((UntrustedInput) args[i]).getInput();
+ if (args[i] instanceof UntrustedUrlInput)
+ {
+ argFilterType[i] = FILTER_URL;
+ }
+ else
+ {
+ argFilterType[i] = FILTER;
+ }
+ }
+ else
+ {
+ this.unpackedArgs[i] = args[i];
+ argFilterType[i] = FILTER;
+ }
+
+ // locale specific
+ this.isLocaleSpecific[i] = (this.unpackedArgs[i] instanceof LocaleString);
+ }
+ }
+
+ public boolean isEmpty()
+ {
+ return unpackedArgs.length == 0;
+ }
+
+ public Object[] getArguments()
+ {
+ return arguments;
+ }
+
+ public Object[] getFilteredArgs(Locale locale)
+ {
+ Object[] result = new Object[unpackedArgs.length];
+ for (int i = 0; i < unpackedArgs.length; i++)
+ {
+ Object arg;
+ if (filteredArgs[i] != null)
+ {
+ arg = filteredArgs[i];
+ }
+ else
+ {
+ arg = unpackedArgs[i];
+ if (isLocaleSpecific[i])
+ {
+ // get locale
+ arg = ((LocaleString) arg).getLocaleString(locale);
+ arg = filter(argFilterType[i], arg);
+ }
+ else
+ {
+ arg = filter(argFilterType[i], arg);
+ filteredArgs[i] = arg;
+ }
+ }
+ result[i] = arg;
+ }
+ return result;
+ }
+
+ private Object filter(int type, Object obj)
+ {
+ if (filter != null)
+ {
+ Object o = (null == obj) ? "null" : obj;
+ switch (type)
+ {
+ case NO_FILTER:
+ return o;
+ case FILTER:
+ return filter.doFilter(o.toString());
+ case FILTER_URL:
+ return filter.doFilterUrl(o.toString());
+ default:
+ return null;
+ }
+ }
+ else
+ {
+ return obj;
+ }
+ }
+
+ public Filter getFilter()
+ {
+ return filter;
+ }
+
+ public void setFilter(Filter filter)
+ {
+ if (filter != this.filter)
+ {
+ for (int i = 0; i < unpackedArgs.length; i++)
+ {
+ filteredArgs[i] = null;
+ }
+ }
+ this.filter = filter;
+ }
+
+ }
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("Resource: \"").append(resource);
+ sb.append("\" Id: \"").append(id).append("\"");
+ sb.append(" Arguments: ").append(arguments.getArguments().length).append(" normal");
+ if (extraArgs != null && extraArgs.getArguments().length > 0)
+ {
+ sb.append(", ").append(extraArgs.getArguments().length).append(" extra");
+ }
+ sb.append(" Encoding: ").append(encoding);
+ sb.append(" ClassLoader: ").append(loader);
+ return sb.toString();
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/i18n/MessageBundle.java b/core/src/main/java/org/bouncycastle/i18n/MessageBundle.java
new file mode 100644
index 00000000..de931fce
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/i18n/MessageBundle.java
@@ -0,0 +1,92 @@
+package org.bouncycastle.i18n;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Locale;
+import java.util.TimeZone;
+
+public class MessageBundle extends TextBundle
+{
+
+ /**
+ * title entry key
+ */
+ public static final String TITLE_ENTRY = "title";
+
+ /**
+ * Constructs a new MessageBundle using <code>resource</code> as the base name for the
+ * RessourceBundle and <code>id</code> as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
+ */
+ public MessageBundle(String resource, String id) throws NullPointerException
+ {
+ super(resource, id);
+ }
+
+ /**
+ * Constructs a new MessageBundle using <code>resource</code> as the base name for the
+ * RessourceBundle and <code>id</code> as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param encoding the encoding of the resource file
+ * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ public MessageBundle(String resource, String id, String encoding) throws NullPointerException, UnsupportedEncodingException
+ {
+ super(resource, id, encoding);
+ }
+
+ /**
+ * Constructs a new MessageBundle using <code>resource</code> as the base name for the
+ * RessourceBundle and <code>id</code> as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param arguments an array containing the arguments for the message
+ * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
+ */
+ public MessageBundle(String resource, String id, Object[] arguments) throws NullPointerException
+ {
+ super(resource, id, arguments);
+ }
+
+ /**
+ * Constructs a new MessageBundle using <code>resource</code> as the base name for the
+ * RessourceBundle and <code>id</code> as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param encoding the encoding of the resource file
+ * @param arguments an array containing the arguments for the message
+ * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ public MessageBundle(String resource, String id, String encoding, Object[] arguments) throws NullPointerException, UnsupportedEncodingException
+ {
+ super(resource, id, encoding, arguments);
+ }
+
+ /**
+ * Returns the title message in the given locale and timezone.
+ * @param loc the {@link Locale}
+ * @param timezone the {@link TimeZone}
+ * @return the title message.
+ * @throws MissingEntryException if the message is not available
+ */
+ public String getTitle(Locale loc,TimeZone timezone) throws MissingEntryException
+ {
+ return getEntry(TITLE_ENTRY,loc,timezone);
+ }
+
+ /**
+ * Returns the title message in the given locale and the default timezone.
+ * @param loc the {@link Locale}
+ * @return the title message.
+ * @throws MissingEntryException if the message is not available
+ */
+ public String getTitle(Locale loc) throws MissingEntryException
+ {
+ return getEntry(TITLE_ENTRY,loc,TimeZone.getDefault());
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/i18n/MissingEntryException.java b/core/src/main/java/org/bouncycastle/i18n/MissingEntryException.java
new file mode 100644
index 00000000..ffd4d543
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/i18n/MissingEntryException.java
@@ -0,0 +1,73 @@
+package org.bouncycastle.i18n;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Locale;
+
+public class MissingEntryException extends RuntimeException
+{
+
+ protected final String resource;
+ protected final String key;
+ protected final ClassLoader loader;
+ protected final Locale locale;
+
+ private String debugMsg;
+
+ public MissingEntryException(String message, String resource, String key, Locale locale, ClassLoader loader)
+ {
+ super(message);
+ this.resource = resource;
+ this.key = key;
+ this.locale = locale;
+ this.loader = loader;
+ }
+
+ public MissingEntryException(String message, Throwable cause, String resource, String key, Locale locale, ClassLoader loader)
+ {
+ super(message, cause);
+ this.resource = resource;
+ this.key = key;
+ this.locale = locale;
+ this.loader = loader;
+ }
+
+ public String getKey()
+ {
+ return key;
+ }
+
+ public String getResource()
+ {
+ return resource;
+ }
+
+ public ClassLoader getClassLoader()
+ {
+ return loader;
+ }
+
+ public Locale getLocale()
+ {
+ return locale;
+ }
+
+ public String getDebugMsg()
+ {
+ if (debugMsg == null)
+ {
+ debugMsg = "Can not find entry " + key + " in resource file " + resource + " for the locale " + locale + ".";
+ if (loader instanceof URLClassLoader)
+ {
+ URL[] urls = ((URLClassLoader) loader).getURLs();
+ debugMsg += " The following entries in the classpath were searched: ";
+ for (int i = 0; i != urls.length; i++)
+ {
+ debugMsg += urls[i] + " ";
+ }
+ }
+ }
+ return debugMsg;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/i18n/TextBundle.java b/core/src/main/java/org/bouncycastle/i18n/TextBundle.java
new file mode 100644
index 00000000..d77e8417
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/i18n/TextBundle.java
@@ -0,0 +1,92 @@
+package org.bouncycastle.i18n;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Locale;
+import java.util.TimeZone;
+
+public class TextBundle extends LocalizedMessage
+{
+
+ /**
+ * text entry key
+ */
+ public static final String TEXT_ENTRY = "text";
+
+ /**
+ * Constructs a new TextBundle using <code>resource</code> as the base name for the
+ * RessourceBundle and <code>id</code> as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
+ */
+ public TextBundle(String resource, String id) throws NullPointerException
+ {
+ super(resource, id);
+ }
+
+ /**
+ * Constructs a new TextBundle using <code>resource</code> as the base name for the
+ * RessourceBundle and <code>id</code> as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param encoding the encoding of the resource file
+ * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ public TextBundle(String resource, String id, String encoding) throws NullPointerException, UnsupportedEncodingException
+ {
+ super(resource, id, encoding);
+ }
+
+ /**
+ * Constructs a new TextBundle using <code>resource</code> as the base name for the
+ * RessourceBundle and <code>id</code> as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param arguments an array containing the arguments for the message
+ * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
+ */
+ public TextBundle(String resource, String id, Object[] arguments) throws NullPointerException
+ {
+ super(resource, id, arguments);
+ }
+
+ /**
+ * Constructs a new TextBundle using <code>resource</code> as the base name for the
+ * RessourceBundle and <code>id</code> as the message bundle id the resource file.
+ * @param resource base name of the resource file
+ * @param id the id of the corresponding bundle in the resource file
+ * @param encoding the encoding of the resource file
+ * @param arguments an array containing the arguments for the message
+ * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ public TextBundle(String resource, String id, String encoding, Object[] arguments) throws NullPointerException, UnsupportedEncodingException
+ {
+ super(resource, id, encoding, arguments);
+ }
+
+ /**
+ * Returns the text message in the given locale and timezone.
+ * @param loc the {@link Locale}
+ * @param timezone the {@link TimeZone}
+ * @return the text message.
+ * @throws MissingEntryException if the message is not available
+ */
+ public String getText(Locale loc, TimeZone timezone) throws MissingEntryException
+ {
+ return getEntry(TEXT_ENTRY,loc,timezone);
+ }
+
+ /**
+ * Returns the text message in the given locale and the defaut timezone.
+ * @param loc the {@link Locale}
+ * @return the text message.
+ * @throws MissingEntryException if the message is not available
+ */
+ public String getText(Locale loc) throws MissingEntryException
+ {
+ return getEntry(TEXT_ENTRY,loc,TimeZone.getDefault());
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/i18n/filter/Filter.java b/core/src/main/java/org/bouncycastle/i18n/filter/Filter.java
new file mode 100644
index 00000000..fc86aaa4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/i18n/filter/Filter.java
@@ -0,0 +1,21 @@
+
+package org.bouncycastle.i18n.filter;
+
+public interface Filter
+{
+
+ /**
+ * Runs the filter on the input String and returns the filtered String
+ * @param input input String
+ * @return filtered String
+ */
+ public String doFilter(String input);
+
+ /**
+ * Runs the filter on the input url and returns the filtered String
+ * @param input input url String
+ * @return filtered String
+ */
+ public String doFilterUrl(String input);
+
+}
diff --git a/core/src/main/java/org/bouncycastle/i18n/filter/HTMLFilter.java b/core/src/main/java/org/bouncycastle/i18n/filter/HTMLFilter.java
new file mode 100644
index 00000000..b9904bc7
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/i18n/filter/HTMLFilter.java
@@ -0,0 +1,68 @@
+
+package org.bouncycastle.i18n.filter;
+
+/**
+ * HTML Filter
+ */
+public class HTMLFilter implements Filter
+{
+
+ public String doFilter(String input)
+ {
+ StringBuffer buf = new StringBuffer(input);
+ int i = 0;
+ while (i < buf.length())
+ {
+ char ch = buf.charAt(i);
+ switch (ch)
+ {
+ case '<':
+ buf.replace(i,i+1,"&#60");
+ break;
+ case '>':
+ buf.replace(i,i+1,"&#62");
+ break;
+ case '(':
+ buf.replace(i,i+1,"&#40");
+ break;
+ case ')':
+ buf.replace(i,i+1,"&#41");
+ break;
+ case '#':
+ buf.replace(i,i+1,"&#35");
+ break;
+ case '&':
+ buf.replace(i,i+1,"&#38");
+ break;
+ case '\"':
+ buf.replace(i,i+1,"&#34");
+ break;
+ case '\'':
+ buf.replace(i,i+1,"&#39");
+ break;
+ case '%':
+ buf.replace(i,i+1,"&#37");
+ break;
+ case ';':
+ buf.replace(i,i+1,"&#59");
+ break;
+ case '+':
+ buf.replace(i,i+1,"&#43");
+ break;
+ case '-':
+ buf.replace(i,i+1,"&#45");
+ break;
+ default:
+ i -= 3;
+ }
+ i += 4;
+ }
+ return buf.toString();
+ }
+
+ public String doFilterUrl(String input)
+ {
+ return doFilter(input);
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/i18n/filter/SQLFilter.java b/core/src/main/java/org/bouncycastle/i18n/filter/SQLFilter.java
new file mode 100644
index 00000000..d55610b6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/i18n/filter/SQLFilter.java
@@ -0,0 +1,69 @@
+
+package org.bouncycastle.i18n.filter;
+
+/**
+ * Filter for strings to store in a SQL table.
+ *
+ * escapes ' " = - / \ ; \r \n
+ */
+public class SQLFilter implements Filter
+{
+
+ public String doFilter(String input)
+ {
+ StringBuffer buf = new StringBuffer(input);
+ int i = 0;
+ while (i < buf.length())
+ {
+ char ch = buf.charAt(i);
+ switch (ch)
+ {
+ case '\'':
+ buf.replace(i,i+1,"\\\'");
+ i += 1;
+ break;
+ case '\"':
+ buf.replace(i,i+1,"\\\"");
+ i += 1;
+ break;
+ case '=':
+ buf.replace(i,i+1,"\\=");
+ i += 1;
+ break;
+ case '-':
+ buf.replace(i,i+1,"\\-");
+ i += 1;
+ break;
+ case '/':
+ buf.replace(i,i+1,"\\/");
+ i += 1;
+ break;
+ case '\\':
+ buf.replace(i,i+1,"\\\\");
+ i += 1;
+ break;
+ case ';':
+ buf.replace(i,i+1,"\\;");
+ i += 1;
+ break;
+ case '\r':
+ buf.replace(i,i+1,"\\r");
+ i += 1;
+ break;
+ case '\n':
+ buf.replace(i,i+1,"\\n");
+ i += 1;
+ break;
+ default:
+ }
+ i++;
+ }
+ return buf.toString();
+ }
+
+ public String doFilterUrl(String input)
+ {
+ return doFilter(input);
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/i18n/filter/TrustedInput.java b/core/src/main/java/org/bouncycastle/i18n/filter/TrustedInput.java
new file mode 100644
index 00000000..0feeeea7
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/i18n/filter/TrustedInput.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.i18n.filter;
+
+public class TrustedInput
+{
+
+ protected Object input;
+
+ public TrustedInput(Object input)
+ {
+ this.input = input;
+ }
+
+ public Object getInput()
+ {
+ return input;
+ }
+
+ public String toString()
+ {
+ return input.toString();
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/i18n/filter/UntrustedInput.java b/core/src/main/java/org/bouncycastle/i18n/filter/UntrustedInput.java
new file mode 100644
index 00000000..cc69ac49
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/i18n/filter/UntrustedInput.java
@@ -0,0 +1,44 @@
+
+package org.bouncycastle.i18n.filter;
+
+/**
+ * Wrapper class to mark untrusted input.
+ */
+public class UntrustedInput
+{
+
+ protected Object input;
+
+ /**
+ * Construct a new UntrustedInput instance.
+ * @param input the untrusted input Object
+ */
+ public UntrustedInput(Object input)
+ {
+ this.input = input;
+ }
+
+ /**
+ * Returns the untrusted input as Object.
+ * @return the <code>input</code> as Object
+ */
+ public Object getInput()
+ {
+ return input;
+ }
+
+ /**
+ * Returns the untrusted input convertet to a String.
+ * @return the <code>input</code> as String
+ */
+ public String getString()
+ {
+ return input.toString();
+ }
+
+ public String toString()
+ {
+ return input.toString();
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/i18n/filter/UntrustedUrlInput.java b/core/src/main/java/org/bouncycastle/i18n/filter/UntrustedUrlInput.java
new file mode 100644
index 00000000..98ee1e72
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/i18n/filter/UntrustedUrlInput.java
@@ -0,0 +1,14 @@
+package org.bouncycastle.i18n.filter;
+
+/**
+ *
+ * Wrapper class to mark an untrusted Url
+ */
+public class UntrustedUrlInput extends UntrustedInput
+{
+ public UntrustedUrlInput(Object url)
+ {
+ super(url);
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/math/ec/ECAlgorithms.java b/core/src/main/java/org/bouncycastle/math/ec/ECAlgorithms.java
new file mode 100644
index 00000000..78a7a8f9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/math/ec/ECAlgorithms.java
@@ -0,0 +1,92 @@
+package org.bouncycastle.math.ec;
+
+import java.math.BigInteger;
+
+public class ECAlgorithms
+{
+ public static ECPoint sumOfTwoMultiplies(ECPoint P, BigInteger a,
+ ECPoint Q, BigInteger b)
+ {
+ ECCurve c = P.getCurve();
+ if (!c.equals(Q.getCurve()))
+ {
+ throw new IllegalArgumentException("P and Q must be on same curve");
+ }
+
+ // Point multiplication for Koblitz curves (using WTNAF) beats Shamir's trick
+ if (c instanceof ECCurve.F2m)
+ {
+ ECCurve.F2m f2mCurve = (ECCurve.F2m)c;
+ if (f2mCurve.isKoblitz())
+ {
+ return P.multiply(a).add(Q.multiply(b));
+ }
+ }
+
+ return implShamirsTrick(P, a, Q, b);
+ }
+
+ /*
+ * "Shamir's Trick", originally due to E. G. Straus
+ * (Addition chains of vectors. American Mathematical Monthly,
+ * 71(7):806-808, Aug./Sept. 1964)
+ * <pre>
+ * Input: The points P, Q, scalar k = (km?, ... , k1, k0)
+ * and scalar l = (lm?, ... , l1, l0).
+ * Output: R = k * P + l * Q.
+ * 1: Z <- P + Q
+ * 2: R <- O
+ * 3: for i from m-1 down to 0 do
+ * 4: R <- R + R {point doubling}
+ * 5: if (ki = 1) and (li = 0) then R <- R + P end if
+ * 6: if (ki = 0) and (li = 1) then R <- R + Q end if
+ * 7: if (ki = 1) and (li = 1) then R <- R + Z end if
+ * 8: end for
+ * 9: return R
+ * </pre>
+ */
+ public static ECPoint shamirsTrick(ECPoint P, BigInteger k,
+ ECPoint Q, BigInteger l)
+ {
+ if (!P.getCurve().equals(Q.getCurve()))
+ {
+ throw new IllegalArgumentException("P and Q must be on same curve");
+ }
+
+ return implShamirsTrick(P, k, Q, l);
+ }
+
+ private static ECPoint implShamirsTrick(ECPoint P, BigInteger k,
+ ECPoint Q, BigInteger l)
+ {
+ int m = Math.max(k.bitLength(), l.bitLength());
+ ECPoint Z = P.add(Q);
+ ECPoint R = P.getCurve().getInfinity();
+
+ for (int i = m - 1; i >= 0; --i)
+ {
+ R = R.twice();
+
+ if (k.testBit(i))
+ {
+ if (l.testBit(i))
+ {
+ R = R.add(Z);
+ }
+ else
+ {
+ R = R.add(P);
+ }
+ }
+ else
+ {
+ if (l.testBit(i))
+ {
+ R = R.add(Q);
+ }
+ }
+ }
+
+ return R;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/math/ec/ECConstants.java b/core/src/main/java/org/bouncycastle/math/ec/ECConstants.java
new file mode 100644
index 00000000..864f746d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/math/ec/ECConstants.java
@@ -0,0 +1,12 @@
+package org.bouncycastle.math.ec;
+
+import java.math.BigInteger;
+
+public interface ECConstants
+{
+ public static final BigInteger ZERO = BigInteger.valueOf(0);
+ public static final BigInteger ONE = BigInteger.valueOf(1);
+ public static final BigInteger TWO = BigInteger.valueOf(2);
+ public static final BigInteger THREE = BigInteger.valueOf(3);
+ public static final BigInteger FOUR = BigInteger.valueOf(4);
+}
diff --git a/core/src/main/java/org/bouncycastle/math/ec/ECCurve.java b/core/src/main/java/org/bouncycastle/math/ec/ECCurve.java
new file mode 100644
index 00000000..58281af7
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/math/ec/ECCurve.java
@@ -0,0 +1,621 @@
+package org.bouncycastle.math.ec;
+
+import java.math.BigInteger;
+import java.util.Random;
+
+/**
+ * base class for an elliptic curve
+ */
+public abstract class ECCurve
+{
+ ECFieldElement a, b;
+
+ public abstract int getFieldSize();
+
+ public abstract ECFieldElement fromBigInteger(BigInteger x);
+
+ public abstract ECPoint createPoint(BigInteger x, BigInteger y, boolean withCompression);
+
+ public abstract ECPoint getInfinity();
+
+ public ECFieldElement getA()
+ {
+ return a;
+ }
+
+ public ECFieldElement getB()
+ {
+ return b;
+ }
+
+ protected abstract ECPoint decompressPoint(int yTilde, BigInteger X1);
+
+ /**
+ * Decode a point on this curve from its ASN.1 encoding. The different
+ * encodings are taken account of, including point compression for
+ * <code>F<sub>p</sub></code> (X9.62 s 4.2.1 pg 17).
+ * @return The decoded point.
+ */
+ public ECPoint decodePoint(byte[] encoded)
+ {
+ ECPoint p = null;
+ int expectedLength = (getFieldSize() + 7) / 8;
+
+ switch (encoded[0])
+ {
+ case 0x00: // infinity
+ {
+ if (encoded.length != 1)
+ {
+ throw new IllegalArgumentException("Incorrect length for infinity encoding");
+ }
+
+ p = getInfinity();
+ break;
+ }
+ case 0x02: // compressed
+ case 0x03: // compressed
+ {
+ if (encoded.length != (expectedLength + 1))
+ {
+ throw new IllegalArgumentException("Incorrect length for compressed encoding");
+ }
+
+ int yTilde = encoded[0] & 1;
+ BigInteger X1 = fromArray(encoded, 1, expectedLength);
+
+ p = decompressPoint(yTilde, X1);
+ break;
+ }
+ case 0x04: // uncompressed
+ case 0x06: // hybrid
+ case 0x07: // hybrid
+ {
+ if (encoded.length != (2 * expectedLength + 1))
+ {
+ throw new IllegalArgumentException("Incorrect length for uncompressed/hybrid encoding");
+ }
+
+ BigInteger X1 = fromArray(encoded, 1, expectedLength);
+ BigInteger Y1 = fromArray(encoded, 1 + expectedLength, expectedLength);
+
+ p = createPoint(X1, Y1, false);
+ break;
+ }
+ default:
+ throw new IllegalArgumentException("Invalid point encoding 0x" + Integer.toString(encoded[0], 16));
+ }
+
+ return p;
+ }
+
+ private static BigInteger fromArray(byte[] buf, int off, int length)
+ {
+ byte[] mag = new byte[length];
+ System.arraycopy(buf, off, mag, 0, length);
+ return new BigInteger(1, mag);
+ }
+
+ /**
+ * Elliptic curve over Fp
+ */
+ public static class Fp extends ECCurve
+ {
+ BigInteger q;
+ ECPoint.Fp infinity;
+
+ public Fp(BigInteger q, BigInteger a, BigInteger b)
+ {
+ this.q = q;
+ this.a = fromBigInteger(a);
+ this.b = fromBigInteger(b);
+ this.infinity = new ECPoint.Fp(this, null, null);
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public int getFieldSize()
+ {
+ return q.bitLength();
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new ECFieldElement.Fp(this.q, x);
+ }
+
+ public ECPoint createPoint(BigInteger x, BigInteger y, boolean withCompression)
+ {
+ return new ECPoint.Fp(this, fromBigInteger(x), fromBigInteger(y), withCompression);
+ }
+
+ protected ECPoint decompressPoint(int yTilde, BigInteger X1)
+ {
+ ECFieldElement x = fromBigInteger(X1);
+ ECFieldElement alpha = x.multiply(x.square().add(a)).add(b);
+ ECFieldElement beta = alpha.sqrt();
+
+ //
+ // if we can't find a sqrt we haven't got a point on the
+ // curve - run!
+ //
+ if (beta == null)
+ {
+ throw new RuntimeException("Invalid point compression");
+ }
+
+ BigInteger betaValue = beta.toBigInteger();
+ int bit0 = betaValue.testBit(0) ? 1 : 0;
+
+ if (bit0 != yTilde)
+ {
+ // Use the other root
+ beta = fromBigInteger(q.subtract(betaValue));
+ }
+
+ return new ECPoint.Fp(this, x, beta, true);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+
+ public boolean equals(
+ Object anObject)
+ {
+ if (anObject == this)
+ {
+ return true;
+ }
+
+ if (!(anObject instanceof ECCurve.Fp))
+ {
+ return false;
+ }
+
+ ECCurve.Fp other = (ECCurve.Fp) anObject;
+
+ return this.q.equals(other.q)
+ && a.equals(other.a) && b.equals(other.b);
+ }
+
+ public int hashCode()
+ {
+ return a.hashCode() ^ b.hashCode() ^ q.hashCode();
+ }
+ }
+
+ /**
+ * Elliptic curves over F2m. The Weierstrass equation is given by
+ * <code>y<sup>2</sup> + xy = x<sup>3</sup> + ax<sup>2</sup> + b</code>.
+ */
+ public static class F2m extends ECCurve
+ {
+ /**
+ * The exponent <code>m</code> of <code>F<sub>2<sup>m</sup></sub></code>.
+ */
+ private int m; // can't be final - JDK 1.1
+
+ /**
+ * TPB: The integer <code>k</code> where <code>x<sup>m</sup> +
+ * x<sup>k</sup> + 1</code> represents the reduction polynomial
+ * <code>f(z)</code>.<br>
+ * PPB: The integer <code>k1</code> where <code>x<sup>m</sup> +
+ * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+ * represents the reduction polynomial <code>f(z)</code>.<br>
+ */
+ private int k1; // can't be final - JDK 1.1
+
+ /**
+ * TPB: Always set to <code>0</code><br>
+ * PPB: The integer <code>k2</code> where <code>x<sup>m</sup> +
+ * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+ * represents the reduction polynomial <code>f(z)</code>.<br>
+ */
+ private int k2; // can't be final - JDK 1.1
+
+ /**
+ * TPB: Always set to <code>0</code><br>
+ * PPB: The integer <code>k3</code> where <code>x<sup>m</sup> +
+ * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+ * represents the reduction polynomial <code>f(z)</code>.<br>
+ */
+ private int k3; // can't be final - JDK 1.1
+
+ /**
+ * The order of the base point of the curve.
+ */
+ private BigInteger n; // can't be final - JDK 1.1
+
+ /**
+ * The cofactor of the curve.
+ */
+ private BigInteger h; // can't be final - JDK 1.1
+
+ /**
+ * The point at infinity on this curve.
+ */
+ private ECPoint.F2m infinity; // can't be final - JDK 1.1
+
+ /**
+ * The parameter <code>&mu;</code> of the elliptic curve if this is
+ * a Koblitz curve.
+ */
+ private byte mu = 0;
+
+ /**
+ * The auxiliary values <code>s<sub>0</sub></code> and
+ * <code>s<sub>1</sub></code> used for partial modular reduction for
+ * Koblitz curves.
+ */
+ private BigInteger[] si = null;
+
+ /**
+ * Constructor for Trinomial Polynomial Basis (TPB).
+ * @param m The exponent <code>m</code> of
+ * <code>F<sub>2<sup>m</sup></sub></code>.
+ * @param k The integer <code>k</code> where <code>x<sup>m</sup> +
+ * x<sup>k</sup> + 1</code> represents the reduction
+ * polynomial <code>f(z)</code>.
+ * @param a The coefficient <code>a</code> in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * <code>F<sub>2<sup>m</sup></sub></code>.
+ * @param b The coefficient <code>b</code> in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * <code>F<sub>2<sup>m</sup></sub></code>.
+ */
+ public F2m(
+ int m,
+ int k,
+ BigInteger a,
+ BigInteger b)
+ {
+ this(m, k, 0, 0, a, b, null, null);
+ }
+
+ /**
+ * Constructor for Trinomial Polynomial Basis (TPB).
+ * @param m The exponent <code>m</code> of
+ * <code>F<sub>2<sup>m</sup></sub></code>.
+ * @param k The integer <code>k</code> where <code>x<sup>m</sup> +
+ * x<sup>k</sup> + 1</code> represents the reduction
+ * polynomial <code>f(z)</code>.
+ * @param a The coefficient <code>a</code> in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * <code>F<sub>2<sup>m</sup></sub></code>.
+ * @param b The coefficient <code>b</code> in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * <code>F<sub>2<sup>m</sup></sub></code>.
+ * @param n The order of the main subgroup of the elliptic curve.
+ * @param h The cofactor of the elliptic curve, i.e.
+ * <code>#E<sub>a</sub>(F<sub>2<sup>m</sup></sub>) = h * n</code>.
+ */
+ public F2m(
+ int m,
+ int k,
+ BigInteger a,
+ BigInteger b,
+ BigInteger n,
+ BigInteger h)
+ {
+ this(m, k, 0, 0, a, b, n, h);
+ }
+
+ /**
+ * Constructor for Pentanomial Polynomial Basis (PPB).
+ * @param m The exponent <code>m</code> of
+ * <code>F<sub>2<sup>m</sup></sub></code>.
+ * @param k1 The integer <code>k1</code> where <code>x<sup>m</sup> +
+ * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+ * represents the reduction polynomial <code>f(z)</code>.
+ * @param k2 The integer <code>k2</code> where <code>x<sup>m</sup> +
+ * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+ * represents the reduction polynomial <code>f(z)</code>.
+ * @param k3 The integer <code>k3</code> where <code>x<sup>m</sup> +
+ * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+ * represents the reduction polynomial <code>f(z)</code>.
+ * @param a The coefficient <code>a</code> in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * <code>F<sub>2<sup>m</sup></sub></code>.
+ * @param b The coefficient <code>b</code> in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * <code>F<sub>2<sup>m</sup></sub></code>.
+ */
+ public F2m(
+ int m,
+ int k1,
+ int k2,
+ int k3,
+ BigInteger a,
+ BigInteger b)
+ {
+ this(m, k1, k2, k3, a, b, null, null);
+ }
+
+ /**
+ * Constructor for Pentanomial Polynomial Basis (PPB).
+ * @param m The exponent <code>m</code> of
+ * <code>F<sub>2<sup>m</sup></sub></code>.
+ * @param k1 The integer <code>k1</code> where <code>x<sup>m</sup> +
+ * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+ * represents the reduction polynomial <code>f(z)</code>.
+ * @param k2 The integer <code>k2</code> where <code>x<sup>m</sup> +
+ * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+ * represents the reduction polynomial <code>f(z)</code>.
+ * @param k3 The integer <code>k3</code> where <code>x<sup>m</sup> +
+ * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+ * represents the reduction polynomial <code>f(z)</code>.
+ * @param a The coefficient <code>a</code> in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * <code>F<sub>2<sup>m</sup></sub></code>.
+ * @param b The coefficient <code>b</code> in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * <code>F<sub>2<sup>m</sup></sub></code>.
+ * @param n The order of the main subgroup of the elliptic curve.
+ * @param h The cofactor of the elliptic curve, i.e.
+ * <code>#E<sub>a</sub>(F<sub>2<sup>m</sup></sub>) = h * n</code>.
+ */
+ public F2m(
+ int m,
+ int k1,
+ int k2,
+ int k3,
+ BigInteger a,
+ BigInteger b,
+ BigInteger n,
+ BigInteger h)
+ {
+ this.m = m;
+ this.k1 = k1;
+ this.k2 = k2;
+ this.k3 = k3;
+ this.n = n;
+ this.h = h;
+
+ if (k1 == 0)
+ {
+ throw new IllegalArgumentException("k1 must be > 0");
+ }
+
+ if (k2 == 0)
+ {
+ if (k3 != 0)
+ {
+ throw new IllegalArgumentException("k3 must be 0 if k2 == 0");
+ }
+ }
+ else
+ {
+ if (k2 <= k1)
+ {
+ throw new IllegalArgumentException("k2 must be > k1");
+ }
+
+ if (k3 <= k2)
+ {
+ throw new IllegalArgumentException("k3 must be > k2");
+ }
+ }
+
+ this.a = fromBigInteger(a);
+ this.b = fromBigInteger(b);
+ this.infinity = new ECPoint.F2m(this, null, null);
+ }
+
+ public int getFieldSize()
+ {
+ return m;
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new ECFieldElement.F2m(this.m, this.k1, this.k2, this.k3, x);
+ }
+
+ public ECPoint createPoint(BigInteger x, BigInteger y, boolean withCompression)
+ {
+ return new ECPoint.F2m(this, fromBigInteger(x), fromBigInteger(y), withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+
+ /**
+ * Returns true if this is a Koblitz curve (ABC curve).
+ * @return true if this is a Koblitz curve (ABC curve), false otherwise
+ */
+ public boolean isKoblitz()
+ {
+ return ((n != null) && (h != null) &&
+ ((a.toBigInteger().equals(ECConstants.ZERO)) ||
+ (a.toBigInteger().equals(ECConstants.ONE))) &&
+ (b.toBigInteger().equals(ECConstants.ONE)));
+ }
+
+ /**
+ * Returns the parameter <code>&mu;</code> of the elliptic curve.
+ * @return <code>&mu;</code> of the elliptic curve.
+ * @throws IllegalArgumentException if the given ECCurve is not a
+ * Koblitz curve.
+ */
+ synchronized byte getMu()
+ {
+ if (mu == 0)
+ {
+ mu = Tnaf.getMu(this);
+ }
+ return mu;
+ }
+
+ /**
+ * @return the auxiliary values <code>s<sub>0</sub></code> and
+ * <code>s<sub>1</sub></code> used for partial modular reduction for
+ * Koblitz curves.
+ */
+ synchronized BigInteger[] getSi()
+ {
+ if (si == null)
+ {
+ si = Tnaf.getSi(this);
+ }
+ return si;
+ }
+
+ /**
+ * Decompresses a compressed point P = (xp, yp) (X9.62 s 4.2.2).
+ *
+ * @param yTilde
+ * ~yp, an indication bit for the decompression of yp.
+ * @param X1
+ * The field element xp.
+ * @return the decompressed point.
+ */
+ protected ECPoint decompressPoint(int yTilde, BigInteger X1)
+ {
+ ECFieldElement xp = fromBigInteger(X1);
+ ECFieldElement yp = null;
+ if (xp.toBigInteger().equals(ECConstants.ZERO))
+ {
+ yp = (ECFieldElement.F2m)b;
+ for (int i = 0; i < m - 1; i++)
+ {
+ yp = yp.square();
+ }
+ }
+ else
+ {
+ ECFieldElement beta = xp.add(a).add(b.multiply(xp.square().invert()));
+ ECFieldElement z = solveQuadradicEquation(beta);
+ if (z == null)
+ {
+ throw new IllegalArgumentException("Invalid point compression");
+ }
+ int zBit = z.toBigInteger().testBit(0) ? 1 : 0;
+ if (zBit != yTilde)
+ {
+ z = z.add(fromBigInteger(ECConstants.ONE));
+ }
+ yp = xp.multiply(z);
+ }
+
+ return new ECPoint.F2m(this, xp, yp, true);
+ }
+
+ /**
+ * Solves a quadratic equation <code>z<sup>2</sup> + z = beta</code>(X9.62
+ * D.1.6) The other solution is <code>z + 1</code>.
+ *
+ * @param beta
+ * The value to solve the qradratic equation for.
+ * @return the solution for <code>z<sup>2</sup> + z = beta</code> or
+ * <code>null</code> if no solution exists.
+ */
+ private ECFieldElement solveQuadradicEquation(ECFieldElement beta)
+ {
+ ECFieldElement zeroElement = new ECFieldElement.F2m(
+ this.m, this.k1, this.k2, this.k3, ECConstants.ZERO);
+
+ if (beta.toBigInteger().equals(ECConstants.ZERO))
+ {
+ return zeroElement;
+ }
+
+ ECFieldElement z = null;
+ ECFieldElement gamma = zeroElement;
+
+ Random rand = new Random();
+ do
+ {
+ ECFieldElement t = new ECFieldElement.F2m(this.m, this.k1,
+ this.k2, this.k3, new BigInteger(m, rand));
+ z = zeroElement;
+ ECFieldElement w = beta;
+ for (int i = 1; i <= m - 1; i++)
+ {
+ ECFieldElement w2 = w.square();
+ z = z.square().add(w2.multiply(t));
+ w = w2.add(beta);
+ }
+ if (!w.toBigInteger().equals(ECConstants.ZERO))
+ {
+ return null;
+ }
+ gamma = z.square().add(z);
+ }
+ while (gamma.toBigInteger().equals(ECConstants.ZERO));
+
+ return z;
+ }
+
+ public boolean equals(
+ Object anObject)
+ {
+ if (anObject == this)
+ {
+ return true;
+ }
+
+ if (!(anObject instanceof ECCurve.F2m))
+ {
+ return false;
+ }
+
+ ECCurve.F2m other = (ECCurve.F2m)anObject;
+
+ return (this.m == other.m) && (this.k1 == other.k1)
+ && (this.k2 == other.k2) && (this.k3 == other.k3)
+ && a.equals(other.a) && b.equals(other.b);
+ }
+
+ public int hashCode()
+ {
+ return this.a.hashCode() ^ this.b.hashCode() ^ m ^ k1 ^ k2 ^ k3;
+ }
+
+ public int getM()
+ {
+ return m;
+ }
+
+ /**
+ * Return true if curve uses a Trinomial basis.
+ *
+ * @return true if curve Trinomial, false otherwise.
+ */
+ public boolean isTrinomial()
+ {
+ return k2 == 0 && k3 == 0;
+ }
+
+ public int getK1()
+ {
+ return k1;
+ }
+
+ public int getK2()
+ {
+ return k2;
+ }
+
+ public int getK3()
+ {
+ return k3;
+ }
+
+ public BigInteger getN()
+ {
+ return n;
+ }
+
+ public BigInteger getH()
+ {
+ return h;
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java b/core/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java
new file mode 100644
index 00000000..b5e9aa55
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java
@@ -0,0 +1,1196 @@
+package org.bouncycastle.math.ec;
+
+import java.math.BigInteger;
+import java.util.Random;
+
+public abstract class ECFieldElement
+ implements ECConstants
+{
+
+ public abstract BigInteger toBigInteger();
+ public abstract String getFieldName();
+ public abstract int getFieldSize();
+ public abstract ECFieldElement add(ECFieldElement b);
+ public abstract ECFieldElement subtract(ECFieldElement b);
+ public abstract ECFieldElement multiply(ECFieldElement b);
+ public abstract ECFieldElement divide(ECFieldElement b);
+ public abstract ECFieldElement negate();
+ public abstract ECFieldElement square();
+ public abstract ECFieldElement invert();
+ public abstract ECFieldElement sqrt();
+
+ public String toString()
+ {
+ return this.toBigInteger().toString(2);
+ }
+
+ public static class Fp extends ECFieldElement
+ {
+ BigInteger x;
+
+ BigInteger q;
+
+ public Fp(BigInteger q, BigInteger x)
+ {
+ this.x = x;
+
+ if (x.compareTo(q) >= 0)
+ {
+ throw new IllegalArgumentException("x value too large in field element");
+ }
+
+ this.q = q;
+ }
+
+ public BigInteger toBigInteger()
+ {
+ return x;
+ }
+
+ /**
+ * return the field name for this field.
+ *
+ * @return the string "Fp".
+ */
+ public String getFieldName()
+ {
+ return "Fp";
+ }
+
+ public int getFieldSize()
+ {
+ return q.bitLength();
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public ECFieldElement add(ECFieldElement b)
+ {
+ return new Fp(q, x.add(b.toBigInteger()).mod(q));
+ }
+
+ public ECFieldElement subtract(ECFieldElement b)
+ {
+ return new Fp(q, x.subtract(b.toBigInteger()).mod(q));
+ }
+
+ public ECFieldElement multiply(ECFieldElement b)
+ {
+ return new Fp(q, x.multiply(b.toBigInteger()).mod(q));
+ }
+
+ public ECFieldElement divide(ECFieldElement b)
+ {
+ return new Fp(q, x.multiply(b.toBigInteger().modInverse(q)).mod(q));
+ }
+
+ public ECFieldElement negate()
+ {
+ return new Fp(q, x.negate().mod(q));
+ }
+
+ public ECFieldElement square()
+ {
+ return new Fp(q, x.multiply(x).mod(q));
+ }
+
+ public ECFieldElement invert()
+ {
+ return new Fp(q, x.modInverse(q));
+ }
+
+ // D.1.4 91
+ /**
+ * return a sqrt root - the routine verifies that the calculation
+ * returns the right value - if none exists it returns null.
+ */
+ public ECFieldElement sqrt()
+ {
+ if (!q.testBit(0))
+ {
+ throw new RuntimeException("not done yet");
+ }
+
+ // note: even though this class implements ECConstants don't be tempted to
+ // remove the explicit declaration, some J2ME environments don't cope.
+ // p mod 4 == 3
+ if (q.testBit(1))
+ {
+ // z = g^(u+1) + p, p = 4u + 3
+ ECFieldElement z = new Fp(q, x.modPow(q.shiftRight(2).add(ECConstants.ONE), q));
+
+ return z.square().equals(this) ? z : null;
+ }
+
+ // p mod 4 == 1
+ BigInteger qMinusOne = q.subtract(ECConstants.ONE);
+
+ BigInteger legendreExponent = qMinusOne.shiftRight(1);
+ if (!(x.modPow(legendreExponent, q).equals(ECConstants.ONE)))
+ {
+ return null;
+ }
+
+ BigInteger u = qMinusOne.shiftRight(2);
+ BigInteger k = u.shiftLeft(1).add(ECConstants.ONE);
+
+ BigInteger Q = this.x;
+ BigInteger fourQ = Q.shiftLeft(2).mod(q);
+
+ BigInteger U, V;
+ Random rand = new Random();
+ do
+ {
+ BigInteger P;
+ do
+ {
+ P = new BigInteger(q.bitLength(), rand);
+ }
+ while (P.compareTo(q) >= 0
+ || !(P.multiply(P).subtract(fourQ).modPow(legendreExponent, q).equals(qMinusOne)));
+
+ BigInteger[] result = lucasSequence(q, P, Q, k);
+ U = result[0];
+ V = result[1];
+
+ if (V.multiply(V).mod(q).equals(fourQ))
+ {
+ // Integer division by 2, mod q
+ if (V.testBit(0))
+ {
+ V = V.add(q);
+ }
+
+ V = V.shiftRight(1);
+
+ //assert V.multiply(V).mod(q).equals(x);
+
+ return new ECFieldElement.Fp(q, V);
+ }
+ }
+ while (U.equals(ECConstants.ONE) || U.equals(qMinusOne));
+
+ return null;
+
+// BigInteger qMinusOne = q.subtract(ECConstants.ONE);
+// BigInteger legendreExponent = qMinusOne.shiftRight(1); //divide(ECConstants.TWO);
+// if (!(x.modPow(legendreExponent, q).equals(ECConstants.ONE)))
+// {
+// return null;
+// }
+//
+// Random rand = new Random();
+// BigInteger fourX = x.shiftLeft(2);
+//
+// BigInteger r;
+// do
+// {
+// r = new BigInteger(q.bitLength(), rand);
+// }
+// while (r.compareTo(q) >= 0
+// || !(r.multiply(r).subtract(fourX).modPow(legendreExponent, q).equals(qMinusOne)));
+//
+// BigInteger n1 = qMinusOne.shiftRight(2); //.divide(ECConstants.FOUR);
+// BigInteger n2 = n1.add(ECConstants.ONE); //q.add(ECConstants.THREE).divide(ECConstants.FOUR);
+//
+// BigInteger wOne = WOne(r, x, q);
+// BigInteger wSum = W(n1, wOne, q).add(W(n2, wOne, q)).mod(q);
+// BigInteger twoR = r.shiftLeft(1); //ECConstants.TWO.multiply(r);
+//
+// BigInteger root = twoR.modPow(q.subtract(ECConstants.TWO), q)
+// .multiply(x).mod(q)
+// .multiply(wSum).mod(q);
+//
+// return new Fp(q, root);
+ }
+
+// private static BigInteger W(BigInteger n, BigInteger wOne, BigInteger p)
+// {
+// if (n.equals(ECConstants.ONE))
+// {
+// return wOne;
+// }
+// boolean isEven = !n.testBit(0);
+// n = n.shiftRight(1);//divide(ECConstants.TWO);
+// if (isEven)
+// {
+// BigInteger w = W(n, wOne, p);
+// return w.multiply(w).subtract(ECConstants.TWO).mod(p);
+// }
+// BigInteger w1 = W(n.add(ECConstants.ONE), wOne, p);
+// BigInteger w2 = W(n, wOne, p);
+// return w1.multiply(w2).subtract(wOne).mod(p);
+// }
+//
+// private BigInteger WOne(BigInteger r, BigInteger x, BigInteger p)
+// {
+// return r.multiply(r).multiply(x.modPow(q.subtract(ECConstants.TWO), q)).subtract(ECConstants.TWO).mod(p);
+// }
+
+ private static BigInteger[] lucasSequence(
+ BigInteger p,
+ BigInteger P,
+ BigInteger Q,
+ BigInteger k)
+ {
+ int n = k.bitLength();
+ int s = k.getLowestSetBit();
+
+ BigInteger Uh = ECConstants.ONE;
+ BigInteger Vl = ECConstants.TWO;
+ BigInteger Vh = P;
+ BigInteger Ql = ECConstants.ONE;
+ BigInteger Qh = ECConstants.ONE;
+
+ for (int j = n - 1; j >= s + 1; --j)
+ {
+ Ql = Ql.multiply(Qh).mod(p);
+
+ if (k.testBit(j))
+ {
+ Qh = Ql.multiply(Q).mod(p);
+ Uh = Uh.multiply(Vh).mod(p);
+ Vl = Vh.multiply(Vl).subtract(P.multiply(Ql)).mod(p);
+ Vh = Vh.multiply(Vh).subtract(Qh.shiftLeft(1)).mod(p);
+ }
+ else
+ {
+ Qh = Ql;
+ Uh = Uh.multiply(Vl).subtract(Ql).mod(p);
+ Vh = Vh.multiply(Vl).subtract(P.multiply(Ql)).mod(p);
+ Vl = Vl.multiply(Vl).subtract(Ql.shiftLeft(1)).mod(p);
+ }
+ }
+
+ Ql = Ql.multiply(Qh).mod(p);
+ Qh = Ql.multiply(Q).mod(p);
+ Uh = Uh.multiply(Vl).subtract(Ql).mod(p);
+ Vl = Vh.multiply(Vl).subtract(P.multiply(Ql)).mod(p);
+ Ql = Ql.multiply(Qh).mod(p);
+
+ for (int j = 1; j <= s; ++j)
+ {
+ Uh = Uh.multiply(Vl).mod(p);
+ Vl = Vl.multiply(Vl).subtract(Ql.shiftLeft(1)).mod(p);
+ Ql = Ql.multiply(Ql).mod(p);
+ }
+
+ return new BigInteger[]{ Uh, Vl };
+ }
+
+ public boolean equals(Object other)
+ {
+ if (other == this)
+ {
+ return true;
+ }
+
+ if (!(other instanceof ECFieldElement.Fp))
+ {
+ return false;
+ }
+
+ ECFieldElement.Fp o = (ECFieldElement.Fp)other;
+ return q.equals(o.q) && x.equals(o.x);
+ }
+
+ public int hashCode()
+ {
+ return q.hashCode() ^ x.hashCode();
+ }
+ }
+
+// /**
+// * Class representing the Elements of the finite field
+// * <code>F<sub>2<sup>m</sup></sub></code> in polynomial basis (PB)
+// * representation. Both trinomial (TPB) and pentanomial (PPB) polynomial
+// * basis representations are supported. Gaussian normal basis (GNB)
+// * representation is not supported.
+// */
+// public static class F2m extends ECFieldElement
+// {
+// BigInteger x;
+//
+// /**
+// * Indicates gaussian normal basis representation (GNB). Number chosen
+// * according to X9.62. GNB is not implemented at present.
+// */
+// public static final int GNB = 1;
+//
+// /**
+// * Indicates trinomial basis representation (TPB). Number chosen
+// * according to X9.62.
+// */
+// public static final int TPB = 2;
+//
+// /**
+// * Indicates pentanomial basis representation (PPB). Number chosen
+// * according to X9.62.
+// */
+// public static final int PPB = 3;
+//
+// /**
+// * TPB or PPB.
+// */
+// private int representation;
+//
+// /**
+// * The exponent <code>m</code> of <code>F<sub>2<sup>m</sup></sub></code>.
+// */
+// private int m;
+//
+// /**
+// * TPB: The integer <code>k</code> where <code>x<sup>m</sup> +
+// * x<sup>k</sup> + 1</code> represents the reduction polynomial
+// * <code>f(z)</code>.<br>
+// * PPB: The integer <code>k1</code> where <code>x<sup>m</sup> +
+// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+// * represents the reduction polynomial <code>f(z)</code>.<br>
+// */
+// private int k1;
+//
+// /**
+// * TPB: Always set to <code>0</code><br>
+// * PPB: The integer <code>k2</code> where <code>x<sup>m</sup> +
+// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+// * represents the reduction polynomial <code>f(z)</code>.<br>
+// */
+// private int k2;
+//
+// /**
+// * TPB: Always set to <code>0</code><br>
+// * PPB: The integer <code>k3</code> where <code>x<sup>m</sup> +
+// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+// * represents the reduction polynomial <code>f(z)</code>.<br>
+// */
+// private int k3;
+//
+// /**
+// * Constructor for PPB.
+// * @param m The exponent <code>m</code> of
+// * <code>F<sub>2<sup>m</sup></sub></code>.
+// * @param k1 The integer <code>k1</code> where <code>x<sup>m</sup> +
+// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+// * represents the reduction polynomial <code>f(z)</code>.
+// * @param k2 The integer <code>k2</code> where <code>x<sup>m</sup> +
+// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+// * represents the reduction polynomial <code>f(z)</code>.
+// * @param k3 The integer <code>k3</code> where <code>x<sup>m</sup> +
+// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+// * represents the reduction polynomial <code>f(z)</code>.
+// * @param x The BigInteger representing the value of the field element.
+// */
+// public F2m(
+// int m,
+// int k1,
+// int k2,
+// int k3,
+// BigInteger x)
+// {
+//// super(x);
+// this.x = x;
+//
+// if ((k2 == 0) && (k3 == 0))
+// {
+// this.representation = TPB;
+// }
+// else
+// {
+// if (k2 >= k3)
+// {
+// throw new IllegalArgumentException(
+// "k2 must be smaller than k3");
+// }
+// if (k2 <= 0)
+// {
+// throw new IllegalArgumentException(
+// "k2 must be larger than 0");
+// }
+// this.representation = PPB;
+// }
+//
+// if (x.signum() < 0)
+// {
+// throw new IllegalArgumentException("x value cannot be negative");
+// }
+//
+// this.m = m;
+// this.k1 = k1;
+// this.k2 = k2;
+// this.k3 = k3;
+// }
+//
+// /**
+// * Constructor for TPB.
+// * @param m The exponent <code>m</code> of
+// * <code>F<sub>2<sup>m</sup></sub></code>.
+// * @param k The integer <code>k</code> where <code>x<sup>m</sup> +
+// * x<sup>k</sup> + 1</code> represents the reduction
+// * polynomial <code>f(z)</code>.
+// * @param x The BigInteger representing the value of the field element.
+// */
+// public F2m(int m, int k, BigInteger x)
+// {
+// // Set k1 to k, and set k2 and k3 to 0
+// this(m, k, 0, 0, x);
+// }
+//
+// public BigInteger toBigInteger()
+// {
+// return x;
+// }
+//
+// public String getFieldName()
+// {
+// return "F2m";
+// }
+//
+// public int getFieldSize()
+// {
+// return m;
+// }
+//
+// /**
+// * Checks, if the ECFieldElements <code>a</code> and <code>b</code>
+// * are elements of the same field <code>F<sub>2<sup>m</sup></sub></code>
+// * (having the same representation).
+// * @param a field element.
+// * @param b field element to be compared.
+// * @throws IllegalArgumentException if <code>a</code> and <code>b</code>
+// * are not elements of the same field
+// * <code>F<sub>2<sup>m</sup></sub></code> (having the same
+// * representation).
+// */
+// public static void checkFieldElements(
+// ECFieldElement a,
+// ECFieldElement b)
+// {
+// if ((!(a instanceof F2m)) || (!(b instanceof F2m)))
+// {
+// throw new IllegalArgumentException("Field elements are not "
+// + "both instances of ECFieldElement.F2m");
+// }
+//
+// if ((a.toBigInteger().signum() < 0) || (b.toBigInteger().signum() < 0))
+// {
+// throw new IllegalArgumentException(
+// "x value may not be negative");
+// }
+//
+// ECFieldElement.F2m aF2m = (ECFieldElement.F2m)a;
+// ECFieldElement.F2m bF2m = (ECFieldElement.F2m)b;
+//
+// if ((aF2m.m != bF2m.m) || (aF2m.k1 != bF2m.k1)
+// || (aF2m.k2 != bF2m.k2) || (aF2m.k3 != bF2m.k3))
+// {
+// throw new IllegalArgumentException("Field elements are not "
+// + "elements of the same field F2m");
+// }
+//
+// if (aF2m.representation != bF2m.representation)
+// {
+// // Should never occur
+// throw new IllegalArgumentException(
+// "One of the field "
+// + "elements are not elements has incorrect representation");
+// }
+// }
+//
+// /**
+// * Computes <code>z * a(z) mod f(z)</code>, where <code>f(z)</code> is
+// * the reduction polynomial of <code>this</code>.
+// * @param a The polynomial <code>a(z)</code> to be multiplied by
+// * <code>z mod f(z)</code>.
+// * @return <code>z * a(z) mod f(z)</code>
+// */
+// private BigInteger multZModF(final BigInteger a)
+// {
+// // Left-shift of a(z)
+// BigInteger az = a.shiftLeft(1);
+// if (az.testBit(this.m))
+// {
+// // If the coefficient of z^m in a(z) equals 1, reduction
+// // modulo f(z) is performed: Add f(z) to to a(z):
+// // Step 1: Unset mth coeffient of a(z)
+// az = az.clearBit(this.m);
+//
+// // Step 2: Add r(z) to a(z), where r(z) is defined as
+// // f(z) = z^m + r(z), and k1, k2, k3 are the positions of
+// // the non-zero coefficients in r(z)
+// az = az.flipBit(0);
+// az = az.flipBit(this.k1);
+// if (this.representation == PPB)
+// {
+// az = az.flipBit(this.k2);
+// az = az.flipBit(this.k3);
+// }
+// }
+// return az;
+// }
+//
+// public ECFieldElement add(final ECFieldElement b)
+// {
+// // No check performed here for performance reasons. Instead the
+// // elements involved are checked in ECPoint.F2m
+// // checkFieldElements(this, b);
+// if (b.toBigInteger().signum() == 0)
+// {
+// return this;
+// }
+//
+// return new F2m(this.m, this.k1, this.k2, this.k3, this.x.xor(b.toBigInteger()));
+// }
+//
+// public ECFieldElement subtract(final ECFieldElement b)
+// {
+// // Addition and subtraction are the same in F2m
+// return add(b);
+// }
+//
+//
+// public ECFieldElement multiply(final ECFieldElement b)
+// {
+// // Left-to-right shift-and-add field multiplication in F2m
+// // Input: Binary polynomials a(z) and b(z) of degree at most m-1
+// // Output: c(z) = a(z) * b(z) mod f(z)
+//
+// // No check performed here for performance reasons. Instead the
+// // elements involved are checked in ECPoint.F2m
+// // checkFieldElements(this, b);
+// final BigInteger az = this.x;
+// BigInteger bz = b.toBigInteger();
+// BigInteger cz;
+//
+// // Compute c(z) = a(z) * b(z) mod f(z)
+// if (az.testBit(0))
+// {
+// cz = bz;
+// }
+// else
+// {
+// cz = ECConstants.ZERO;
+// }
+//
+// for (int i = 1; i < this.m; i++)
+// {
+// // b(z) := z * b(z) mod f(z)
+// bz = multZModF(bz);
+//
+// if (az.testBit(i))
+// {
+// // If the coefficient of x^i in a(z) equals 1, b(z) is added
+// // to c(z)
+// cz = cz.xor(bz);
+// }
+// }
+// return new ECFieldElement.F2m(m, this.k1, this.k2, this.k3, cz);
+// }
+//
+//
+// public ECFieldElement divide(final ECFieldElement b)
+// {
+// // There may be more efficient implementations
+// ECFieldElement bInv = b.invert();
+// return multiply(bInv);
+// }
+//
+// public ECFieldElement negate()
+// {
+// // -x == x holds for all x in F2m
+// return this;
+// }
+//
+// public ECFieldElement square()
+// {
+// // Naive implementation, can probably be speeded up using modular
+// // reduction
+// return multiply(this);
+// }
+//
+// public ECFieldElement invert()
+// {
+// // Inversion in F2m using the extended Euclidean algorithm
+// // Input: A nonzero polynomial a(z) of degree at most m-1
+// // Output: a(z)^(-1) mod f(z)
+//
+// // u(z) := a(z)
+// BigInteger uz = this.x;
+// if (uz.signum() <= 0)
+// {
+// throw new ArithmeticException("x is zero or negative, " +
+// "inversion is impossible");
+// }
+//
+// // v(z) := f(z)
+// BigInteger vz = ECConstants.ZERO.setBit(m);
+// vz = vz.setBit(0);
+// vz = vz.setBit(this.k1);
+// if (this.representation == PPB)
+// {
+// vz = vz.setBit(this.k2);
+// vz = vz.setBit(this.k3);
+// }
+//
+// // g1(z) := 1, g2(z) := 0
+// BigInteger g1z = ECConstants.ONE;
+// BigInteger g2z = ECConstants.ZERO;
+//
+// // while u != 1
+// while (!(uz.equals(ECConstants.ZERO)))
+// {
+// // j := deg(u(z)) - deg(v(z))
+// int j = uz.bitLength() - vz.bitLength();
+//
+// // If j < 0 then: u(z) <-> v(z), g1(z) <-> g2(z), j := -j
+// if (j < 0)
+// {
+// final BigInteger uzCopy = uz;
+// uz = vz;
+// vz = uzCopy;
+//
+// final BigInteger g1zCopy = g1z;
+// g1z = g2z;
+// g2z = g1zCopy;
+//
+// j = -j;
+// }
+//
+// // u(z) := u(z) + z^j * v(z)
+// // Note, that no reduction modulo f(z) is required, because
+// // deg(u(z) + z^j * v(z)) <= max(deg(u(z)), j + deg(v(z)))
+// // = max(deg(u(z)), deg(u(z)) - deg(v(z)) + deg(v(z))
+// // = deg(u(z))
+// uz = uz.xor(vz.shiftLeft(j));
+//
+// // g1(z) := g1(z) + z^j * g2(z)
+// g1z = g1z.xor(g2z.shiftLeft(j));
+//// if (g1z.bitLength() > this.m) {
+//// throw new ArithmeticException(
+//// "deg(g1z) >= m, g1z = " + g1z.toString(2));
+//// }
+// }
+// return new ECFieldElement.F2m(
+// this.m, this.k1, this.k2, this.k3, g2z);
+// }
+//
+// public ECFieldElement sqrt()
+// {
+// throw new RuntimeException("Not implemented");
+// }
+//
+// /**
+// * @return the representation of the field
+// * <code>F<sub>2<sup>m</sup></sub></code>, either of
+// * TPB (trinomial
+// * basis representation) or
+// * PPB (pentanomial
+// * basis representation).
+// */
+// public int getRepresentation()
+// {
+// return this.representation;
+// }
+//
+// /**
+// * @return the degree <code>m</code> of the reduction polynomial
+// * <code>f(z)</code>.
+// */
+// public int getM()
+// {
+// return this.m;
+// }
+//
+// /**
+// * @return TPB: The integer <code>k</code> where <code>x<sup>m</sup> +
+// * x<sup>k</sup> + 1</code> represents the reduction polynomial
+// * <code>f(z)</code>.<br>
+// * PPB: The integer <code>k1</code> where <code>x<sup>m</sup> +
+// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+// * represents the reduction polynomial <code>f(z)</code>.<br>
+// */
+// public int getK1()
+// {
+// return this.k1;
+// }
+//
+// /**
+// * @return TPB: Always returns <code>0</code><br>
+// * PPB: The integer <code>k2</code> where <code>x<sup>m</sup> +
+// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+// * represents the reduction polynomial <code>f(z)</code>.<br>
+// */
+// public int getK2()
+// {
+// return this.k2;
+// }
+//
+// /**
+// * @return TPB: Always set to <code>0</code><br>
+// * PPB: The integer <code>k3</code> where <code>x<sup>m</sup> +
+// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+// * represents the reduction polynomial <code>f(z)</code>.<br>
+// */
+// public int getK3()
+// {
+// return this.k3;
+// }
+//
+// public boolean equals(Object anObject)
+// {
+// if (anObject == this)
+// {
+// return true;
+// }
+//
+// if (!(anObject instanceof ECFieldElement.F2m))
+// {
+// return false;
+// }
+//
+// ECFieldElement.F2m b = (ECFieldElement.F2m)anObject;
+//
+// return ((this.m == b.m) && (this.k1 == b.k1) && (this.k2 == b.k2)
+// && (this.k3 == b.k3)
+// && (this.representation == b.representation)
+// && (this.x.equals(b.x)));
+// }
+//
+// public int hashCode()
+// {
+// return x.hashCode() ^ m ^ k1 ^ k2 ^ k3;
+// }
+// }
+
+ /**
+ * Class representing the Elements of the finite field
+ * <code>F<sub>2<sup>m</sup></sub></code> in polynomial basis (PB)
+ * representation. Both trinomial (TPB) and pentanomial (PPB) polynomial
+ * basis representations are supported. Gaussian normal basis (GNB)
+ * representation is not supported.
+ */
+ public static class F2m extends ECFieldElement
+ {
+ /**
+ * Indicates gaussian normal basis representation (GNB). Number chosen
+ * according to X9.62. GNB is not implemented at present.
+ */
+ public static final int GNB = 1;
+
+ /**
+ * Indicates trinomial basis representation (TPB). Number chosen
+ * according to X9.62.
+ */
+ public static final int TPB = 2;
+
+ /**
+ * Indicates pentanomial basis representation (PPB). Number chosen
+ * according to X9.62.
+ */
+ public static final int PPB = 3;
+
+ /**
+ * TPB or PPB.
+ */
+ private int representation;
+
+ /**
+ * The exponent <code>m</code> of <code>F<sub>2<sup>m</sup></sub></code>.
+ */
+ private int m;
+
+ /**
+ * TPB: The integer <code>k</code> where <code>x<sup>m</sup> +
+ * x<sup>k</sup> + 1</code> represents the reduction polynomial
+ * <code>f(z)</code>.<br>
+ * PPB: The integer <code>k1</code> where <code>x<sup>m</sup> +
+ * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+ * represents the reduction polynomial <code>f(z)</code>.<br>
+ */
+ private int k1;
+
+ /**
+ * TPB: Always set to <code>0</code><br>
+ * PPB: The integer <code>k2</code> where <code>x<sup>m</sup> +
+ * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+ * represents the reduction polynomial <code>f(z)</code>.<br>
+ */
+ private int k2;
+
+ /**
+ * TPB: Always set to <code>0</code><br>
+ * PPB: The integer <code>k3</code> where <code>x<sup>m</sup> +
+ * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+ * represents the reduction polynomial <code>f(z)</code>.<br>
+ */
+ private int k3;
+
+ /**
+ * The <code>IntArray</code> holding the bits.
+ */
+ private IntArray x;
+
+ /**
+ * The number of <code>int</code>s required to hold <code>m</code> bits.
+ */
+ private int t;
+
+ /**
+ * Constructor for PPB.
+ * @param m The exponent <code>m</code> of
+ * <code>F<sub>2<sup>m</sup></sub></code>.
+ * @param k1 The integer <code>k1</code> where <code>x<sup>m</sup> +
+ * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+ * represents the reduction polynomial <code>f(z)</code>.
+ * @param k2 The integer <code>k2</code> where <code>x<sup>m</sup> +
+ * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+ * represents the reduction polynomial <code>f(z)</code>.
+ * @param k3 The integer <code>k3</code> where <code>x<sup>m</sup> +
+ * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+ * represents the reduction polynomial <code>f(z)</code>.
+ * @param x The BigInteger representing the value of the field element.
+ */
+ public F2m(
+ int m,
+ int k1,
+ int k2,
+ int k3,
+ BigInteger x)
+ {
+ // t = m / 32 rounded up to the next integer
+ t = (m + 31) >> 5;
+ this.x = new IntArray(x, t);
+
+ if ((k2 == 0) && (k3 == 0))
+ {
+ this.representation = TPB;
+ }
+ else
+ {
+ if (k2 >= k3)
+ {
+ throw new IllegalArgumentException(
+ "k2 must be smaller than k3");
+ }
+ if (k2 <= 0)
+ {
+ throw new IllegalArgumentException(
+ "k2 must be larger than 0");
+ }
+ this.representation = PPB;
+ }
+
+ if (x.signum() < 0)
+ {
+ throw new IllegalArgumentException("x value cannot be negative");
+ }
+
+ this.m = m;
+ this.k1 = k1;
+ this.k2 = k2;
+ this.k3 = k3;
+ }
+
+ /**
+ * Constructor for TPB.
+ * @param m The exponent <code>m</code> of
+ * <code>F<sub>2<sup>m</sup></sub></code>.
+ * @param k The integer <code>k</code> where <code>x<sup>m</sup> +
+ * x<sup>k</sup> + 1</code> represents the reduction
+ * polynomial <code>f(z)</code>.
+ * @param x The BigInteger representing the value of the field element.
+ */
+ public F2m(int m, int k, BigInteger x)
+ {
+ // Set k1 to k, and set k2 and k3 to 0
+ this(m, k, 0, 0, x);
+ }
+
+ private F2m(int m, int k1, int k2, int k3, IntArray x)
+ {
+ t = (m + 31) >> 5;
+ this.x = x;
+ this.m = m;
+ this.k1 = k1;
+ this.k2 = k2;
+ this.k3 = k3;
+
+ if ((k2 == 0) && (k3 == 0))
+ {
+ this.representation = TPB;
+ }
+ else
+ {
+ this.representation = PPB;
+ }
+
+ }
+
+ public BigInteger toBigInteger()
+ {
+ return x.toBigInteger();
+ }
+
+ public String getFieldName()
+ {
+ return "F2m";
+ }
+
+ public int getFieldSize()
+ {
+ return m;
+ }
+
+ /**
+ * Checks, if the ECFieldElements <code>a</code> and <code>b</code>
+ * are elements of the same field <code>F<sub>2<sup>m</sup></sub></code>
+ * (having the same representation).
+ * @param a field element.
+ * @param b field element to be compared.
+ * @throws IllegalArgumentException if <code>a</code> and <code>b</code>
+ * are not elements of the same field
+ * <code>F<sub>2<sup>m</sup></sub></code> (having the same
+ * representation).
+ */
+ public static void checkFieldElements(
+ ECFieldElement a,
+ ECFieldElement b)
+ {
+ if ((!(a instanceof F2m)) || (!(b instanceof F2m)))
+ {
+ throw new IllegalArgumentException("Field elements are not "
+ + "both instances of ECFieldElement.F2m");
+ }
+
+ ECFieldElement.F2m aF2m = (ECFieldElement.F2m)a;
+ ECFieldElement.F2m bF2m = (ECFieldElement.F2m)b;
+
+ if ((aF2m.m != bF2m.m) || (aF2m.k1 != bF2m.k1)
+ || (aF2m.k2 != bF2m.k2) || (aF2m.k3 != bF2m.k3))
+ {
+ throw new IllegalArgumentException("Field elements are not "
+ + "elements of the same field F2m");
+ }
+
+ if (aF2m.representation != bF2m.representation)
+ {
+ // Should never occur
+ throw new IllegalArgumentException(
+ "One of the field "
+ + "elements are not elements has incorrect representation");
+ }
+ }
+
+ public ECFieldElement add(final ECFieldElement b)
+ {
+ // No check performed here for performance reasons. Instead the
+ // elements involved are checked in ECPoint.F2m
+ // checkFieldElements(this, b);
+ IntArray iarrClone = (IntArray)this.x.clone();
+ F2m bF2m = (F2m)b;
+ iarrClone.addShifted(bF2m.x, 0);
+ return new F2m(m, k1, k2, k3, iarrClone);
+ }
+
+ public ECFieldElement subtract(final ECFieldElement b)
+ {
+ // Addition and subtraction are the same in F2m
+ return add(b);
+ }
+
+ public ECFieldElement multiply(final ECFieldElement b)
+ {
+ // Right-to-left comb multiplication in the IntArray
+ // Input: Binary polynomials a(z) and b(z) of degree at most m-1
+ // Output: c(z) = a(z) * b(z) mod f(z)
+
+ // No check performed here for performance reasons. Instead the
+ // elements involved are checked in ECPoint.F2m
+ // checkFieldElements(this, b);
+ F2m bF2m = (F2m)b;
+ IntArray mult = x.multiply(bF2m.x, m);
+ mult.reduce(m, new int[]{k1, k2, k3});
+ return new F2m(m, k1, k2, k3, mult);
+ }
+
+ public ECFieldElement divide(final ECFieldElement b)
+ {
+ // There may be more efficient implementations
+ ECFieldElement bInv = b.invert();
+ return multiply(bInv);
+ }
+
+ public ECFieldElement negate()
+ {
+ // -x == x holds for all x in F2m
+ return this;
+ }
+
+ public ECFieldElement square()
+ {
+ IntArray squared = x.square(m);
+ squared.reduce(m, new int[]{k1, k2, k3});
+ return new F2m(m, k1, k2, k3, squared);
+ }
+
+
+ public ECFieldElement invert()
+ {
+ // Inversion in F2m using the extended Euclidean algorithm
+ // Input: A nonzero polynomial a(z) of degree at most m-1
+ // Output: a(z)^(-1) mod f(z)
+
+ // u(z) := a(z)
+ IntArray uz = (IntArray)this.x.clone();
+
+ // v(z) := f(z)
+ IntArray vz = new IntArray(t);
+ vz.setBit(m);
+ vz.setBit(0);
+ vz.setBit(this.k1);
+ if (this.representation == PPB)
+ {
+ vz.setBit(this.k2);
+ vz.setBit(this.k3);
+ }
+
+ // g1(z) := 1, g2(z) := 0
+ IntArray g1z = new IntArray(t);
+ g1z.setBit(0);
+ IntArray g2z = new IntArray(t);
+
+ // while u != 0
+ while (!uz.isZero())
+// while (uz.getUsedLength() > 0)
+// while (uz.bitLength() > 1)
+ {
+ // j := deg(u(z)) - deg(v(z))
+ int j = uz.bitLength() - vz.bitLength();
+
+ // If j < 0 then: u(z) <-> v(z), g1(z) <-> g2(z), j := -j
+ if (j < 0)
+ {
+ final IntArray uzCopy = uz;
+ uz = vz;
+ vz = uzCopy;
+
+ final IntArray g1zCopy = g1z;
+ g1z = g2z;
+ g2z = g1zCopy;
+
+ j = -j;
+ }
+
+ // u(z) := u(z) + z^j * v(z)
+ // Note, that no reduction modulo f(z) is required, because
+ // deg(u(z) + z^j * v(z)) <= max(deg(u(z)), j + deg(v(z)))
+ // = max(deg(u(z)), deg(u(z)) - deg(v(z)) + deg(v(z))
+ // = deg(u(z))
+ // uz = uz.xor(vz.shiftLeft(j));
+ // jInt = n / 32
+ int jInt = j >> 5;
+ // jInt = n % 32
+ int jBit = j & 0x1F;
+ IntArray vzShift = vz.shiftLeft(jBit);
+ uz.addShifted(vzShift, jInt);
+
+ // g1(z) := g1(z) + z^j * g2(z)
+// g1z = g1z.xor(g2z.shiftLeft(j));
+ IntArray g2zShift = g2z.shiftLeft(jBit);
+ g1z.addShifted(g2zShift, jInt);
+
+ }
+ return new ECFieldElement.F2m(
+ this.m, this.k1, this.k2, this.k3, g2z);
+ }
+
+ public ECFieldElement sqrt()
+ {
+ throw new RuntimeException("Not implemented");
+ }
+
+ /**
+ * @return the representation of the field
+ * <code>F<sub>2<sup>m</sup></sub></code>, either of
+ * TPB (trinomial
+ * basis representation) or
+ * PPB (pentanomial
+ * basis representation).
+ */
+ public int getRepresentation()
+ {
+ return this.representation;
+ }
+
+ /**
+ * @return the degree <code>m</code> of the reduction polynomial
+ * <code>f(z)</code>.
+ */
+ public int getM()
+ {
+ return this.m;
+ }
+
+ /**
+ * @return TPB: The integer <code>k</code> where <code>x<sup>m</sup> +
+ * x<sup>k</sup> + 1</code> represents the reduction polynomial
+ * <code>f(z)</code>.<br>
+ * PPB: The integer <code>k1</code> where <code>x<sup>m</sup> +
+ * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+ * represents the reduction polynomial <code>f(z)</code>.<br>
+ */
+ public int getK1()
+ {
+ return this.k1;
+ }
+
+ /**
+ * @return TPB: Always returns <code>0</code><br>
+ * PPB: The integer <code>k2</code> where <code>x<sup>m</sup> +
+ * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+ * represents the reduction polynomial <code>f(z)</code>.<br>
+ */
+ public int getK2()
+ {
+ return this.k2;
+ }
+
+ /**
+ * @return TPB: Always set to <code>0</code><br>
+ * PPB: The integer <code>k3</code> where <code>x<sup>m</sup> +
+ * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+ * represents the reduction polynomial <code>f(z)</code>.<br>
+ */
+ public int getK3()
+ {
+ return this.k3;
+ }
+
+ public boolean equals(Object anObject)
+ {
+ if (anObject == this)
+ {
+ return true;
+ }
+
+ if (!(anObject instanceof ECFieldElement.F2m))
+ {
+ return false;
+ }
+
+ ECFieldElement.F2m b = (ECFieldElement.F2m)anObject;
+
+ return ((this.m == b.m) && (this.k1 == b.k1) && (this.k2 == b.k2)
+ && (this.k3 == b.k3)
+ && (this.representation == b.representation)
+ && (this.x.equals(b.x)));
+ }
+
+ public int hashCode()
+ {
+ return x.hashCode() ^ m ^ k1 ^ k2 ^ k3;
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/math/ec/ECMultiplier.java b/core/src/main/java/org/bouncycastle/math/ec/ECMultiplier.java
new file mode 100644
index 00000000..4d72e33c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/math/ec/ECMultiplier.java
@@ -0,0 +1,19 @@
+package org.bouncycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Interface for classes encapsulating a point multiplication algorithm
+ * for <code>ECPoint</code>s.
+ */
+interface ECMultiplier
+{
+ /**
+ * Multiplies the <code>ECPoint p</code> by <code>k</code>, i.e.
+ * <code>p</code> is added <code>k</code> times to itself.
+ * @param p The <code>ECPoint</code> to be multiplied.
+ * @param k The factor by which <code>p</code> i multiplied.
+ * @return <code>p</code> multiplied by <code>k</code>.
+ */
+ ECPoint multiply(ECPoint p, BigInteger k, PreCompInfo preCompInfo);
+}
diff --git a/core/src/main/java/org/bouncycastle/math/ec/ECPoint.java b/core/src/main/java/org/bouncycastle/math/ec/ECPoint.java
new file mode 100644
index 00000000..cbc5aaf4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/math/ec/ECPoint.java
@@ -0,0 +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();
+ }
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/math/ec/FpNafMultiplier.java b/core/src/main/java/org/bouncycastle/math/ec/FpNafMultiplier.java
new file mode 100644
index 00000000..35e601d5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/math/ec/FpNafMultiplier.java
@@ -0,0 +1,39 @@
+package org.bouncycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class implementing the NAF (Non-Adjacent Form) multiplication algorithm.
+ */
+class FpNafMultiplier implements ECMultiplier
+{
+ /**
+ * D.3.2 pg 101
+ * @see org.bouncycastle.math.ec.ECMultiplier#multiply(org.bouncycastle.math.ec.ECPoint, java.math.BigInteger)
+ */
+ public ECPoint multiply(ECPoint p, BigInteger k, PreCompInfo preCompInfo)
+ {
+ // TODO Probably should try to add this
+ // BigInteger e = k.mod(n); // n == order of p
+ BigInteger e = k;
+ BigInteger h = e.multiply(BigInteger.valueOf(3));
+
+ ECPoint neg = p.negate();
+ ECPoint R = p;
+
+ for (int i = h.bitLength() - 2; i > 0; --i)
+ {
+ R = R.twice();
+
+ boolean hBit = h.testBit(i);
+ boolean eBit = e.testBit(i);
+
+ if (hBit != eBit)
+ {
+ R = R.add(hBit ? p : neg);
+ }
+ }
+
+ return R;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/math/ec/IntArray.java b/core/src/main/java/org/bouncycastle/math/ec/IntArray.java
new file mode 100644
index 00000000..ead38c48
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/math/ec/IntArray.java
@@ -0,0 +1,518 @@
+package org.bouncycastle.math.ec;
+
+import org.bouncycastle.util.Arrays;
+
+import java.math.BigInteger;
+
+class IntArray
+{
+ // TODO make m fixed for the IntArray, and hence compute T once and for all
+
+ private int[] m_ints;
+
+ public IntArray(int intLen)
+ {
+ m_ints = new int[intLen];
+ }
+
+ public IntArray(int[] ints)
+ {
+ m_ints = ints;
+ }
+
+ public IntArray(BigInteger bigInt)
+ {
+ this(bigInt, 0);
+ }
+
+ public IntArray(BigInteger bigInt, int minIntLen)
+ {
+ if (bigInt.signum() == -1)
+ {
+ throw new IllegalArgumentException("Only positive Integers allowed");
+ }
+ if (bigInt.equals(ECConstants.ZERO))
+ {
+ m_ints = new int[] { 0 };
+ return;
+ }
+
+ byte[] barr = bigInt.toByteArray();
+ int barrLen = barr.length;
+ int barrStart = 0;
+ if (barr[0] == 0)
+ {
+ // First byte is 0 to enforce highest (=sign) bit is zero.
+ // In this case ignore barr[0].
+ barrLen--;
+ barrStart = 1;
+ }
+ int intLen = (barrLen + 3) / 4;
+ if (intLen < minIntLen)
+ {
+ m_ints = new int[minIntLen];
+ }
+ else
+ {
+ m_ints = new int[intLen];
+ }
+
+ int iarrJ = intLen - 1;
+ int rem = barrLen % 4 + barrStart;
+ int temp = 0;
+ int barrI = barrStart;
+ if (barrStart < rem)
+ {
+ for (; barrI < rem; barrI++)
+ {
+ temp <<= 8;
+ int barrBarrI = barr[barrI];
+ if (barrBarrI < 0)
+ {
+ barrBarrI += 256;
+ }
+ temp |= barrBarrI;
+ }
+ m_ints[iarrJ--] = temp;
+ }
+
+ for (; iarrJ >= 0; iarrJ--)
+ {
+ temp = 0;
+ for (int i = 0; i < 4; i++)
+ {
+ temp <<= 8;
+ int barrBarrI = barr[barrI++];
+ if (barrBarrI < 0)
+ {
+ barrBarrI += 256;
+ }
+ temp |= barrBarrI;
+ }
+ m_ints[iarrJ] = temp;
+ }
+ }
+
+ public boolean isZero()
+ {
+ return m_ints.length == 0
+ || (m_ints[0] == 0 && getUsedLength() == 0);
+ }
+
+ public int getUsedLength()
+ {
+ int highestIntPos = m_ints.length;
+
+ if (highestIntPos < 1)
+ {
+ return 0;
+ }
+
+ // Check if first element will act as sentinel
+ if (m_ints[0] != 0)
+ {
+ while (m_ints[--highestIntPos] == 0)
+ {
+ }
+ return highestIntPos + 1;
+ }
+
+ do
+ {
+ if (m_ints[--highestIntPos] != 0)
+ {
+ return highestIntPos + 1;
+ }
+ }
+ while (highestIntPos > 0);
+
+ return 0;
+ }
+
+ public int bitLength()
+ {
+ // JDK 1.5: see Integer.numberOfLeadingZeros()
+ int intLen = getUsedLength();
+ if (intLen == 0)
+ {
+ return 0;
+ }
+
+ int last = intLen - 1;
+ int highest = m_ints[last];
+ int bits = (last << 5) + 1;
+
+ // A couple of binary search steps
+ if ((highest & 0xffff0000) != 0)
+ {
+ if ((highest & 0xff000000) != 0)
+ {
+ bits += 24;
+ highest >>>= 24;
+ }
+ else
+ {
+ bits += 16;
+ highest >>>= 16;
+ }
+ }
+ else if (highest > 0x000000ff)
+ {
+ bits += 8;
+ highest >>>= 8;
+ }
+
+ while (highest != 1)
+ {
+ ++bits;
+ highest >>>= 1;
+ }
+
+ return bits;
+ }
+
+ private int[] resizedInts(int newLen)
+ {
+ int[] newInts = new int[newLen];
+ int oldLen = m_ints.length;
+ int copyLen = oldLen < newLen ? oldLen : newLen;
+ System.arraycopy(m_ints, 0, newInts, 0, copyLen);
+ return newInts;
+ }
+
+ public BigInteger toBigInteger()
+ {
+ int usedLen = getUsedLength();
+ if (usedLen == 0)
+ {
+ return ECConstants.ZERO;
+ }
+
+ int highestInt = m_ints[usedLen - 1];
+ byte[] temp = new byte[4];
+ int barrI = 0;
+ boolean trailingZeroBytesDone = false;
+ for (int j = 3; j >= 0; j--)
+ {
+ byte thisByte = (byte) (highestInt >>> (8 * j));
+ if (trailingZeroBytesDone || (thisByte != 0))
+ {
+ trailingZeroBytesDone = true;
+ temp[barrI++] = thisByte;
+ }
+ }
+
+ int barrLen = 4 * (usedLen - 1) + barrI;
+ byte[] barr = new byte[barrLen];
+ for (int j = 0; j < barrI; j++)
+ {
+ barr[j] = temp[j];
+ }
+ // Highest value int is done now
+
+ for (int iarrJ = usedLen - 2; iarrJ >= 0; iarrJ--)
+ {
+ for (int j = 3; j >= 0; j--)
+ {
+ barr[barrI++] = (byte) (m_ints[iarrJ] >>> (8 * j));
+ }
+ }
+ return new BigInteger(1, barr);
+ }
+
+ public void shiftLeft()
+ {
+ int usedLen = getUsedLength();
+ if (usedLen == 0)
+ {
+ return;
+ }
+ if (m_ints[usedLen - 1] < 0)
+ {
+ // highest bit of highest used byte is set, so shifting left will
+ // make the IntArray one byte longer
+ usedLen++;
+ if (usedLen > m_ints.length)
+ {
+ // make the m_ints one byte longer, because we need one more
+ // byte which is not available in m_ints
+ m_ints = resizedInts(m_ints.length + 1);
+ }
+ }
+
+ boolean carry = false;
+ for (int i = 0; i < usedLen; i++)
+ {
+ // nextCarry is true if highest bit is set
+ boolean nextCarry = m_ints[i] < 0;
+ m_ints[i] <<= 1;
+ if (carry)
+ {
+ // set lowest bit
+ m_ints[i] |= 1;
+ }
+ carry = nextCarry;
+ }
+ }
+
+ public IntArray shiftLeft(int n)
+ {
+ int usedLen = getUsedLength();
+ if (usedLen == 0)
+ {
+ return this;
+ }
+
+ if (n == 0)
+ {
+ return this;
+ }
+
+ if (n > 31)
+ {
+ throw new IllegalArgumentException("shiftLeft() for max 31 bits "
+ + ", " + n + "bit shift is not possible");
+ }
+
+ int[] newInts = new int[usedLen + 1];
+
+ int nm32 = 32 - n;
+ newInts[0] = m_ints[0] << n;
+ for (int i = 1; i < usedLen; i++)
+ {
+ newInts[i] = (m_ints[i] << n) | (m_ints[i - 1] >>> nm32);
+ }
+ newInts[usedLen] = m_ints[usedLen - 1] >>> nm32;
+
+ return new IntArray(newInts);
+ }
+
+ public void addShifted(IntArray other, int shift)
+ {
+ int usedLenOther = other.getUsedLength();
+ int newMinUsedLen = usedLenOther + shift;
+ if (newMinUsedLen > m_ints.length)
+ {
+ m_ints = resizedInts(newMinUsedLen);
+ //System.out.println("Resize required");
+ }
+
+ for (int i = 0; i < usedLenOther; i++)
+ {
+ m_ints[i + shift] ^= other.m_ints[i];
+ }
+ }
+
+ public int getLength()
+ {
+ return m_ints.length;
+ }
+
+ public boolean testBit(int n)
+ {
+ // theInt = n / 32
+ int theInt = n >> 5;
+ // theBit = n % 32
+ int theBit = n & 0x1F;
+ int tester = 1 << theBit;
+ return ((m_ints[theInt] & tester) != 0);
+ }
+
+ public void flipBit(int n)
+ {
+ // theInt = n / 32
+ int theInt = n >> 5;
+ // theBit = n % 32
+ int theBit = n & 0x1F;
+ int flipper = 1 << theBit;
+ m_ints[theInt] ^= flipper;
+ }
+
+ public void setBit(int n)
+ {
+ // theInt = n / 32
+ int theInt = n >> 5;
+ // theBit = n % 32
+ int theBit = n & 0x1F;
+ int setter = 1 << theBit;
+ m_ints[theInt] |= setter;
+ }
+
+ public IntArray multiply(IntArray other, int m)
+ {
+ // Lenght of c is 2m bits rounded up to the next int (32 bit)
+ int t = (m + 31) >> 5;
+ if (m_ints.length < t)
+ {
+ m_ints = resizedInts(t);
+ }
+
+ IntArray b = new IntArray(other.resizedInts(other.getLength() + 1));
+ IntArray c = new IntArray((m + m + 31) >> 5);
+ // IntArray c = new IntArray(t + t);
+ int testBit = 1;
+ for (int k = 0; k < 32; k++)
+ {
+ for (int j = 0; j < t; j++)
+ {
+ if ((m_ints[j] & testBit) != 0)
+ {
+ // The kth bit of m_ints[j] is set
+ c.addShifted(b, j);
+ }
+ }
+ testBit <<= 1;
+ b.shiftLeft();
+ }
+ return c;
+ }
+
+ // public IntArray multiplyLeftToRight(IntArray other, int m) {
+ // // Lenght of c is 2m bits rounded up to the next int (32 bit)
+ // int t = (m + 31) / 32;
+ // if (m_ints.length < t) {
+ // m_ints = resizedInts(t);
+ // }
+ //
+ // IntArray b = new IntArray(other.resizedInts(other.getLength() + 1));
+ // IntArray c = new IntArray((m + m + 31) / 32);
+ // // IntArray c = new IntArray(t + t);
+ // int testBit = 1 << 31;
+ // for (int k = 31; k >= 0; k--) {
+ // for (int j = 0; j < t; j++) {
+ // if ((m_ints[j] & testBit) != 0) {
+ // // The kth bit of m_ints[j] is set
+ // c.addShifted(b, j);
+ // }
+ // }
+ // testBit >>>= 1;
+ // if (k > 0) {
+ // c.shiftLeft();
+ // }
+ // }
+ // return c;
+ // }
+
+ // TODO note, redPol.length must be 3 for TPB and 5 for PPB
+ public void reduce(int m, int[] redPol)
+ {
+ for (int i = m + m - 2; i >= m; i--)
+ {
+ if (testBit(i))
+ {
+ int bit = i - m;
+ flipBit(bit);
+ flipBit(i);
+ int l = redPol.length;
+ while (--l >= 0)
+ {
+ flipBit(redPol[l] + bit);
+ }
+ }
+ }
+ m_ints = resizedInts((m + 31) >> 5);
+ }
+
+ public IntArray square(int m)
+ {
+ // TODO make the table static final
+ final int[] table = { 0x0, 0x1, 0x4, 0x5, 0x10, 0x11, 0x14, 0x15, 0x40,
+ 0x41, 0x44, 0x45, 0x50, 0x51, 0x54, 0x55 };
+
+ int t = (m + 31) >> 5;
+ if (m_ints.length < t)
+ {
+ m_ints = resizedInts(t);
+ }
+
+ IntArray c = new IntArray(t + t);
+
+ // TODO twice the same code, put in separate private method
+ for (int i = 0; i < t; i++)
+ {
+ int v0 = 0;
+ for (int j = 0; j < 4; j++)
+ {
+ v0 = v0 >>> 8;
+ int u = (m_ints[i] >>> (j * 4)) & 0xF;
+ int w = table[u] << 24;
+ v0 |= w;
+ }
+ c.m_ints[i + i] = v0;
+
+ v0 = 0;
+ int upper = m_ints[i] >>> 16;
+ for (int j = 0; j < 4; j++)
+ {
+ v0 = v0 >>> 8;
+ int u = (upper >>> (j * 4)) & 0xF;
+ int w = table[u] << 24;
+ v0 |= w;
+ }
+ c.m_ints[i + i + 1] = v0;
+ }
+ return c;
+ }
+
+ public boolean equals(Object o)
+ {
+ if (!(o instanceof IntArray))
+ {
+ return false;
+ }
+ IntArray other = (IntArray) o;
+ int usedLen = getUsedLength();
+ if (other.getUsedLength() != usedLen)
+ {
+ return false;
+ }
+ for (int i = 0; i < usedLen; i++)
+ {
+ if (m_ints[i] != other.m_ints[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public int hashCode()
+ {
+ int usedLen = getUsedLength();
+ int hash = 1;
+ for (int i = 0; i < usedLen; i++)
+ {
+ hash = hash * 31 + m_ints[i];
+ }
+ return hash;
+ }
+
+ public Object clone()
+ {
+ return new IntArray(Arrays.clone(m_ints));
+ }
+
+ public String toString()
+ {
+ int usedLen = getUsedLength();
+ if (usedLen == 0)
+ {
+ return "0";
+ }
+
+ StringBuffer sb = new StringBuffer(Integer
+ .toBinaryString(m_ints[usedLen - 1]));
+ for (int iarrJ = usedLen - 2; iarrJ >= 0; iarrJ--)
+ {
+ String hexString = Integer.toBinaryString(m_ints[iarrJ]);
+
+ // Add leading zeroes, except for highest significant int
+ for (int i = hexString.length(); i < 8; i++)
+ {
+ hexString = "0" + hexString;
+ }
+ sb.append(hexString);
+ }
+ return sb.toString();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/math/ec/PreCompInfo.java b/core/src/main/java/org/bouncycastle/math/ec/PreCompInfo.java
new file mode 100644
index 00000000..804dcf74
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/math/ec/PreCompInfo.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.math.ec;
+
+/**
+ * Interface for classes storing precomputation data for multiplication
+ * algorithms. Used as a Memento (see GOF patterns) for
+ * <code>WNafMultiplier</code>.
+ */
+interface PreCompInfo
+{
+}
diff --git a/core/src/main/java/org/bouncycastle/math/ec/ReferenceMultiplier.java b/core/src/main/java/org/bouncycastle/math/ec/ReferenceMultiplier.java
new file mode 100644
index 00000000..c1dd5483
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/math/ec/ReferenceMultiplier.java
@@ -0,0 +1,30 @@
+package org.bouncycastle.math.ec;
+
+import java.math.BigInteger;
+
+class ReferenceMultiplier implements ECMultiplier
+{
+ /**
+ * Simple shift-and-add multiplication. Serves as reference implementation
+ * to verify (possibly faster) implementations in
+ * {@link org.bouncycastle.math.ec.ECPoint ECPoint}.
+ *
+ * @param p The point to multiply.
+ * @param k The factor by which to multiply.
+ * @return The result of the point multiplication <code>k * p</code>.
+ */
+ public ECPoint multiply(ECPoint p, BigInteger k, PreCompInfo preCompInfo)
+ {
+ ECPoint q = p.getCurve().getInfinity();
+ int t = k.bitLength();
+ for (int i = 0; i < t; i++)
+ {
+ if (k.testBit(i))
+ {
+ q = q.add(p);
+ }
+ p = p.twice();
+ }
+ return q;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/math/ec/SimpleBigDecimal.java b/core/src/main/java/org/bouncycastle/math/ec/SimpleBigDecimal.java
new file mode 100644
index 00000000..96e666d1
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/math/ec/SimpleBigDecimal.java
@@ -0,0 +1,253 @@
+package org.bouncycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class representing a simple version of a big decimal. A
+ * <code>SimpleBigDecimal</code> is basically a
+ * {@link java.math.BigInteger BigInteger} with a few digits on the right of
+ * the decimal point. The number of (binary) digits on the right of the decimal
+ * point is called the <code>scale</code> of the <code>SimpleBigDecimal</code>.
+ * Unlike in {@link java.math.BigDecimal BigDecimal}, the scale is not adjusted
+ * automatically, but must be set manually. All <code>SimpleBigDecimal</code>s
+ * taking part in the same arithmetic operation must have equal scale. The
+ * result of a multiplication of two <code>SimpleBigDecimal</code>s returns a
+ * <code>SimpleBigDecimal</code> with double scale.
+ */
+class SimpleBigDecimal
+ //extends Number // not in J2ME - add compatibility class?
+{
+ private static final long serialVersionUID = 1L;
+
+ private final BigInteger bigInt;
+ private final int scale;
+
+ /**
+ * Returns a <code>SimpleBigDecimal</code> representing the same numerical
+ * value as <code>value</code>.
+ * @param value The value of the <code>SimpleBigDecimal</code> to be
+ * created.
+ * @param scale The scale of the <code>SimpleBigDecimal</code> to be
+ * created.
+ * @return The such created <code>SimpleBigDecimal</code>.
+ */
+ public static SimpleBigDecimal getInstance(BigInteger value, int scale)
+ {
+ return new SimpleBigDecimal(value.shiftLeft(scale), scale);
+ }
+
+ /**
+ * Constructor for <code>SimpleBigDecimal</code>. The value of the
+ * constructed <code>SimpleBigDecimal</code> equals <code>bigInt /
+ * 2<sup>scale</sup></code>.
+ * @param bigInt The <code>bigInt</code> value parameter.
+ * @param scale The scale of the constructed <code>SimpleBigDecimal</code>.
+ */
+ public SimpleBigDecimal(BigInteger bigInt, int scale)
+ {
+ if (scale < 0)
+ {
+ throw new IllegalArgumentException("scale may not be negative");
+ }
+
+ this.bigInt = bigInt;
+ this.scale = scale;
+ }
+
+ private SimpleBigDecimal(SimpleBigDecimal limBigDec)
+ {
+ bigInt = limBigDec.bigInt;
+ scale = limBigDec.scale;
+ }
+
+ private void checkScale(SimpleBigDecimal b)
+ {
+ if (scale != b.scale)
+ {
+ throw new IllegalArgumentException("Only SimpleBigDecimal of " +
+ "same scale allowed in arithmetic operations");
+ }
+ }
+
+ public SimpleBigDecimal adjustScale(int newScale)
+ {
+ if (newScale < 0)
+ {
+ throw new IllegalArgumentException("scale may not be negative");
+ }
+
+ if (newScale == scale)
+ {
+ return new SimpleBigDecimal(this);
+ }
+
+ return new SimpleBigDecimal(bigInt.shiftLeft(newScale - scale),
+ newScale);
+ }
+
+ public SimpleBigDecimal add(SimpleBigDecimal b)
+ {
+ checkScale(b);
+ return new SimpleBigDecimal(bigInt.add(b.bigInt), scale);
+ }
+
+ public SimpleBigDecimal add(BigInteger b)
+ {
+ return new SimpleBigDecimal(bigInt.add(b.shiftLeft(scale)), scale);
+ }
+
+ public SimpleBigDecimal negate()
+ {
+ return new SimpleBigDecimal(bigInt.negate(), scale);
+ }
+
+ public SimpleBigDecimal subtract(SimpleBigDecimal b)
+ {
+ return add(b.negate());
+ }
+
+ public SimpleBigDecimal subtract(BigInteger b)
+ {
+ return new SimpleBigDecimal(bigInt.subtract(b.shiftLeft(scale)),
+ scale);
+ }
+
+ public SimpleBigDecimal multiply(SimpleBigDecimal b)
+ {
+ checkScale(b);
+ return new SimpleBigDecimal(bigInt.multiply(b.bigInt), scale + scale);
+ }
+
+ public SimpleBigDecimal multiply(BigInteger b)
+ {
+ return new SimpleBigDecimal(bigInt.multiply(b), scale);
+ }
+
+ public SimpleBigDecimal divide(SimpleBigDecimal b)
+ {
+ checkScale(b);
+ BigInteger dividend = bigInt.shiftLeft(scale);
+ return new SimpleBigDecimal(dividend.divide(b.bigInt), scale);
+ }
+
+ public SimpleBigDecimal divide(BigInteger b)
+ {
+ return new SimpleBigDecimal(bigInt.divide(b), scale);
+ }
+
+ public SimpleBigDecimal shiftLeft(int n)
+ {
+ return new SimpleBigDecimal(bigInt.shiftLeft(n), scale);
+ }
+
+ public int compareTo(SimpleBigDecimal val)
+ {
+ checkScale(val);
+ return bigInt.compareTo(val.bigInt);
+ }
+
+ public int compareTo(BigInteger val)
+ {
+ return bigInt.compareTo(val.shiftLeft(scale));
+ }
+
+ public BigInteger floor()
+ {
+ return bigInt.shiftRight(scale);
+ }
+
+ public BigInteger round()
+ {
+ SimpleBigDecimal oneHalf = new SimpleBigDecimal(ECConstants.ONE, 1);
+ return add(oneHalf.adjustScale(scale)).floor();
+ }
+
+ public int intValue()
+ {
+ return floor().intValue();
+ }
+
+ public long longValue()
+ {
+ return floor().longValue();
+ }
+ /* NON-J2ME compliant.
+ public double doubleValue()
+ {
+ return Double.valueOf(toString()).doubleValue();
+ }
+
+ public float floatValue()
+ {
+ return Float.valueOf(toString()).floatValue();
+ }
+ */
+ public int getScale()
+ {
+ return scale;
+ }
+
+ public String toString()
+ {
+ if (scale == 0)
+ {
+ return bigInt.toString();
+ }
+
+ BigInteger floorBigInt = floor();
+
+ BigInteger fract = bigInt.subtract(floorBigInt.shiftLeft(scale));
+ if (bigInt.signum() == -1)
+ {
+ fract = ECConstants.ONE.shiftLeft(scale).subtract(fract);
+ }
+
+ if ((floorBigInt.signum() == -1) && (!(fract.equals(ECConstants.ZERO))))
+ {
+ floorBigInt = floorBigInt.add(ECConstants.ONE);
+ }
+ String leftOfPoint = floorBigInt.toString();
+
+ char[] fractCharArr = new char[scale];
+ String fractStr = fract.toString(2);
+ int fractLen = fractStr.length();
+ int zeroes = scale - fractLen;
+ for (int i = 0; i < zeroes; i++)
+ {
+ fractCharArr[i] = '0';
+ }
+ for (int j = 0; j < fractLen; j++)
+ {
+ fractCharArr[zeroes + j] = fractStr.charAt(j);
+ }
+ String rightOfPoint = new String(fractCharArr);
+
+ StringBuffer sb = new StringBuffer(leftOfPoint);
+ sb.append(".");
+ sb.append(rightOfPoint);
+
+ return sb.toString();
+ }
+
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ {
+ return true;
+ }
+
+ if (!(o instanceof SimpleBigDecimal))
+ {
+ return false;
+ }
+
+ SimpleBigDecimal other = (SimpleBigDecimal)o;
+ return ((bigInt.equals(other.bigInt)) && (scale == other.scale));
+ }
+
+ public int hashCode()
+ {
+ return bigInt.hashCode() ^ scale;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/math/ec/Tnaf.java b/core/src/main/java/org/bouncycastle/math/ec/Tnaf.java
new file mode 100644
index 00000000..af4355f3
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/math/ec/Tnaf.java
@@ -0,0 +1,844 @@
+package org.bouncycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class holding methods for point multiplication based on the window
+ * &tau;-adic nonadjacent form (WTNAF). The algorithms are based on the
+ * paper "Improved Algorithms for Arithmetic on Anomalous Binary Curves"
+ * by Jerome A. Solinas. The paper first appeared in the Proceedings of
+ * Crypto 1997.
+ */
+class Tnaf
+{
+ private static final BigInteger MINUS_ONE = ECConstants.ONE.negate();
+ private static final BigInteger MINUS_TWO = ECConstants.TWO.negate();
+ private static final BigInteger MINUS_THREE = ECConstants.THREE.negate();
+
+ /**
+ * The window width of WTNAF. The standard value of 4 is slightly less
+ * than optimal for running time, but keeps space requirements for
+ * precomputation low. For typical curves, a value of 5 or 6 results in
+ * a better running time. When changing this value, the
+ * <code>&alpha;<sub>u</sub></code>'s must be computed differently, see
+ * e.g. "Guide to Elliptic Curve Cryptography", Darrel Hankerson,
+ * Alfred Menezes, Scott Vanstone, Springer-Verlag New York Inc., 2004,
+ * p. 121-122
+ */
+ public static final byte WIDTH = 4;
+
+ /**
+ * 2<sup>4</sup>
+ */
+ public static final byte POW_2_WIDTH = 16;
+
+ /**
+ * The <code>&alpha;<sub>u</sub></code>'s for <code>a=0</code> as an array
+ * of <code>ZTauElement</code>s.
+ */
+ public static final ZTauElement[] alpha0 = {
+ null,
+ new ZTauElement(ECConstants.ONE, ECConstants.ZERO), null,
+ new ZTauElement(MINUS_THREE, MINUS_ONE), null,
+ new ZTauElement(MINUS_ONE, MINUS_ONE), null,
+ new ZTauElement(ECConstants.ONE, MINUS_ONE), null
+ };
+
+ /**
+ * The <code>&alpha;<sub>u</sub></code>'s for <code>a=0</code> as an array
+ * of TNAFs.
+ */
+ public static final byte[][] alpha0Tnaf = {
+ null, {1}, null, {-1, 0, 1}, null, {1, 0, 1}, null, {-1, 0, 0, 1}
+ };
+
+ /**
+ * The <code>&alpha;<sub>u</sub></code>'s for <code>a=1</code> as an array
+ * of <code>ZTauElement</code>s.
+ */
+ public static final ZTauElement[] alpha1 = {null,
+ new ZTauElement(ECConstants.ONE, ECConstants.ZERO), null,
+ new ZTauElement(MINUS_THREE, ECConstants.ONE), null,
+ new ZTauElement(MINUS_ONE, ECConstants.ONE), null,
+ new ZTauElement(ECConstants.ONE, ECConstants.ONE), null
+ };
+
+ /**
+ * The <code>&alpha;<sub>u</sub></code>'s for <code>a=1</code> as an array
+ * of TNAFs.
+ */
+ public static final byte[][] alpha1Tnaf = {
+ null, {1}, null, {-1, 0, 1}, null, {1, 0, 1}, null, {-1, 0, 0, -1}
+ };
+
+ /**
+ * Computes the norm of an element <code>&lambda;</code> of
+ * <code><b>Z</b>[&tau;]</code>.
+ * @param mu The parameter <code>&mu;</code> of the elliptic curve.
+ * @param lambda The element <code>&lambda;</code> of
+ * <code><b>Z</b>[&tau;]</code>.
+ * @return The norm of <code>&lambda;</code>.
+ */
+ public static BigInteger norm(final byte mu, ZTauElement lambda)
+ {
+ BigInteger norm;
+
+ // s1 = u^2
+ BigInteger s1 = lambda.u.multiply(lambda.u);
+
+ // s2 = u * v
+ BigInteger s2 = lambda.u.multiply(lambda.v);
+
+ // s3 = 2 * v^2
+ BigInteger s3 = lambda.v.multiply(lambda.v).shiftLeft(1);
+
+ if (mu == 1)
+ {
+ norm = s1.add(s2).add(s3);
+ }
+ else if (mu == -1)
+ {
+ norm = s1.subtract(s2).add(s3);
+ }
+ else
+ {
+ throw new IllegalArgumentException("mu must be 1 or -1");
+ }
+
+ return norm;
+ }
+
+ /**
+ * Computes the norm of an element <code>&lambda;</code> of
+ * <code><b>R</b>[&tau;]</code>, where <code>&lambda; = u + v&tau;</code>
+ * and <code>u</code> and <code>u</code> are real numbers (elements of
+ * <code><b>R</b></code>).
+ * @param mu The parameter <code>&mu;</code> of the elliptic curve.
+ * @param u The real part of the element <code>&lambda;</code> of
+ * <code><b>R</b>[&tau;]</code>.
+ * @param v The <code>&tau;</code>-adic part of the element
+ * <code>&lambda;</code> of <code><b>R</b>[&tau;]</code>.
+ * @return The norm of <code>&lambda;</code>.
+ */
+ public static SimpleBigDecimal norm(final byte mu, SimpleBigDecimal u,
+ SimpleBigDecimal v)
+ {
+ SimpleBigDecimal norm;
+
+ // s1 = u^2
+ SimpleBigDecimal s1 = u.multiply(u);
+
+ // s2 = u * v
+ SimpleBigDecimal s2 = u.multiply(v);
+
+ // s3 = 2 * v^2
+ SimpleBigDecimal s3 = v.multiply(v).shiftLeft(1);
+
+ if (mu == 1)
+ {
+ norm = s1.add(s2).add(s3);
+ }
+ else if (mu == -1)
+ {
+ norm = s1.subtract(s2).add(s3);
+ }
+ else
+ {
+ throw new IllegalArgumentException("mu must be 1 or -1");
+ }
+
+ return norm;
+ }
+
+ /**
+ * Rounds an element <code>&lambda;</code> of <code><b>R</b>[&tau;]</code>
+ * to an element of <code><b>Z</b>[&tau;]</code>, such that their difference
+ * has minimal norm. <code>&lambda;</code> is given as
+ * <code>&lambda; = &lambda;<sub>0</sub> + &lambda;<sub>1</sub>&tau;</code>.
+ * @param lambda0 The component <code>&lambda;<sub>0</sub></code>.
+ * @param lambda1 The component <code>&lambda;<sub>1</sub></code>.
+ * @param mu The parameter <code>&mu;</code> of the elliptic curve. Must
+ * equal 1 or -1.
+ * @return The rounded element of <code><b>Z</b>[&tau;]</code>.
+ * @throws IllegalArgumentException if <code>lambda0</code> and
+ * <code>lambda1</code> do not have same scale.
+ */
+ public static ZTauElement round(SimpleBigDecimal lambda0,
+ SimpleBigDecimal lambda1, byte mu)
+ {
+ int scale = lambda0.getScale();
+ if (lambda1.getScale() != scale)
+ {
+ throw new IllegalArgumentException("lambda0 and lambda1 do not " +
+ "have same scale");
+ }
+
+ if (!((mu == 1) || (mu == -1)))
+ {
+ throw new IllegalArgumentException("mu must be 1 or -1");
+ }
+
+ BigInteger f0 = lambda0.round();
+ BigInteger f1 = lambda1.round();
+
+ SimpleBigDecimal eta0 = lambda0.subtract(f0);
+ SimpleBigDecimal eta1 = lambda1.subtract(f1);
+
+ // eta = 2*eta0 + mu*eta1
+ SimpleBigDecimal eta = eta0.add(eta0);
+ if (mu == 1)
+ {
+ eta = eta.add(eta1);
+ }
+ else
+ {
+ // mu == -1
+ eta = eta.subtract(eta1);
+ }
+
+ // check1 = eta0 - 3*mu*eta1
+ // check2 = eta0 + 4*mu*eta1
+ SimpleBigDecimal threeEta1 = eta1.add(eta1).add(eta1);
+ SimpleBigDecimal fourEta1 = threeEta1.add(eta1);
+ SimpleBigDecimal check1;
+ SimpleBigDecimal check2;
+ if (mu == 1)
+ {
+ check1 = eta0.subtract(threeEta1);
+ check2 = eta0.add(fourEta1);
+ }
+ else
+ {
+ // mu == -1
+ check1 = eta0.add(threeEta1);
+ check2 = eta0.subtract(fourEta1);
+ }
+
+ byte h0 = 0;
+ byte h1 = 0;
+
+ // if eta >= 1
+ if (eta.compareTo(ECConstants.ONE) >= 0)
+ {
+ if (check1.compareTo(MINUS_ONE) < 0)
+ {
+ h1 = mu;
+ }
+ else
+ {
+ h0 = 1;
+ }
+ }
+ else
+ {
+ // eta < 1
+ if (check2.compareTo(ECConstants.TWO) >= 0)
+ {
+ h1 = mu;
+ }
+ }
+
+ // if eta < -1
+ if (eta.compareTo(MINUS_ONE) < 0)
+ {
+ if (check1.compareTo(ECConstants.ONE) >= 0)
+ {
+ h1 = (byte)-mu;
+ }
+ else
+ {
+ h0 = -1;
+ }
+ }
+ else
+ {
+ // eta >= -1
+ if (check2.compareTo(MINUS_TWO) < 0)
+ {
+ h1 = (byte)-mu;
+ }
+ }
+
+ BigInteger q0 = f0.add(BigInteger.valueOf(h0));
+ BigInteger q1 = f1.add(BigInteger.valueOf(h1));
+ return new ZTauElement(q0, q1);
+ }
+
+ /**
+ * Approximate division by <code>n</code>. For an integer
+ * <code>k</code>, the value <code>&lambda; = s k / n</code> is
+ * computed to <code>c</code> bits of accuracy.
+ * @param k The parameter <code>k</code>.
+ * @param s The curve parameter <code>s<sub>0</sub></code> or
+ * <code>s<sub>1</sub></code>.
+ * @param vm The Lucas Sequence element <code>V<sub>m</sub></code>.
+ * @param a The parameter <code>a</code> of the elliptic curve.
+ * @param m The bit length of the finite field
+ * <code><b>F</b><sub>m</sub></code>.
+ * @param c The number of bits of accuracy, i.e. the scale of the returned
+ * <code>SimpleBigDecimal</code>.
+ * @return The value <code>&lambda; = s k / n</code> computed to
+ * <code>c</code> bits of accuracy.
+ */
+ public static SimpleBigDecimal approximateDivisionByN(BigInteger k,
+ BigInteger s, BigInteger vm, byte a, int m, int c)
+ {
+ int _k = (m + 5)/2 + c;
+ BigInteger ns = k.shiftRight(m - _k - 2 + a);
+
+ BigInteger gs = s.multiply(ns);
+
+ BigInteger hs = gs.shiftRight(m);
+
+ BigInteger js = vm.multiply(hs);
+
+ BigInteger gsPlusJs = gs.add(js);
+ BigInteger ls = gsPlusJs.shiftRight(_k-c);
+ if (gsPlusJs.testBit(_k-c-1))
+ {
+ // round up
+ ls = ls.add(ECConstants.ONE);
+ }
+
+ return new SimpleBigDecimal(ls, c);
+ }
+
+ /**
+ * Computes the <code>&tau;</code>-adic NAF (non-adjacent form) of an
+ * element <code>&lambda;</code> of <code><b>Z</b>[&tau;]</code>.
+ * @param mu The parameter <code>&mu;</code> of the elliptic curve.
+ * @param lambda The element <code>&lambda;</code> of
+ * <code><b>Z</b>[&tau;]</code>.
+ * @return The <code>&tau;</code>-adic NAF of <code>&lambda;</code>.
+ */
+ public static byte[] tauAdicNaf(byte mu, ZTauElement lambda)
+ {
+ if (!((mu == 1) || (mu == -1)))
+ {
+ throw new IllegalArgumentException("mu must be 1 or -1");
+ }
+
+ BigInteger norm = norm(mu, lambda);
+
+ // Ceiling of log2 of the norm
+ int log2Norm = norm.bitLength();
+
+ // If length(TNAF) > 30, then length(TNAF) < log2Norm + 3.52
+ int maxLength = log2Norm > 30 ? log2Norm + 4 : 34;
+
+ // The array holding the TNAF
+ byte[] u = new byte[maxLength];
+ int i = 0;
+
+ // The actual length of the TNAF
+ int length = 0;
+
+ BigInteger r0 = lambda.u;
+ BigInteger r1 = lambda.v;
+
+ while(!((r0.equals(ECConstants.ZERO)) && (r1.equals(ECConstants.ZERO))))
+ {
+ // If r0 is odd
+ if (r0.testBit(0))
+ {
+ u[i] = (byte) ECConstants.TWO.subtract((r0.subtract(r1.shiftLeft(1))).mod(ECConstants.FOUR)).intValue();
+
+ // r0 = r0 - u[i]
+ if (u[i] == 1)
+ {
+ r0 = r0.clearBit(0);
+ }
+ else
+ {
+ // u[i] == -1
+ r0 = r0.add(ECConstants.ONE);
+ }
+ length = i;
+ }
+ else
+ {
+ u[i] = 0;
+ }
+
+ BigInteger t = r0;
+ BigInteger s = r0.shiftRight(1);
+ if (mu == 1)
+ {
+ r0 = r1.add(s);
+ }
+ else
+ {
+ // mu == -1
+ r0 = r1.subtract(s);
+ }
+
+ r1 = t.shiftRight(1).negate();
+ i++;
+ }
+
+ length++;
+
+ // Reduce the TNAF array to its actual length
+ byte[] tnaf = new byte[length];
+ System.arraycopy(u, 0, tnaf, 0, length);
+ return tnaf;
+ }
+
+ /**
+ * Applies the operation <code>&tau;()</code> to an
+ * <code>ECPoint.F2m</code>.
+ * @param p The ECPoint.F2m to which <code>&tau;()</code> is applied.
+ * @return <code>&tau;(p)</code>
+ */
+ public static ECPoint.F2m tau(ECPoint.F2m p)
+ {
+ if (p.isInfinity())
+ {
+ return p;
+ }
+
+ ECFieldElement x = p.getX();
+ ECFieldElement y = p.getY();
+
+ return new ECPoint.F2m(p.getCurve(), x.square(), y.square(), p.isCompressed());
+ }
+
+ /**
+ * Returns the parameter <code>&mu;</code> of the elliptic curve.
+ * @param curve The elliptic curve from which to obtain <code>&mu;</code>.
+ * The curve must be a Koblitz curve, i.e. <code>a</code> equals
+ * <code>0</code> or <code>1</code> and <code>b</code> equals
+ * <code>1</code>.
+ * @return <code>&mu;</code> of the elliptic curve.
+ * @throws IllegalArgumentException if the given ECCurve is not a Koblitz
+ * curve.
+ */
+ public static byte getMu(ECCurve.F2m curve)
+ {
+ BigInteger a = curve.getA().toBigInteger();
+ byte mu;
+
+ if (a.equals(ECConstants.ZERO))
+ {
+ mu = -1;
+ }
+ else if (a.equals(ECConstants.ONE))
+ {
+ mu = 1;
+ }
+ else
+ {
+ throw new IllegalArgumentException("No Koblitz curve (ABC), " +
+ "TNAF multiplication not possible");
+ }
+ return mu;
+ }
+
+ /**
+ * Calculates the Lucas Sequence elements <code>U<sub>k-1</sub></code> and
+ * <code>U<sub>k</sub></code> or <code>V<sub>k-1</sub></code> and
+ * <code>V<sub>k</sub></code>.
+ * @param mu The parameter <code>&mu;</code> of the elliptic curve.
+ * @param k The index of the second element of the Lucas Sequence to be
+ * returned.
+ * @param doV If set to true, computes <code>V<sub>k-1</sub></code> and
+ * <code>V<sub>k</sub></code>, otherwise <code>U<sub>k-1</sub></code> and
+ * <code>U<sub>k</sub></code>.
+ * @return An array with 2 elements, containing <code>U<sub>k-1</sub></code>
+ * and <code>U<sub>k</sub></code> or <code>V<sub>k-1</sub></code>
+ * and <code>V<sub>k</sub></code>.
+ */
+ public static BigInteger[] getLucas(byte mu, int k, boolean doV)
+ {
+ if (!((mu == 1) || (mu == -1)))
+ {
+ throw new IllegalArgumentException("mu must be 1 or -1");
+ }
+
+ BigInteger u0;
+ BigInteger u1;
+ BigInteger u2;
+
+ if (doV)
+ {
+ u0 = ECConstants.TWO;
+ u1 = BigInteger.valueOf(mu);
+ }
+ else
+ {
+ u0 = ECConstants.ZERO;
+ u1 = ECConstants.ONE;
+ }
+
+ for (int i = 1; i < k; i++)
+ {
+ // u2 = mu*u1 - 2*u0;
+ BigInteger s = null;
+ if (mu == 1)
+ {
+ s = u1;
+ }
+ else
+ {
+ // mu == -1
+ s = u1.negate();
+ }
+
+ u2 = s.subtract(u0.shiftLeft(1));
+ u0 = u1;
+ u1 = u2;
+// System.out.println(i + ": " + u2);
+// System.out.println();
+ }
+
+ BigInteger[] retVal = {u0, u1};
+ return retVal;
+ }
+
+ /**
+ * Computes the auxiliary value <code>t<sub>w</sub></code>. If the width is
+ * 4, then for <code>mu = 1</code>, <code>t<sub>w</sub> = 6</code> and for
+ * <code>mu = -1</code>, <code>t<sub>w</sub> = 10</code>
+ * @param mu The parameter <code>&mu;</code> of the elliptic curve.
+ * @param w The window width of the WTNAF.
+ * @return the auxiliary value <code>t<sub>w</sub></code>
+ */
+ public static BigInteger getTw(byte mu, int w)
+ {
+ if (w == 4)
+ {
+ if (mu == 1)
+ {
+ return BigInteger.valueOf(6);
+ }
+ else
+ {
+ // mu == -1
+ return BigInteger.valueOf(10);
+ }
+ }
+ else
+ {
+ // For w <> 4, the values must be computed
+ BigInteger[] us = getLucas(mu, w, false);
+ BigInteger twoToW = ECConstants.ZERO.setBit(w);
+ BigInteger u1invert = us[1].modInverse(twoToW);
+ BigInteger tw;
+ tw = ECConstants.TWO.multiply(us[0]).multiply(u1invert).mod(twoToW);
+// System.out.println("mu = " + mu);
+// System.out.println("tw = " + tw);
+ return tw;
+ }
+ }
+
+ /**
+ * Computes the auxiliary values <code>s<sub>0</sub></code> and
+ * <code>s<sub>1</sub></code> used for partial modular reduction.
+ * @param curve The elliptic curve for which to compute
+ * <code>s<sub>0</sub></code> and <code>s<sub>1</sub></code>.
+ * @throws IllegalArgumentException if <code>curve</code> is not a
+ * Koblitz curve (Anomalous Binary Curve, ABC).
+ */
+ public static BigInteger[] getSi(ECCurve.F2m curve)
+ {
+ if (!curve.isKoblitz())
+ {
+ throw new IllegalArgumentException("si is defined for Koblitz curves only");
+ }
+
+ int m = curve.getM();
+ int a = curve.getA().toBigInteger().intValue();
+ byte mu = curve.getMu();
+ int h = curve.getH().intValue();
+ int index = m + 3 - a;
+ BigInteger[] ui = getLucas(mu, index, false);
+
+ BigInteger dividend0;
+ BigInteger dividend1;
+ if (mu == 1)
+ {
+ dividend0 = ECConstants.ONE.subtract(ui[1]);
+ dividend1 = ECConstants.ONE.subtract(ui[0]);
+ }
+ else if (mu == -1)
+ {
+ dividend0 = ECConstants.ONE.add(ui[1]);
+ dividend1 = ECConstants.ONE.add(ui[0]);
+ }
+ else
+ {
+ throw new IllegalArgumentException("mu must be 1 or -1");
+ }
+
+ BigInteger[] si = new BigInteger[2];
+
+ if (h == 2)
+ {
+ si[0] = dividend0.shiftRight(1);
+ si[1] = dividend1.shiftRight(1).negate();
+ }
+ else if (h == 4)
+ {
+ si[0] = dividend0.shiftRight(2);
+ si[1] = dividend1.shiftRight(2).negate();
+ }
+ else
+ {
+ throw new IllegalArgumentException("h (Cofactor) must be 2 or 4");
+ }
+
+ return si;
+ }
+
+ /**
+ * Partial modular reduction modulo
+ * <code>(&tau;<sup>m</sup> - 1)/(&tau; - 1)</code>.
+ * @param k The integer to be reduced.
+ * @param m The bitlength of the underlying finite field.
+ * @param a The parameter <code>a</code> of the elliptic curve.
+ * @param s The auxiliary values <code>s<sub>0</sub></code> and
+ * <code>s<sub>1</sub></code>.
+ * @param mu The parameter &mu; of the elliptic curve.
+ * @param c The precision (number of bits of accuracy) of the partial
+ * modular reduction.
+ * @return <code>&rho; := k partmod (&tau;<sup>m</sup> - 1)/(&tau; - 1)</code>
+ */
+ public static ZTauElement partModReduction(BigInteger k, int m, byte a,
+ BigInteger[] s, byte mu, byte c)
+ {
+ // d0 = s[0] + mu*s[1]; mu is either 1 or -1
+ BigInteger d0;
+ if (mu == 1)
+ {
+ d0 = s[0].add(s[1]);
+ }
+ else
+ {
+ d0 = s[0].subtract(s[1]);
+ }
+
+ BigInteger[] v = getLucas(mu, m, true);
+ BigInteger vm = v[1];
+
+ SimpleBigDecimal lambda0 = approximateDivisionByN(
+ k, s[0], vm, a, m, c);
+
+ SimpleBigDecimal lambda1 = approximateDivisionByN(
+ k, s[1], vm, a, m, c);
+
+ ZTauElement q = round(lambda0, lambda1, mu);
+
+ // r0 = n - d0*q0 - 2*s1*q1
+ BigInteger r0 = k.subtract(d0.multiply(q.u)).subtract(
+ BigInteger.valueOf(2).multiply(s[1]).multiply(q.v));
+
+ // r1 = s1*q0 - s0*q1
+ BigInteger r1 = s[1].multiply(q.u).subtract(s[0].multiply(q.v));
+
+ return new ZTauElement(r0, r1);
+ }
+
+ /**
+ * Multiplies a {@link org.bouncycastle.math.ec.ECPoint.F2m ECPoint.F2m}
+ * by a <code>BigInteger</code> using the reduced <code>&tau;</code>-adic
+ * NAF (RTNAF) method.
+ * @param p The ECPoint.F2m to multiply.
+ * @param k The <code>BigInteger</code> by which to multiply <code>p</code>.
+ * @return <code>k * p</code>
+ */
+ public static ECPoint.F2m multiplyRTnaf(ECPoint.F2m p, BigInteger k)
+ {
+ ECCurve.F2m curve = (ECCurve.F2m) p.getCurve();
+ int m = curve.getM();
+ byte a = (byte) curve.getA().toBigInteger().intValue();
+ byte mu = curve.getMu();
+ BigInteger[] s = curve.getSi();
+ ZTauElement rho = partModReduction(k, m, a, s, mu, (byte)10);
+
+ return multiplyTnaf(p, rho);
+ }
+
+ /**
+ * Multiplies a {@link org.bouncycastle.math.ec.ECPoint.F2m ECPoint.F2m}
+ * by an element <code>&lambda;</code> of <code><b>Z</b>[&tau;]</code>
+ * using the <code>&tau;</code>-adic NAF (TNAF) method.
+ * @param p The ECPoint.F2m to multiply.
+ * @param lambda The element <code>&lambda;</code> of
+ * <code><b>Z</b>[&tau;]</code>.
+ * @return <code>&lambda; * p</code>
+ */
+ public static ECPoint.F2m multiplyTnaf(ECPoint.F2m p, ZTauElement lambda)
+ {
+ ECCurve.F2m curve = (ECCurve.F2m)p.getCurve();
+ byte mu = curve.getMu();
+ byte[] u = tauAdicNaf(mu, lambda);
+
+ ECPoint.F2m q = multiplyFromTnaf(p, u);
+
+ return q;
+ }
+
+ /**
+ * Multiplies a {@link org.bouncycastle.math.ec.ECPoint.F2m ECPoint.F2m}
+ * by an element <code>&lambda;</code> of <code><b>Z</b>[&tau;]</code>
+ * using the <code>&tau;</code>-adic NAF (TNAF) method, given the TNAF
+ * of <code>&lambda;</code>.
+ * @param p The ECPoint.F2m to multiply.
+ * @param u The the TNAF of <code>&lambda;</code>..
+ * @return <code>&lambda; * p</code>
+ */
+ public static ECPoint.F2m multiplyFromTnaf(ECPoint.F2m p, byte[] u)
+ {
+ ECCurve.F2m curve = (ECCurve.F2m)p.getCurve();
+ ECPoint.F2m q = (ECPoint.F2m) curve.getInfinity();
+ for (int i = u.length - 1; i >= 0; i--)
+ {
+ q = tau(q);
+ if (u[i] == 1)
+ {
+ q = (ECPoint.F2m)q.addSimple(p);
+ }
+ else if (u[i] == -1)
+ {
+ q = (ECPoint.F2m)q.subtractSimple(p);
+ }
+ }
+ return q;
+ }
+
+ /**
+ * Computes the <code>[&tau;]</code>-adic window NAF of an element
+ * <code>&lambda;</code> of <code><b>Z</b>[&tau;]</code>.
+ * @param mu The parameter &mu; of the elliptic curve.
+ * @param lambda The element <code>&lambda;</code> of
+ * <code><b>Z</b>[&tau;]</code> of which to compute the
+ * <code>[&tau;]</code>-adic NAF.
+ * @param width The window width of the resulting WNAF.
+ * @param pow2w 2<sup>width</sup>.
+ * @param tw The auxiliary value <code>t<sub>w</sub></code>.
+ * @param alpha The <code>&alpha;<sub>u</sub></code>'s for the window width.
+ * @return The <code>[&tau;]</code>-adic window NAF of
+ * <code>&lambda;</code>.
+ */
+ public static byte[] tauAdicWNaf(byte mu, ZTauElement lambda,
+ byte width, BigInteger pow2w, BigInteger tw, ZTauElement[] alpha)
+ {
+ if (!((mu == 1) || (mu == -1)))
+ {
+ throw new IllegalArgumentException("mu must be 1 or -1");
+ }
+
+ BigInteger norm = norm(mu, lambda);
+
+ // Ceiling of log2 of the norm
+ int log2Norm = norm.bitLength();
+
+ // If length(TNAF) > 30, then length(TNAF) < log2Norm + 3.52
+ int maxLength = log2Norm > 30 ? log2Norm + 4 + width : 34 + width;
+
+ // The array holding the TNAF
+ byte[] u = new byte[maxLength];
+
+ // 2^(width - 1)
+ BigInteger pow2wMin1 = pow2w.shiftRight(1);
+
+ // Split lambda into two BigIntegers to simplify calculations
+ BigInteger r0 = lambda.u;
+ BigInteger r1 = lambda.v;
+ int i = 0;
+
+ // while lambda <> (0, 0)
+ while (!((r0.equals(ECConstants.ZERO))&&(r1.equals(ECConstants.ZERO))))
+ {
+ // if r0 is odd
+ if (r0.testBit(0))
+ {
+ // uUnMod = r0 + r1*tw mod 2^width
+ BigInteger uUnMod
+ = r0.add(r1.multiply(tw)).mod(pow2w);
+
+ byte uLocal;
+ // if uUnMod >= 2^(width - 1)
+ if (uUnMod.compareTo(pow2wMin1) >= 0)
+ {
+ uLocal = (byte) uUnMod.subtract(pow2w).intValue();
+ }
+ else
+ {
+ uLocal = (byte) uUnMod.intValue();
+ }
+ // uLocal is now in [-2^(width-1), 2^(width-1)-1]
+
+ u[i] = uLocal;
+ boolean s = true;
+ if (uLocal < 0)
+ {
+ s = false;
+ uLocal = (byte)-uLocal;
+ }
+ // uLocal is now >= 0
+
+ if (s)
+ {
+ r0 = r0.subtract(alpha[uLocal].u);
+ r1 = r1.subtract(alpha[uLocal].v);
+ }
+ else
+ {
+ r0 = r0.add(alpha[uLocal].u);
+ r1 = r1.add(alpha[uLocal].v);
+ }
+ }
+ else
+ {
+ u[i] = 0;
+ }
+
+ BigInteger t = r0;
+
+ if (mu == 1)
+ {
+ r0 = r1.add(r0.shiftRight(1));
+ }
+ else
+ {
+ // mu == -1
+ r0 = r1.subtract(r0.shiftRight(1));
+ }
+ r1 = t.shiftRight(1).negate();
+ i++;
+ }
+ return u;
+ }
+
+ /**
+ * Does the precomputation for WTNAF multiplication.
+ * @param p The <code>ECPoint</code> for which to do the precomputation.
+ * @param a The parameter <code>a</code> of the elliptic curve.
+ * @return The precomputation array for <code>p</code>.
+ */
+ public static ECPoint.F2m[] getPreComp(ECPoint.F2m p, byte a)
+ {
+ ECPoint.F2m[] pu;
+ pu = new ECPoint.F2m[16];
+ pu[1] = p;
+ byte[][] alphaTnaf;
+ if (a == 0)
+ {
+ alphaTnaf = Tnaf.alpha0Tnaf;
+ }
+ else
+ {
+ // a == 1
+ alphaTnaf = Tnaf.alpha1Tnaf;
+ }
+
+ int precompLen = alphaTnaf.length;
+ for (int i = 3; i < precompLen; i = i + 2)
+ {
+ pu[i] = Tnaf.multiplyFromTnaf(p, alphaTnaf[i]);
+ }
+
+ return pu;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/math/ec/WNafMultiplier.java b/core/src/main/java/org/bouncycastle/math/ec/WNafMultiplier.java
new file mode 100644
index 00000000..10c8ed24
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/math/ec/WNafMultiplier.java
@@ -0,0 +1,240 @@
+package org.bouncycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class implementing the WNAF (Window Non-Adjacent Form) multiplication
+ * algorithm.
+ */
+class WNafMultiplier implements ECMultiplier
+{
+ /**
+ * Computes the Window NAF (non-adjacent Form) of an integer.
+ * @param width The width <code>w</code> of the Window NAF. The width is
+ * defined as the minimal number <code>w</code>, such that for any
+ * <code>w</code> consecutive digits in the resulting representation, at
+ * most one is non-zero.
+ * @param k The integer of which the Window NAF is computed.
+ * @return The Window NAF of the given width, such that the following holds:
+ * <code>k = &sum;<sub>i=0</sub><sup>l-1</sup> k<sub>i</sub>2<sup>i</sup>
+ * </code>, where the <code>k<sub>i</sub></code> denote the elements of the
+ * returned <code>byte[]</code>.
+ */
+ public byte[] windowNaf(byte width, BigInteger k)
+ {
+ // The window NAF is at most 1 element longer than the binary
+ // representation of the integer k. byte can be used instead of short or
+ // int unless the window width is larger than 8. For larger width use
+ // short or int. However, a width of more than 8 is not efficient for
+ // m = log2(q) smaller than 2305 Bits. Note: Values for m larger than
+ // 1000 Bits are currently not used in practice.
+ byte[] wnaf = new byte[k.bitLength() + 1];
+
+ // 2^width as short and BigInteger
+ short pow2wB = (short)(1 << width);
+ BigInteger pow2wBI = BigInteger.valueOf(pow2wB);
+
+ int i = 0;
+
+ // The actual length of the WNAF
+ int length = 0;
+
+ // while k >= 1
+ while (k.signum() > 0)
+ {
+ // if k is odd
+ if (k.testBit(0))
+ {
+ // k mod 2^width
+ BigInteger remainder = k.mod(pow2wBI);
+
+ // if remainder > 2^(width - 1) - 1
+ if (remainder.testBit(width - 1))
+ {
+ wnaf[i] = (byte)(remainder.intValue() - pow2wB);
+ }
+ else
+ {
+ wnaf[i] = (byte)remainder.intValue();
+ }
+ // wnaf[i] is now in [-2^(width-1), 2^(width-1)-1]
+
+ k = k.subtract(BigInteger.valueOf(wnaf[i]));
+ length = i;
+ }
+ else
+ {
+ wnaf[i] = 0;
+ }
+
+ // k = k/2
+ k = k.shiftRight(1);
+ i++;
+ }
+
+ length++;
+
+ // Reduce the WNAF array to its actual length
+ byte[] wnafShort = new byte[length];
+ System.arraycopy(wnaf, 0, wnafShort, 0, length);
+ return wnafShort;
+ }
+
+ /**
+ * Multiplies <code>this</code> by an integer <code>k</code> using the
+ * Window NAF method.
+ * @param k The integer by which <code>this</code> is multiplied.
+ * @return A new <code>ECPoint</code> which equals <code>this</code>
+ * multiplied by <code>k</code>.
+ */
+ public ECPoint multiply(ECPoint p, BigInteger k, PreCompInfo preCompInfo)
+ {
+ WNafPreCompInfo wnafPreCompInfo;
+
+ if ((preCompInfo != null) && (preCompInfo instanceof WNafPreCompInfo))
+ {
+ wnafPreCompInfo = (WNafPreCompInfo)preCompInfo;
+ }
+ else
+ {
+ // Ignore empty PreCompInfo or PreCompInfo of incorrect type
+ wnafPreCompInfo = new WNafPreCompInfo();
+ }
+
+ // floor(log2(k))
+ int m = k.bitLength();
+
+ // width of the Window NAF
+ byte width;
+
+ // Required length of precomputation array
+ int reqPreCompLen;
+
+ // Determine optimal width and corresponding length of precomputation
+ // array based on literature values
+ if (m < 13)
+ {
+ width = 2;
+ reqPreCompLen = 1;
+ }
+ else
+ {
+ if (m < 41)
+ {
+ width = 3;
+ reqPreCompLen = 2;
+ }
+ else
+ {
+ if (m < 121)
+ {
+ width = 4;
+ reqPreCompLen = 4;
+ }
+ else
+ {
+ if (m < 337)
+ {
+ width = 5;
+ reqPreCompLen = 8;
+ }
+ else
+ {
+ if (m < 897)
+ {
+ width = 6;
+ reqPreCompLen = 16;
+ }
+ else
+ {
+ if (m < 2305)
+ {
+ width = 7;
+ reqPreCompLen = 32;
+ }
+ else
+ {
+ width = 8;
+ reqPreCompLen = 127;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // The length of the precomputation array
+ int preCompLen = 1;
+
+ ECPoint[] preComp = wnafPreCompInfo.getPreComp();
+ ECPoint twiceP = wnafPreCompInfo.getTwiceP();
+
+ // Check if the precomputed ECPoints already exist
+ if (preComp == null)
+ {
+ // Precomputation must be performed from scratch, create an empty
+ // precomputation array of desired length
+ preComp = new ECPoint[]{ p };
+ }
+ else
+ {
+ // Take the already precomputed ECPoints to start with
+ preCompLen = preComp.length;
+ }
+
+ if (twiceP == null)
+ {
+ // Compute twice(p)
+ twiceP = p.twice();
+ }
+
+ if (preCompLen < reqPreCompLen)
+ {
+ // Precomputation array must be made bigger, copy existing preComp
+ // array into the larger new preComp array
+ ECPoint[] oldPreComp = preComp;
+ preComp = new ECPoint[reqPreCompLen];
+ System.arraycopy(oldPreComp, 0, preComp, 0, preCompLen);
+
+ for (int i = preCompLen; i < reqPreCompLen; i++)
+ {
+ // Compute the new ECPoints for the precomputation array.
+ // The values 1, 3, 5, ..., 2^(width-1)-1 times p are
+ // computed
+ preComp[i] = twiceP.add(preComp[i - 1]);
+ }
+ }
+
+ // Compute the Window NAF of the desired width
+ byte[] wnaf = windowNaf(width, k);
+ int l = wnaf.length;
+
+ // Apply the Window NAF to p using the precomputed ECPoint values.
+ ECPoint q = p.getCurve().getInfinity();
+ for (int i = l - 1; i >= 0; i--)
+ {
+ q = q.twice();
+
+ if (wnaf[i] != 0)
+ {
+ if (wnaf[i] > 0)
+ {
+ q = q.add(preComp[(wnaf[i] - 1)/2]);
+ }
+ else
+ {
+ // wnaf[i] < 0
+ q = q.subtract(preComp[(-wnaf[i] - 1)/2]);
+ }
+ }
+ }
+
+ // Set PreCompInfo in ECPoint, such that it is available for next
+ // multiplication.
+ wnafPreCompInfo.setPreComp(preComp);
+ wnafPreCompInfo.setTwiceP(twiceP);
+ p.setPreCompInfo(wnafPreCompInfo);
+ return q;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/math/ec/WNafPreCompInfo.java b/core/src/main/java/org/bouncycastle/math/ec/WNafPreCompInfo.java
new file mode 100644
index 00000000..fc0d5fe9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/math/ec/WNafPreCompInfo.java
@@ -0,0 +1,44 @@
+package org.bouncycastle.math.ec;
+
+/**
+ * Class holding precomputation data for the WNAF (Window Non-Adjacent Form)
+ * algorithm.
+ */
+class WNafPreCompInfo implements PreCompInfo
+{
+ /**
+ * Array holding the precomputed <code>ECPoint</code>s used for the Window
+ * NAF multiplication in <code>
+ * {@link org.bouncycastle.math.ec.multiplier.WNafMultiplier.multiply()
+ * WNafMultiplier.multiply()}</code>.
+ */
+ private ECPoint[] preComp = null;
+
+ /**
+ * Holds an <code>ECPoint</code> representing twice(this). Used for the
+ * Window NAF multiplication in <code>
+ * {@link org.bouncycastle.math.ec.multiplier.WNafMultiplier.multiply()
+ * WNafMultiplier.multiply()}</code>.
+ */
+ private ECPoint twiceP = null;
+
+ protected ECPoint[] getPreComp()
+ {
+ return preComp;
+ }
+
+ protected void setPreComp(ECPoint[] preComp)
+ {
+ this.preComp = preComp;
+ }
+
+ protected ECPoint getTwiceP()
+ {
+ return twiceP;
+ }
+
+ protected void setTwiceP(ECPoint twiceThis)
+ {
+ this.twiceP = twiceThis;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java b/core/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java
new file mode 100644
index 00000000..2353979e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java
@@ -0,0 +1,119 @@
+package org.bouncycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class implementing the WTNAF (Window
+ * <code>&tau;</code>-adic Non-Adjacent Form) algorithm.
+ */
+class WTauNafMultiplier implements ECMultiplier
+{
+ /**
+ * Multiplies a {@link org.bouncycastle.math.ec.ECPoint.F2m ECPoint.F2m}
+ * by <code>k</code> using the reduced <code>&tau;</code>-adic NAF (RTNAF)
+ * method.
+ * @param p The ECPoint.F2m to multiply.
+ * @param k The integer by which to multiply <code>k</code>.
+ * @return <code>p</code> multiplied by <code>k</code>.
+ */
+ public ECPoint multiply(ECPoint point, BigInteger k, PreCompInfo preCompInfo)
+ {
+ if (!(point instanceof ECPoint.F2m))
+ {
+ throw new IllegalArgumentException("Only ECPoint.F2m can be " +
+ "used in WTauNafMultiplier");
+ }
+
+ ECPoint.F2m p = (ECPoint.F2m)point;
+
+ ECCurve.F2m curve = (ECCurve.F2m) p.getCurve();
+ int m = curve.getM();
+ byte a = curve.getA().toBigInteger().byteValue();
+ byte mu = curve.getMu();
+ BigInteger[] s = curve.getSi();
+
+ ZTauElement rho = Tnaf.partModReduction(k, m, a, s, mu, (byte)10);
+
+ return multiplyWTnaf(p, rho, preCompInfo, a, mu);
+ }
+
+ /**
+ * Multiplies a {@link org.bouncycastle.math.ec.ECPoint.F2m ECPoint.F2m}
+ * by an element <code>&lambda;</code> of <code><b>Z</b>[&tau;]</code> using
+ * the <code>&tau;</code>-adic NAF (TNAF) method.
+ * @param p The ECPoint.F2m to multiply.
+ * @param lambda The element <code>&lambda;</code> of
+ * <code><b>Z</b>[&tau;]</code> of which to compute the
+ * <code>[&tau;]</code>-adic NAF.
+ * @return <code>p</code> multiplied by <code>&lambda;</code>.
+ */
+ private ECPoint.F2m multiplyWTnaf(ECPoint.F2m p, ZTauElement lambda,
+ PreCompInfo preCompInfo, byte a, byte mu)
+ {
+ ZTauElement[] alpha;
+ if (a == 0)
+ {
+ alpha = Tnaf.alpha0;
+ }
+ else
+ {
+ // a == 1
+ alpha = Tnaf.alpha1;
+ }
+
+ BigInteger tw = Tnaf.getTw(mu, Tnaf.WIDTH);
+
+ byte[]u = Tnaf.tauAdicWNaf(mu, lambda, Tnaf.WIDTH,
+ BigInteger.valueOf(Tnaf.POW_2_WIDTH), tw, alpha);
+
+ return multiplyFromWTnaf(p, u, preCompInfo);
+ }
+
+ /**
+ * Multiplies a {@link org.bouncycastle.math.ec.ECPoint.F2m ECPoint.F2m}
+ * by an element <code>&lambda;</code> of <code><b>Z</b>[&tau;]</code>
+ * using the window <code>&tau;</code>-adic NAF (TNAF) method, given the
+ * WTNAF of <code>&lambda;</code>.
+ * @param p The ECPoint.F2m to multiply.
+ * @param u The the WTNAF of <code>&lambda;</code>..
+ * @return <code>&lambda; * p</code>
+ */
+ private static ECPoint.F2m multiplyFromWTnaf(ECPoint.F2m p, byte[] u,
+ PreCompInfo preCompInfo)
+ {
+ ECCurve.F2m curve = (ECCurve.F2m)p.getCurve();
+ byte a = curve.getA().toBigInteger().byteValue();
+
+ ECPoint.F2m[] pu;
+ if ((preCompInfo == null) || !(preCompInfo instanceof WTauNafPreCompInfo))
+ {
+ pu = Tnaf.getPreComp(p, a);
+ p.setPreCompInfo(new WTauNafPreCompInfo(pu));
+ }
+ else
+ {
+ pu = ((WTauNafPreCompInfo)preCompInfo).getPreComp();
+ }
+
+ // q = infinity
+ ECPoint.F2m q = (ECPoint.F2m) p.getCurve().getInfinity();
+ for (int i = u.length - 1; i >= 0; i--)
+ {
+ q = Tnaf.tau(q);
+ if (u[i] != 0)
+ {
+ if (u[i] > 0)
+ {
+ q = q.addSimple(pu[u[i]]);
+ }
+ else
+ {
+ // u[i] < 0
+ q = q.subtractSimple(pu[-u[i]]);
+ }
+ }
+ }
+
+ return q;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/math/ec/WTauNafPreCompInfo.java b/core/src/main/java/org/bouncycastle/math/ec/WTauNafPreCompInfo.java
new file mode 100644
index 00000000..d7c583f1
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/math/ec/WTauNafPreCompInfo.java
@@ -0,0 +1,39 @@
+package org.bouncycastle.math.ec;
+
+/**
+ * Class holding precomputation data for the WTNAF (Window
+ * <code>&tau;</code>-adic Non-Adjacent Form) algorithm.
+ */
+class WTauNafPreCompInfo implements PreCompInfo
+{
+ /**
+ * Array holding the precomputed <code>ECPoint.F2m</code>s used for the
+ * WTNAF multiplication in <code>
+ * {@link org.bouncycastle.math.ec.multiplier.WTauNafMultiplier.multiply()
+ * WTauNafMultiplier.multiply()}</code>.
+ */
+ private ECPoint.F2m[] preComp = null;
+
+ /**
+ * Constructor for <code>WTauNafPreCompInfo</code>
+ * @param preComp Array holding the precomputed <code>ECPoint.F2m</code>s
+ * used for the WTNAF multiplication in <code>
+ * {@link org.bouncycastle.math.ec.multiplier.WTauNafMultiplier.multiply()
+ * WTauNafMultiplier.multiply()}</code>.
+ */
+ WTauNafPreCompInfo(ECPoint.F2m[] preComp)
+ {
+ this.preComp = preComp;
+ }
+
+ /**
+ * @return the array holding the precomputed <code>ECPoint.F2m</code>s
+ * used for the WTNAF multiplication in <code>
+ * {@link org.bouncycastle.math.ec.multiplier.WTauNafMultiplier.multiply()
+ * WTauNafMultiplier.multiply()}</code>.
+ */
+ protected ECPoint.F2m[] getPreComp()
+ {
+ return preComp;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/math/ec/ZTauElement.java b/core/src/main/java/org/bouncycastle/math/ec/ZTauElement.java
new file mode 100644
index 00000000..7402f222
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/math/ec/ZTauElement.java
@@ -0,0 +1,37 @@
+package org.bouncycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class representing an element of <code><b>Z</b>[&tau;]</code>. Let
+ * <code>&lambda;</code> be an element of <code><b>Z</b>[&tau;]</code>. Then
+ * <code>&lambda;</code> is given as <code>&lambda; = u + v&tau;</code>. The
+ * components <code>u</code> and <code>v</code> may be used directly, there
+ * are no accessor methods.
+ * Immutable class.
+ */
+class ZTauElement
+{
+ /**
+ * The &quot;real&quot; part of <code>&lambda;</code>.
+ */
+ public final BigInteger u;
+
+ /**
+ * The &quot;<code>&tau;</code>-adic&quot; part of <code>&lambda;</code>.
+ */
+ public final BigInteger v;
+
+ /**
+ * Constructor for an element <code>&lambda;</code> of
+ * <code><b>Z</b>[&tau;]</code>.
+ * @param u The &quot;real&quot; part of <code>&lambda;</code>.
+ * @param v The &quot;<code>&tau;</code>-adic&quot; part of
+ * <code>&lambda;</code>.
+ */
+ public ZTauElement(BigInteger u, BigInteger v)
+ {
+ this.u = u;
+ this.v = v;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/asn1/GMSSPrivateKey.java b/core/src/main/java/org/bouncycastle/pqc/asn1/GMSSPrivateKey.java
new file mode 100644
index 00000000..4e182c59
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/asn1/GMSSPrivateKey.java
@@ -0,0 +1,1312 @@
+package org.bouncycastle.pqc.asn1;
+
+import java.math.BigInteger;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.pqc.crypto.gmss.GMSSLeaf;
+import org.bouncycastle.pqc.crypto.gmss.GMSSParameters;
+import org.bouncycastle.pqc.crypto.gmss.GMSSRootCalc;
+import org.bouncycastle.pqc.crypto.gmss.GMSSRootSig;
+import org.bouncycastle.pqc.crypto.gmss.Treehash;
+
+public class GMSSPrivateKey
+ extends ASN1Object
+{
+ private ASN1Primitive primitive;
+
+ private GMSSPrivateKey(ASN1Sequence mtsPrivateKey)
+ {
+ // --- Decode <index>.
+ ASN1Sequence indexPart = (ASN1Sequence)mtsPrivateKey.getObjectAt(0);
+ int[] index = new int[indexPart.size()];
+ for (int i = 0; i < indexPart.size(); i++)
+ {
+ index[i] = checkBigIntegerInIntRange(indexPart.getObjectAt(i));
+ }
+
+ // --- Decode <curSeeds>.
+ ASN1Sequence curSeedsPart = (ASN1Sequence)mtsPrivateKey.getObjectAt(1);
+ byte[][] curSeeds = new byte[curSeedsPart.size()][];
+ for (int i = 0; i < curSeeds.length; i++)
+ {
+ curSeeds[i] = ((DEROctetString)curSeedsPart.getObjectAt(i)).getOctets();
+ }
+
+ // --- Decode <nextNextSeeds>.
+ ASN1Sequence nextNextSeedsPart = (ASN1Sequence)mtsPrivateKey.getObjectAt(2);
+ byte[][] nextNextSeeds = new byte[nextNextSeedsPart.size()][];
+ for (int i = 0; i < nextNextSeeds.length; i++)
+ {
+ nextNextSeeds[i] = ((DEROctetString)nextNextSeedsPart.getObjectAt(i)).getOctets();
+ }
+
+ // --- Decode <curAuth>.
+ ASN1Sequence curAuthPart0 = (ASN1Sequence)mtsPrivateKey.getObjectAt(3);
+ ASN1Sequence curAuthPart1;
+
+ byte[][][] curAuth = new byte[curAuthPart0.size()][][];
+ for (int i = 0; i < curAuth.length; i++)
+ {
+ curAuthPart1 = (ASN1Sequence)curAuthPart0.getObjectAt(i);
+ curAuth[i] = new byte[curAuthPart1.size()][];
+ for (int j = 0; j < curAuth[i].length; j++)
+ {
+ curAuth[i][j] = ((DEROctetString)curAuthPart1.getObjectAt(j)).getOctets();
+ }
+ }
+
+ // --- Decode <nextAuth>.
+ ASN1Sequence nextAuthPart0 = (ASN1Sequence)mtsPrivateKey.getObjectAt(4);
+ ASN1Sequence nextAuthPart1;
+
+ byte[][][] nextAuth = new byte[nextAuthPart0.size()][][];
+ for (int i = 0; i < nextAuth.length; i++)
+ {
+ nextAuthPart1 = (ASN1Sequence)nextAuthPart0.getObjectAt(i);
+ nextAuth[i] = new byte[nextAuthPart1.size()][];
+ for (int j = 0; j < nextAuth[i].length; j++)
+ {
+ nextAuth[i][j] = ((DEROctetString)nextAuthPart1.getObjectAt(j)).getOctets();
+ }
+ }
+
+ // --- Decode <curTreehash>.
+ ASN1Sequence seqOfcurTreehash0 = (ASN1Sequence)mtsPrivateKey.getObjectAt(5);
+ ASN1Sequence seqOfcurTreehash1;
+ ASN1Sequence seqOfcurTreehashStat;
+ ASN1Sequence seqOfcurTreehashBytes;
+ ASN1Sequence seqOfcurTreehashInts;
+ ASN1Sequence seqOfcurTreehashString;
+
+ Treehash[][] curTreehash = new Treehash[seqOfcurTreehash0.size()][];
+ /*
+ for (int i = 0; i < curTreehash.length; i++)
+ {
+ seqOfcurTreehash1 = (ASN1Sequence)seqOfcurTreehash0.getObjectAt(i);
+ curTreehash[i] = new Treehash[seqOfcurTreehash1.size()];
+ for (int j = 0; j < curTreehash[i].length; j++)
+ {
+ seqOfcurTreehashStat = (ASN1Sequence)seqOfcurTreehash1.getObjectAt(j);
+ seqOfcurTreehashString = (ASN1Sequence)seqOfcurTreehashStat
+ .getObjectAt(0);
+ seqOfcurTreehashBytes = (ASN1Sequence)seqOfcurTreehashStat
+ .getObjectAt(1);
+ seqOfcurTreehashInts = (ASN1Sequence)seqOfcurTreehashStat
+ .getObjectAt(2);
+
+ String[] name = new String[2];
+ name[0] = ((DERIA5String)seqOfcurTreehashString.getObjectAt(0)).getString();
+ name[1] = ((DERIA5String)seqOfcurTreehashString.getObjectAt(1)).getString();
+
+ int tailLength = checkBigIntegerInIntRange(seqOfcurTreehashInts.getObjectAt(1));
+ byte[][] statByte = new byte[3 + tailLength][];
+ statByte[0] = ((DEROctetString)seqOfcurTreehashBytes.getObjectAt(0)).getOctets();
+
+ if (statByte[0].length == 0)
+ { // if null was encoded
+ statByte[0] = null;
+ }
+
+ statByte[1] = ((DEROctetString)seqOfcurTreehashBytes.getObjectAt(1)).getOctets();
+ statByte[2] = ((DEROctetString)seqOfcurTreehashBytes.getObjectAt(2)).getOctets();
+ for (int k = 0; k < tailLength; k++)
+ {
+ statByte[3 + k] = ((DEROctetString)seqOfcurTreehashBytes
+ .getObjectAt(3 + k)).getOctets();
+ }
+ int[] statInt = new int[6 + tailLength];
+ statInt[0] = checkBigIntegerInIntRange(seqOfcurTreehashInts.getObjectAt(0));
+ statInt[1] = tailLength;
+ statInt[2] = checkBigIntegerInIntRange(seqOfcurTreehashInts.getObjectAt(2));
+ statInt[3] = checkBigIntegerInIntRange(seqOfcurTreehashInts.getObjectAt(3));
+ statInt[4] = checkBigIntegerInIntRange(seqOfcurTreehashInts.getObjectAt(4));
+ statInt[5] = checkBigIntegerInIntRange(seqOfcurTreehashInts.getObjectAt(5));
+ for (int k = 0; k < tailLength; k++)
+ {
+ statInt[6 + k] = checkBigIntegerInIntRange(seqOfcurTreehashInts.getObjectAt(6 + k));
+ }
+
+ // TODO: Check if we can do better than throwing away name[1] !!!
+ curTreehash[i][j] = new Treehash(DigestFactory.getDigest(name[0]).getClass(), statByte, statInt);
+ }
+ }
+
+
+ // --- Decode <nextTreehash>.
+ ASN1Sequence seqOfNextTreehash0 = (ASN1Sequence)mtsPrivateKey.getObjectAt(6);
+ ASN1Sequence seqOfNextTreehash1;
+ ASN1Sequence seqOfNextTreehashStat;
+ ASN1Sequence seqOfNextTreehashBytes;
+ ASN1Sequence seqOfNextTreehashInts;
+ ASN1Sequence seqOfNextTreehashString;
+
+ Treehash[][] nextTreehash = new Treehash[seqOfNextTreehash0.size()][];
+
+ for (int i = 0; i < nextTreehash.length; i++)
+ {
+ seqOfNextTreehash1 = (ASN1Sequence)seqOfNextTreehash0.getObjectAt(i);
+ nextTreehash[i] = new Treehash[seqOfNextTreehash1.size()];
+ for (int j = 0; j < nextTreehash[i].length; j++)
+ {
+ seqOfNextTreehashStat = (ASN1Sequence)seqOfNextTreehash1
+ .getObjectAt(j);
+ seqOfNextTreehashString = (ASN1Sequence)seqOfNextTreehashStat
+ .getObjectAt(0);
+ seqOfNextTreehashBytes = (ASN1Sequence)seqOfNextTreehashStat
+ .getObjectAt(1);
+ seqOfNextTreehashInts = (ASN1Sequence)seqOfNextTreehashStat
+ .getObjectAt(2);
+
+ String[] name = new String[2];
+ name[0] = ((DERIA5String)seqOfNextTreehashString.getObjectAt(0))
+ .getString();
+ name[1] = ((DERIA5String)seqOfNextTreehashString.getObjectAt(1))
+ .getString();
+
+ int tailLength = checkBigIntegerInIntRange(seqOfNextTreehashInts.getObjectAt(1));
+
+ byte[][] statByte = new byte[3 + tailLength][];
+ statByte[0] = ((DEROctetString)seqOfNextTreehashBytes.getObjectAt(0)).getOctets();
+ if (statByte[0].length == 0)
+ { // if null was encoded
+ statByte[0] = null;
+ }
+
+ statByte[1] = ((DEROctetString)seqOfNextTreehashBytes.getObjectAt(1)).getOctets();
+ statByte[2] = ((DEROctetString)seqOfNextTreehashBytes.getObjectAt(2)).getOctets();
+ for (int k = 0; k < tailLength; k++)
+ {
+ statByte[3 + k] = ((DEROctetString)seqOfNextTreehashBytes
+ .getObjectAt(3 + k)).getOctets();
+ }
+ int[] statInt = new int[6 + tailLength];
+ statInt[0] = checkBigIntegerInIntRange(seqOfNextTreehashInts.getObjectAt(0));
+
+ statInt[1] = tailLength;
+ statInt[2] = checkBigIntegerInIntRange(seqOfNextTreehashInts.getObjectAt(2));
+
+ statInt[3] = checkBigIntegerInIntRange(seqOfNextTreehashInts.getObjectAt(3));
+
+ statInt[4] = checkBigIntegerInIntRange(seqOfNextTreehashInts.getObjectAt(4));
+
+ statInt[5] = checkBigIntegerInIntRange(seqOfNextTreehashInts.getObjectAt(5));
+
+ for (int k = 0; k < tailLength; k++)
+ {
+ statInt[6 + k] = checkBigIntegerInIntRange(seqOfNextTreehashInts.getObjectAt(6 + k));
+
+ }
+ nextTreehash[i][j] = new Treehash(DigestFactory.getDigest(name[0]).getClass(), statByte, statInt);
+ }
+ }
+
+
+ // --- Decode <keep>.
+ ASN1Sequence keepPart0 = (ASN1Sequence)mtsPrivateKey.getObjectAt(7);
+ ASN1Sequence keepPart1;
+
+ byte[][][] keep = new byte[keepPart0.size()][][];
+ for (int i = 0; i < keep.length; i++)
+ {
+ keepPart1 = (ASN1Sequence)keepPart0.getObjectAt(i);
+ keep[i] = new byte[keepPart1.size()][];
+ for (int j = 0; j < keep[i].length; j++)
+ {
+ keep[i][j] = ((DEROctetString)keepPart1.getObjectAt(j)).getOctets();
+ }
+ }
+
+ // --- Decode <curStack>.
+ ASN1Sequence curStackPart0 = (ASN1Sequence)mtsPrivateKey.getObjectAt(8);
+ ASN1Sequence curStackPart1;
+
+ Vector[] curStack = new Vector[curStackPart0.size()];
+ for (int i = 0; i < curStack.length; i++)
+ {
+ curStackPart1 = (ASN1Sequence)curStackPart0.getObjectAt(i);
+ curStack[i] = new Vector();
+ for (int j = 0; j < curStackPart1.size(); j++)
+ {
+ curStack[i].addElement(((DEROctetString)curStackPart1.getObjectAt(j)).getOctets());
+ }
+ }
+
+ // --- Decode <nextStack>.
+ ASN1Sequence nextStackPart0 = (ASN1Sequence)mtsPrivateKey.getObjectAt(9);
+ ASN1Sequence nextStackPart1;
+
+ Vector[] nextStack = new Vector[nextStackPart0.size()];
+ for (int i = 0; i < nextStack.length; i++)
+ {
+ nextStackPart1 = (ASN1Sequence)nextStackPart0.getObjectAt(i);
+ nextStack[i] = new Vector();
+ for (int j = 0; j < nextStackPart1.size(); j++)
+ {
+ nextStack[i].addElement(((DEROctetString)nextStackPart1
+ .getObjectAt(j)).getOctets());
+ }
+ }
+
+ // --- Decode <curRetain>.
+ ASN1Sequence curRetainPart0 = (ASN1Sequence)mtsPrivateKey.getObjectAt(10);
+ ASN1Sequence curRetainPart1;
+ ASN1Sequence curRetainPart2;
+
+ Vector[][] curRetain = new Vector[curRetainPart0.size()][];
+ for (int i = 0; i < curRetain.length; i++)
+ {
+ curRetainPart1 = (ASN1Sequence)curRetainPart0.getObjectAt(i);
+ curRetain[i] = new Vector[curRetainPart1.size()];
+ for (int j = 0; j < curRetain[i].length; j++)
+ {
+ curRetainPart2 = (ASN1Sequence)curRetainPart1.getObjectAt(j);
+ curRetain[i][j] = new Vector();
+ for (int k = 0; k < curRetainPart2.size(); k++)
+ {
+ curRetain[i][j]
+ .addElement(((DEROctetString)curRetainPart2
+ .getObjectAt(k)).getOctets());
+ }
+ }
+ }
+
+ // --- Decode <nextRetain>.
+ ASN1Sequence nextRetainPart0 = (ASN1Sequence)mtsPrivateKey.getObjectAt(11);
+ ASN1Sequence nextRetainPart1;
+ ASN1Sequence nextRetainPart2;
+
+ Vector[][] nextRetain = new Vector[nextRetainPart0.size()][];
+ for (int i = 0; i < nextRetain.length; i++)
+ {
+ nextRetainPart1 = (ASN1Sequence)nextRetainPart0.getObjectAt(i);
+ nextRetain[i] = new Vector[nextRetainPart1.size()];
+ for (int j = 0; j < nextRetain[i].length; j++)
+ {
+ nextRetainPart2 = (ASN1Sequence)nextRetainPart1.getObjectAt(j);
+ nextRetain[i][j] = new Vector();
+ for (int k = 0; k < nextRetainPart2.size(); k++)
+ {
+ nextRetain[i][j]
+ .addElement(((DEROctetString)nextRetainPart2
+ .getObjectAt(k)).getOctets());
+ }
+ }
+ }
+
+ // --- Decode <nextNextLeaf>.
+ ASN1Sequence seqOfLeafs = (ASN1Sequence)mtsPrivateKey.getObjectAt(12);
+ ASN1Sequence seqOfLeafStat;
+ ASN1Sequence seqOfLeafBytes;
+ ASN1Sequence seqOfLeafInts;
+ ASN1Sequence seqOfLeafString;
+
+ GMSSLeaf[] nextNextLeaf = new GMSSLeaf[seqOfLeafs.size()];
+
+ for (int i = 0; i < nextNextLeaf.length; i++)
+ {
+ seqOfLeafStat = (ASN1Sequence)seqOfLeafs.getObjectAt(i);
+ // nextNextAuth[i]= new byte[nextNextAuthPart1.size()][];
+ seqOfLeafString = (ASN1Sequence)seqOfLeafStat.getObjectAt(0);
+ seqOfLeafBytes = (ASN1Sequence)seqOfLeafStat.getObjectAt(1);
+ seqOfLeafInts = (ASN1Sequence)seqOfLeafStat.getObjectAt(2);
+
+ String[] name = new String[2];
+ name[0] = ((DERIA5String)seqOfLeafString.getObjectAt(0)).getString();
+ name[1] = ((DERIA5String)seqOfLeafString.getObjectAt(1)).getString();
+ byte[][] statByte = new byte[4][];
+ statByte[0] = ((DEROctetString)seqOfLeafBytes.getObjectAt(0))
+ .getOctets();
+ statByte[1] = ((DEROctetString)seqOfLeafBytes.getObjectAt(1))
+ .getOctets();
+ statByte[2] = ((DEROctetString)seqOfLeafBytes.getObjectAt(2))
+ .getOctets();
+ statByte[3] = ((DEROctetString)seqOfLeafBytes.getObjectAt(3))
+ .getOctets();
+ int[] statInt = new int[4];
+ statInt[0] = checkBigIntegerInIntRange(seqOfLeafInts.getObjectAt(0));
+ statInt[1] = checkBigIntegerInIntRange(seqOfLeafInts.getObjectAt(1));
+ statInt[2] = checkBigIntegerInIntRange(seqOfLeafInts.getObjectAt(2));
+ statInt[3] = checkBigIntegerInIntRange(seqOfLeafInts.getObjectAt(3));
+ nextNextLeaf[i] = new GMSSLeaf(DigestFactory.getDigest(name[0]).getClass(), statByte, statInt);
+ }
+
+ // --- Decode <upperLeaf>.
+ ASN1Sequence seqOfUpperLeafs = (ASN1Sequence)mtsPrivateKey.getObjectAt(13);
+ ASN1Sequence seqOfUpperLeafStat;
+ ASN1Sequence seqOfUpperLeafBytes;
+ ASN1Sequence seqOfUpperLeafInts;
+ ASN1Sequence seqOfUpperLeafString;
+
+ GMSSLeaf[] upperLeaf = new GMSSLeaf[seqOfUpperLeafs.size()];
+
+ for (int i = 0; i < upperLeaf.length; i++)
+ {
+ seqOfUpperLeafStat = (ASN1Sequence)seqOfUpperLeafs.getObjectAt(i);
+ seqOfUpperLeafString = (ASN1Sequence)seqOfUpperLeafStat.getObjectAt(0);
+ seqOfUpperLeafBytes = (ASN1Sequence)seqOfUpperLeafStat.getObjectAt(1);
+ seqOfUpperLeafInts = (ASN1Sequence)seqOfUpperLeafStat.getObjectAt(2);
+
+ String[] name = new String[2];
+ name[0] = ((DERIA5String)seqOfUpperLeafString.getObjectAt(0)).getString();
+ name[1] = ((DERIA5String)seqOfUpperLeafString.getObjectAt(1)).getString();
+ byte[][] statByte = new byte[4][];
+ statByte[0] = ((DEROctetString)seqOfUpperLeafBytes.getObjectAt(0))
+ .getOctets();
+ statByte[1] = ((DEROctetString)seqOfUpperLeafBytes.getObjectAt(1))
+ .getOctets();
+ statByte[2] = ((DEROctetString)seqOfUpperLeafBytes.getObjectAt(2))
+ .getOctets();
+ statByte[3] = ((DEROctetString)seqOfUpperLeafBytes.getObjectAt(3))
+ .getOctets();
+ int[] statInt = new int[4];
+ statInt[0] = checkBigIntegerInIntRange(seqOfUpperLeafInts.getObjectAt(0));
+ statInt[1] = checkBigIntegerInIntRange(seqOfUpperLeafInts.getObjectAt(1));
+ statInt[2] = checkBigIntegerInIntRange(seqOfUpperLeafInts.getObjectAt(2));
+ statInt[3] = checkBigIntegerInIntRange(seqOfUpperLeafInts.getObjectAt(3));
+ upperLeaf[i] = new GMSSLeaf(DigestFactory.getDigest(name[0]).getClass(), statByte, statInt);
+ }
+
+ // --- Decode <upperTreehashLeaf>.
+ ASN1Sequence seqOfUpperTHLeafs = (ASN1Sequence)mtsPrivateKey.getObjectAt(14);
+ ASN1Sequence seqOfUpperTHLeafStat;
+ ASN1Sequence seqOfUpperTHLeafBytes;
+ ASN1Sequence seqOfUpperTHLeafInts;
+ ASN1Sequence seqOfUpperTHLeafString;
+
+ GMSSLeaf[] upperTHLeaf = new GMSSLeaf[seqOfUpperTHLeafs.size()];
+
+ for (int i = 0; i < upperTHLeaf.length; i++)
+ {
+ seqOfUpperTHLeafStat = (ASN1Sequence)seqOfUpperTHLeafs.getObjectAt(i);
+ seqOfUpperTHLeafString = (ASN1Sequence)seqOfUpperTHLeafStat.getObjectAt(0);
+ seqOfUpperTHLeafBytes = (ASN1Sequence)seqOfUpperTHLeafStat.getObjectAt(1);
+ seqOfUpperTHLeafInts = (ASN1Sequence)seqOfUpperTHLeafStat.getObjectAt(2);
+
+ String[] name = new String[2];
+ name[0] = ((DERIA5String)seqOfUpperTHLeafString.getObjectAt(0))
+ .getString();
+ name[1] = ((DERIA5String)seqOfUpperTHLeafString.getObjectAt(1))
+ .getString();
+ byte[][] statByte = new byte[4][];
+ statByte[0] = ((DEROctetString)seqOfUpperTHLeafBytes.getObjectAt(0))
+ .getOctets();
+ statByte[1] = ((DEROctetString)seqOfUpperTHLeafBytes.getObjectAt(1))
+ .getOctets();
+ statByte[2] = ((DEROctetString)seqOfUpperTHLeafBytes.getObjectAt(2))
+ .getOctets();
+ statByte[3] = ((DEROctetString)seqOfUpperTHLeafBytes.getObjectAt(3))
+ .getOctets();
+ int[] statInt = new int[4];
+ statInt[0] = checkBigIntegerInIntRange(seqOfUpperTHLeafInts.getObjectAt(0));
+ statInt[1] = checkBigIntegerInIntRange(seqOfUpperTHLeafInts.getObjectAt(1));
+ statInt[2] = checkBigIntegerInIntRange(seqOfUpperTHLeafInts.getObjectAt(2));
+ statInt[3] = checkBigIntegerInIntRange(seqOfUpperTHLeafInts.getObjectAt(3));
+ upperTHLeaf[i] = new GMSSLeaf(DigestFactory.getDigest(name[0]).getClass(), statByte, statInt);
+ }
+
+ // --- Decode <minTreehash>.
+ ASN1Sequence minTreehashPart = (ASN1Sequence)mtsPrivateKey.getObjectAt(15);
+ int[] minTreehash = new int[minTreehashPart.size()];
+ for (int i = 0; i < minTreehashPart.size(); i++)
+ {
+ minTreehash[i] = checkBigIntegerInIntRange(minTreehashPart.getObjectAt(i));
+ }
+
+ // --- Decode <nextRoot>.
+ ASN1Sequence seqOfnextRoots = (ASN1Sequence)mtsPrivateKey.getObjectAt(16);
+ byte[][] nextRoot = new byte[seqOfnextRoots.size()][];
+ for (int i = 0; i < nextRoot.length; i++)
+ {
+ nextRoot[i] = ((DEROctetString)seqOfnextRoots.getObjectAt(i))
+ .getOctets();
+ }
+
+ // --- Decode <nextNextRoot>.
+ ASN1Sequence seqOfnextNextRoot = (ASN1Sequence)mtsPrivateKey.getObjectAt(17);
+ ASN1Sequence seqOfnextNextRootStat;
+ ASN1Sequence seqOfnextNextRootBytes;
+ ASN1Sequence seqOfnextNextRootInts;
+ ASN1Sequence seqOfnextNextRootString;
+ ASN1Sequence seqOfnextNextRootTreeH;
+ ASN1Sequence seqOfnextNextRootRetain;
+
+ GMSSRootCalc[] nextNextRoot = new GMSSRootCalc[seqOfnextNextRoot.size()];
+
+ for (int i = 0; i < nextNextRoot.length; i++)
+ {
+ seqOfnextNextRootStat = (ASN1Sequence)seqOfnextNextRoot.getObjectAt(i);
+ seqOfnextNextRootString = (ASN1Sequence)seqOfnextNextRootStat
+ .getObjectAt(0);
+ seqOfnextNextRootBytes = (ASN1Sequence)seqOfnextNextRootStat
+ .getObjectAt(1);
+ seqOfnextNextRootInts = (ASN1Sequence)seqOfnextNextRootStat.getObjectAt(2);
+ seqOfnextNextRootTreeH = (ASN1Sequence)seqOfnextNextRootStat
+ .getObjectAt(3);
+ seqOfnextNextRootRetain = (ASN1Sequence)seqOfnextNextRootStat
+ .getObjectAt(4);
+
+ // decode treehash of nextNextRoot
+ // ---------------------------------
+ ASN1Sequence seqOfnextNextRootTreeHStat;
+ ASN1Sequence seqOfnextNextRootTreeHBytes;
+ ASN1Sequence seqOfnextNextRootTreeHInts;
+ ASN1Sequence seqOfnextNextRootTreeHString;
+
+ Treehash[] nnRTreehash = new Treehash[seqOfnextNextRootTreeH.size()];
+
+ for (int k = 0; k < nnRTreehash.length; k++)
+ {
+ seqOfnextNextRootTreeHStat = (ASN1Sequence)seqOfnextNextRootTreeH
+ .getObjectAt(k);
+ seqOfnextNextRootTreeHString = (ASN1Sequence)seqOfnextNextRootTreeHStat
+ .getObjectAt(0);
+ seqOfnextNextRootTreeHBytes = (ASN1Sequence)seqOfnextNextRootTreeHStat
+ .getObjectAt(1);
+ seqOfnextNextRootTreeHInts = (ASN1Sequence)seqOfnextNextRootTreeHStat
+ .getObjectAt(2);
+
+ String[] name = new String[2];
+ name[0] = ((DERIA5String)seqOfnextNextRootTreeHString.getObjectAt(0))
+ .getString();
+ name[1] = ((DERIA5String)seqOfnextNextRootTreeHString.getObjectAt(1))
+ .getString();
+
+ int tailLength = checkBigIntegerInIntRange(seqOfnextNextRootTreeHInts.getObjectAt(1));
+
+ byte[][] statByte = new byte[3 + tailLength][];
+ statByte[0] = ((DEROctetString)seqOfnextNextRootTreeHBytes
+ .getObjectAt(0)).getOctets();
+ if (statByte[0].length == 0)
+ { // if null was encoded
+ statByte[0] = null;
+ }
+
+ statByte[1] = ((DEROctetString)seqOfnextNextRootTreeHBytes
+ .getObjectAt(1)).getOctets();
+ statByte[2] = ((DEROctetString)seqOfnextNextRootTreeHBytes
+ .getObjectAt(2)).getOctets();
+ for (int j = 0; j < tailLength; j++)
+ {
+ statByte[3 + j] = ((DEROctetString)seqOfnextNextRootTreeHBytes
+ .getObjectAt(3 + j)).getOctets();
+ }
+ int[] statInt = new int[6 + tailLength];
+ statInt[0] = checkBigIntegerInIntRange(seqOfnextNextRootTreeHInts.getObjectAt(0));
+
+ statInt[1] = tailLength;
+ statInt[2] = checkBigIntegerInIntRange(seqOfnextNextRootTreeHInts.getObjectAt(2));
+
+ statInt[3] = checkBigIntegerInIntRange(seqOfnextNextRootTreeHInts.getObjectAt(3));
+
+ statInt[4] = checkBigIntegerInIntRange(seqOfnextNextRootTreeHInts.getObjectAt(4));
+
+ statInt[5] = checkBigIntegerInIntRange(seqOfnextNextRootTreeHInts.getObjectAt(5));
+
+ for (int j = 0; j < tailLength; j++)
+ {
+ statInt[6 + j] = checkBigIntegerInIntRange(seqOfnextNextRootTreeHInts
+ .getObjectAt(6 + j));
+ }
+ nnRTreehash[k] = new Treehash(DigestFactory.getDigest(name[0]).getClass(), statByte, statInt);
+ }
+ // ---------------------------------
+
+ // decode retain of nextNextRoot
+ // ---------------------------------
+ // ASN1Sequence seqOfnextNextRootRetainPart0 =
+ // (ASN1Sequence)seqOfnextNextRootRetain.get(0);
+ ASN1Sequence seqOfnextNextRootRetainPart1;
+
+ Vector[] nnRRetain = new Vector[seqOfnextNextRootRetain.size()];
+ for (int j = 0; j < nnRRetain.length; j++)
+ {
+ seqOfnextNextRootRetainPart1 = (ASN1Sequence)seqOfnextNextRootRetain
+ .getObjectAt(j);
+ nnRRetain[j] = new Vector();
+ for (int k = 0; k < seqOfnextNextRootRetainPart1.size(); k++)
+ {
+ nnRRetain[j]
+ .addElement(((DEROctetString)seqOfnextNextRootRetainPart1
+ .getObjectAt(k)).getOctets());
+ }
+ }
+ // ---------------------------------
+
+ String[] name = new String[2];
+ name[0] = ((DERIA5String)seqOfnextNextRootString.getObjectAt(0))
+ .getString();
+ name[1] = ((DERIA5String)seqOfnextNextRootString.getObjectAt(1))
+ .getString();
+
+ int heightOfTree = checkBigIntegerInIntRange(seqOfnextNextRootInts.getObjectAt(0));
+ int tailLength = checkBigIntegerInIntRange(seqOfnextNextRootInts.getObjectAt(7));
+ byte[][] statByte = new byte[1 + heightOfTree + tailLength][];
+ statByte[0] = ((DEROctetString)seqOfnextNextRootBytes.getObjectAt(0))
+ .getOctets();
+ for (int j = 0; j < heightOfTree; j++)
+ {
+ statByte[1 + j] = ((DEROctetString)seqOfnextNextRootBytes
+ .getObjectAt(1 + j)).getOctets();
+ }
+ for (int j = 0; j < tailLength; j++)
+ {
+ statByte[1 + heightOfTree + j] = ((DEROctetString)seqOfnextNextRootBytes
+ .getObjectAt(1 + heightOfTree + j)).getOctets();
+ }
+ int[] statInt = new int[8 + heightOfTree + tailLength];
+ statInt[0] = heightOfTree;
+ statInt[1] = checkBigIntegerInIntRange(seqOfnextNextRootInts.getObjectAt(1));
+ statInt[2] = checkBigIntegerInIntRange(seqOfnextNextRootInts.getObjectAt(2));
+ statInt[3] = checkBigIntegerInIntRange(seqOfnextNextRootInts.getObjectAt(3));
+ statInt[4] = checkBigIntegerInIntRange(seqOfnextNextRootInts.getObjectAt(4));
+ statInt[5] = checkBigIntegerInIntRange(seqOfnextNextRootInts.getObjectAt(5));
+ statInt[6] = checkBigIntegerInIntRange(seqOfnextNextRootInts.getObjectAt(6));
+ statInt[7] = tailLength;
+ for (int j = 0; j < heightOfTree; j++)
+ {
+ statInt[8 + j] = checkBigIntegerInIntRange(seqOfnextNextRootInts.getObjectAt(8 + j));
+ }
+ for (int j = 0; j < tailLength; j++)
+ {
+ statInt[8 + heightOfTree + j] = checkBigIntegerInIntRange(seqOfnextNextRootInts.getObjectAt(8
+ + heightOfTree + j));
+ }
+ nextNextRoot[i] = new GMSSRootCalc(DigestFactory.getDigest(name[0]).getClass(), statByte, statInt,
+ nnRTreehash, nnRRetain);
+ }
+
+ // --- Decode <curRootSig>.
+ ASN1Sequence seqOfcurRootSig = (ASN1Sequence)mtsPrivateKey.getObjectAt(18);
+ byte[][] curRootSig = new byte[seqOfcurRootSig.size()][];
+ for (int i = 0; i < curRootSig.length; i++)
+ {
+ curRootSig[i] = ((DEROctetString)seqOfcurRootSig.getObjectAt(i))
+ .getOctets();
+ }
+
+ // --- Decode <nextRootSig>.
+ ASN1Sequence seqOfnextRootSigs = (ASN1Sequence)mtsPrivateKey.getObjectAt(19);
+ ASN1Sequence seqOfnRSStats;
+ ASN1Sequence seqOfnRSStrings;
+ ASN1Sequence seqOfnRSInts;
+ ASN1Sequence seqOfnRSBytes;
+
+ GMSSRootSig[] nextRootSig = new GMSSRootSig[seqOfnextRootSigs.size()];
+
+ for (int i = 0; i < nextRootSig.length; i++)
+ {
+ seqOfnRSStats = (ASN1Sequence)seqOfnextRootSigs.getObjectAt(i);
+ // nextNextAuth[i]= new byte[nextNextAuthPart1.size()][];
+ seqOfnRSStrings = (ASN1Sequence)seqOfnRSStats.getObjectAt(0);
+ seqOfnRSBytes = (ASN1Sequence)seqOfnRSStats.getObjectAt(1);
+ seqOfnRSInts = (ASN1Sequence)seqOfnRSStats.getObjectAt(2);
+
+ String[] name = new String[2];
+ name[0] = ((DERIA5String)seqOfnRSStrings.getObjectAt(0)).getString();
+ name[1] = ((DERIA5String)seqOfnRSStrings.getObjectAt(1)).getString();
+ byte[][] statByte = new byte[5][];
+ statByte[0] = ((DEROctetString)seqOfnRSBytes.getObjectAt(0))
+ .getOctets();
+ statByte[1] = ((DEROctetString)seqOfnRSBytes.getObjectAt(1))
+ .getOctets();
+ statByte[2] = ((DEROctetString)seqOfnRSBytes.getObjectAt(2))
+ .getOctets();
+ statByte[3] = ((DEROctetString)seqOfnRSBytes.getObjectAt(3))
+ .getOctets();
+ statByte[4] = ((DEROctetString)seqOfnRSBytes.getObjectAt(4))
+ .getOctets();
+ int[] statInt = new int[9];
+ statInt[0] = checkBigIntegerInIntRange(seqOfnRSInts.getObjectAt(0));
+ statInt[1] = checkBigIntegerInIntRange(seqOfnRSInts.getObjectAt(1));
+ statInt[2] = checkBigIntegerInIntRange(seqOfnRSInts.getObjectAt(2));
+ statInt[3] = checkBigIntegerInIntRange(seqOfnRSInts.getObjectAt(3));
+ statInt[4] = checkBigIntegerInIntRange(seqOfnRSInts.getObjectAt(4));
+ statInt[5] = checkBigIntegerInIntRange(seqOfnRSInts.getObjectAt(5));
+ statInt[6] = checkBigIntegerInIntRange(seqOfnRSInts.getObjectAt(6));
+ statInt[7] = checkBigIntegerInIntRange(seqOfnRSInts.getObjectAt(7));
+ statInt[8] = checkBigIntegerInIntRange(seqOfnRSInts.getObjectAt(8));
+ nextRootSig[i] = new GMSSRootSig(DigestFactory.getDigest(name[0]).getClass(), statByte, statInt);
+ }
+
+ // --- Decode <name>.
+
+ // TODO: Really check, why there are multiple algorithms, we only
+ // use the first one!!!
+ ASN1Sequence namePart = (ASN1Sequence)mtsPrivateKey.getObjectAt(20);
+ String[] name = new String[namePart.size()];
+ for (int i = 0; i < name.length; i++)
+ {
+ name[i] = ((DERIA5String)namePart.getObjectAt(i)).getString();
+ }
+ */
+ }
+
+ public GMSSPrivateKey(int[] index, byte[][] currentSeed,
+ byte[][] nextNextSeed, byte[][][] currentAuthPath,
+ byte[][][] nextAuthPath, Treehash[][] currentTreehash,
+ Treehash[][] nextTreehash, Vector[] currentStack,
+ Vector[] nextStack, Vector[][] currentRetain,
+ Vector[][] nextRetain, byte[][][] keep, GMSSLeaf[] nextNextLeaf,
+ GMSSLeaf[] upperLeaf, GMSSLeaf[] upperTreehashLeaf,
+ int[] minTreehash, byte[][] nextRoot, GMSSRootCalc[] nextNextRoot,
+ byte[][] currentRootSig, GMSSRootSig[] nextRootSig,
+ GMSSParameters gmssParameterset, AlgorithmIdentifier digestAlg)
+ {
+ AlgorithmIdentifier[] names = new AlgorithmIdentifier[] { digestAlg };
+ this.primitive = encode(index, currentSeed, nextNextSeed, currentAuthPath, nextAuthPath, keep, currentTreehash, nextTreehash, currentStack, nextStack, currentRetain, nextRetain, nextNextLeaf, upperLeaf, upperTreehashLeaf, minTreehash, nextRoot, nextNextRoot, currentRootSig, nextRootSig, gmssParameterset, names);
+ }
+
+
+ // TODO: change method signature to something more integrated into BouncyCastle
+
+ /**
+ * @param index tree indices
+ * @param currentSeeds seed for the generation of private OTS keys for the
+ * current subtrees (TREE)
+ * @param nextNextSeeds seed for the generation of private OTS keys for the
+ * subtrees after next (TREE++)
+ * @param currentAuthPaths array of current authentication paths (AUTHPATH)
+ * @param nextAuthPaths array of next authentication paths (AUTHPATH+)
+ * @param keep keep array for the authPath algorithm
+ * @param currentTreehash treehash for authPath algorithm of current tree
+ * @param nextTreehash treehash for authPath algorithm of next tree (TREE+)
+ * @param currentStack shared stack for authPath algorithm of current tree
+ * @param nextStack shared stack for authPath algorithm of next tree (TREE+)
+ * @param currentRetain retain stack for authPath algorithm of current tree
+ * @param nextRetain retain stack for authPath algorithm of next tree (TREE+)
+ * @param nextNextLeaf array of upcoming leafs of the tree after next (LEAF++) of
+ * each layer
+ * @param upperLeaf needed for precomputation of upper nodes
+ * @param upperTreehashLeaf needed for precomputation of upper treehash nodes
+ * @param minTreehash index of next treehash instance to receive an update
+ * @param nextRoot the roots of the next trees (ROOT+)
+ * @param nextNextRoot the roots of the tree after next (ROOT++)
+ * @param currentRootSig array of signatures of the roots of the current subtrees
+ * (SIG)
+ * @param nextRootSig array of signatures of the roots of the next subtree
+ * (SIG+)
+ * @param gmssParameterset the GMSS Parameterset
+ * @param algorithms An array of algorithm identifiers, containing the hash function details
+ */
+ private ASN1Primitive encode(int[] index, byte[][] currentSeeds,
+ byte[][] nextNextSeeds, byte[][][] currentAuthPaths,
+ byte[][][] nextAuthPaths, byte[][][] keep,
+ Treehash[][] currentTreehash, Treehash[][] nextTreehash,
+ Vector[] currentStack, Vector[] nextStack,
+ Vector[][] currentRetain, Vector[][] nextRetain,
+ GMSSLeaf[] nextNextLeaf, GMSSLeaf[] upperLeaf,
+ GMSSLeaf[] upperTreehashLeaf, int[] minTreehash, byte[][] nextRoot,
+ GMSSRootCalc[] nextNextRoot, byte[][] currentRootSig,
+ GMSSRootSig[] nextRootSig, GMSSParameters gmssParameterset,
+ AlgorithmIdentifier[] algorithms)
+ {
+
+ ASN1EncodableVector result = new ASN1EncodableVector();
+
+ // --- Encode <index>.
+ ASN1EncodableVector indexPart = new ASN1EncodableVector();
+ for (int i = 0; i < index.length; i++)
+ {
+ indexPart.add(new ASN1Integer(index[i]));
+ }
+ result.add(new DERSequence(indexPart));
+
+ // --- Encode <curSeeds>.
+ ASN1EncodableVector curSeedsPart = new ASN1EncodableVector();
+ for (int i = 0; i < currentSeeds.length; i++)
+ {
+ curSeedsPart.add(new DEROctetString(currentSeeds[i]));
+ }
+ result.add(new DERSequence(curSeedsPart));
+
+ // --- Encode <nextNextSeeds>.
+ ASN1EncodableVector nextNextSeedsPart = new ASN1EncodableVector();
+ for (int i = 0; i < nextNextSeeds.length; i++)
+ {
+ nextNextSeedsPart.add(new DEROctetString(nextNextSeeds[i]));
+ }
+ result.add(new DERSequence(nextNextSeedsPart));
+
+ // --- Encode <curAuth>.
+ ASN1EncodableVector curAuthPart0 = new ASN1EncodableVector();
+ ASN1EncodableVector curAuthPart1 = new ASN1EncodableVector();
+ for (int i = 0; i < currentAuthPaths.length; i++)
+ {
+ for (int j = 0; j < currentAuthPaths[i].length; j++)
+ {
+ curAuthPart0.add(new DEROctetString(currentAuthPaths[i][j]));
+ }
+ curAuthPart1.add(new DERSequence(curAuthPart0));
+ curAuthPart0 = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(curAuthPart1));
+
+ // --- Encode <nextAuth>.
+ ASN1EncodableVector nextAuthPart0 = new ASN1EncodableVector();
+ ASN1EncodableVector nextAuthPart1 = new ASN1EncodableVector();
+ for (int i = 0; i < nextAuthPaths.length; i++)
+ {
+ for (int j = 0; j < nextAuthPaths[i].length; j++)
+ {
+ nextAuthPart0.add(new DEROctetString(nextAuthPaths[i][j]));
+ }
+ nextAuthPart1.add(new DERSequence(nextAuthPart0));
+ nextAuthPart0 = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(nextAuthPart1));
+
+ // --- Encode <curTreehash>.
+ ASN1EncodableVector seqOfTreehash0 = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfTreehash1 = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfStat = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfByte = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfInt = new ASN1EncodableVector();
+
+ for (int i = 0; i < currentTreehash.length; i++)
+ {
+ for (int j = 0; j < currentTreehash[i].length; j++)
+ {
+ seqOfStat.add(new DERSequence(algorithms[0]));
+
+ int tailLength = currentTreehash[i][j].getStatInt()[1];
+
+ seqOfByte.add(new DEROctetString(currentTreehash[i][j]
+ .getStatByte()[0]));
+ seqOfByte.add(new DEROctetString(currentTreehash[i][j]
+ .getStatByte()[1]));
+ seqOfByte.add(new DEROctetString(currentTreehash[i][j]
+ .getStatByte()[2]));
+ for (int k = 0; k < tailLength; k++)
+ {
+ seqOfByte.add(new DEROctetString(currentTreehash[i][j]
+ .getStatByte()[3 + k]));
+ }
+ seqOfStat.add(new DERSequence(seqOfByte));
+ seqOfByte = new ASN1EncodableVector();
+
+ seqOfInt.add(new ASN1Integer(
+ currentTreehash[i][j].getStatInt()[0]));
+ seqOfInt.add(new ASN1Integer(tailLength));
+ seqOfInt.add(new ASN1Integer(
+ currentTreehash[i][j].getStatInt()[2]));
+ seqOfInt.add(new ASN1Integer(
+ currentTreehash[i][j].getStatInt()[3]));
+ seqOfInt.add(new ASN1Integer(
+ currentTreehash[i][j].getStatInt()[4]));
+ seqOfInt.add(new ASN1Integer(
+ currentTreehash[i][j].getStatInt()[5]));
+ for (int k = 0; k < tailLength; k++)
+ {
+ seqOfInt.add(new ASN1Integer(currentTreehash[i][j]
+ .getStatInt()[6 + k]));
+ }
+ seqOfStat.add(new DERSequence(seqOfInt));
+ seqOfInt = new ASN1EncodableVector();
+
+ seqOfTreehash1.add(new DERSequence(seqOfStat));
+ seqOfStat = new ASN1EncodableVector();
+ }
+ seqOfTreehash0.add(new DERSequence(seqOfTreehash1));
+ seqOfTreehash1 = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(seqOfTreehash0));
+
+ // --- Encode <nextTreehash>.
+ seqOfTreehash0 = new ASN1EncodableVector();
+ seqOfTreehash1 = new ASN1EncodableVector();
+ seqOfStat = new ASN1EncodableVector();
+ seqOfByte = new ASN1EncodableVector();
+ seqOfInt = new ASN1EncodableVector();
+
+ for (int i = 0; i < nextTreehash.length; i++)
+ {
+ for (int j = 0; j < nextTreehash[i].length; j++)
+ {
+ seqOfStat.add(new DERSequence(algorithms[0]));
+
+ int tailLength = nextTreehash[i][j].getStatInt()[1];
+
+ seqOfByte.add(new DEROctetString(nextTreehash[i][j]
+ .getStatByte()[0]));
+ seqOfByte.add(new DEROctetString(nextTreehash[i][j]
+ .getStatByte()[1]));
+ seqOfByte.add(new DEROctetString(nextTreehash[i][j]
+ .getStatByte()[2]));
+ for (int k = 0; k < tailLength; k++)
+ {
+ seqOfByte.add(new DEROctetString(nextTreehash[i][j]
+ .getStatByte()[3 + k]));
+ }
+ seqOfStat.add(new DERSequence(seqOfByte));
+ seqOfByte = new ASN1EncodableVector();
+
+ seqOfInt
+ .add(new ASN1Integer(nextTreehash[i][j].getStatInt()[0]));
+ seqOfInt.add(new ASN1Integer(tailLength));
+ seqOfInt
+ .add(new ASN1Integer(nextTreehash[i][j].getStatInt()[2]));
+ seqOfInt
+ .add(new ASN1Integer(nextTreehash[i][j].getStatInt()[3]));
+ seqOfInt
+ .add(new ASN1Integer(nextTreehash[i][j].getStatInt()[4]));
+ seqOfInt
+ .add(new ASN1Integer(nextTreehash[i][j].getStatInt()[5]));
+ for (int k = 0; k < tailLength; k++)
+ {
+ seqOfInt.add(new ASN1Integer(nextTreehash[i][j]
+ .getStatInt()[6 + k]));
+ }
+ seqOfStat.add(new DERSequence(seqOfInt));
+ seqOfInt = new ASN1EncodableVector();
+
+ seqOfTreehash1.add(new DERSequence(seqOfStat));
+ seqOfStat = new ASN1EncodableVector();
+ }
+ seqOfTreehash0.add(new DERSequence(new DERSequence(seqOfTreehash1)));
+ seqOfTreehash1 = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(seqOfTreehash0));
+
+ // --- Encode <keep>.
+ ASN1EncodableVector keepPart0 = new ASN1EncodableVector();
+ ASN1EncodableVector keepPart1 = new ASN1EncodableVector();
+ for (int i = 0; i < keep.length; i++)
+ {
+ for (int j = 0; j < keep[i].length; j++)
+ {
+ keepPart0.add(new DEROctetString(keep[i][j]));
+ }
+ keepPart1.add(new DERSequence(keepPart0));
+ keepPart0 = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(keepPart1));
+
+ // --- Encode <curStack>.
+ ASN1EncodableVector curStackPart0 = new ASN1EncodableVector();
+ ASN1EncodableVector curStackPart1 = new ASN1EncodableVector();
+ for (int i = 0; i < currentStack.length; i++)
+ {
+ for (int j = 0; j < currentStack[i].size(); j++)
+ {
+ curStackPart0.add(new DEROctetString((byte[])currentStack[i]
+ .elementAt(j)));
+ }
+ curStackPart1.add(new DERSequence(curStackPart0));
+ curStackPart0 = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(curStackPart1));
+
+ // --- Encode <nextStack>.
+ ASN1EncodableVector nextStackPart0 = new ASN1EncodableVector();
+ ASN1EncodableVector nextStackPart1 = new ASN1EncodableVector();
+ for (int i = 0; i < nextStack.length; i++)
+ {
+ for (int j = 0; j < nextStack[i].size(); j++)
+ {
+ nextStackPart0.add(new DEROctetString((byte[])nextStack[i]
+ .elementAt(j)));
+ }
+ nextStackPart1.add(new DERSequence(nextStackPart0));
+ nextStackPart0 = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(nextStackPart1));
+
+ // --- Encode <curRetain>.
+ ASN1EncodableVector currentRetainPart0 = new ASN1EncodableVector();
+ ASN1EncodableVector currentRetainPart1 = new ASN1EncodableVector();
+ ASN1EncodableVector currentRetainPart2 = new ASN1EncodableVector();
+ for (int i = 0; i < currentRetain.length; i++)
+ {
+ for (int j = 0; j < currentRetain[i].length; j++)
+ {
+ for (int k = 0; k < currentRetain[i][j].size(); k++)
+ {
+ currentRetainPart0.add(new DEROctetString(
+ (byte[])currentRetain[i][j].elementAt(k)));
+ }
+ currentRetainPart1.add(new DERSequence(currentRetainPart0));
+ currentRetainPart0 = new ASN1EncodableVector();
+ }
+ currentRetainPart2.add(new DERSequence(currentRetainPart1));
+ currentRetainPart1 = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(currentRetainPart2));
+
+ // --- Encode <nextRetain>.
+ ASN1EncodableVector nextRetainPart0 = new ASN1EncodableVector();
+ ASN1EncodableVector nextRetainPart1 = new ASN1EncodableVector();
+ ASN1EncodableVector nextRetainPart2 = new ASN1EncodableVector();
+ for (int i = 0; i < nextRetain.length; i++)
+ {
+ for (int j = 0; j < nextRetain[i].length; j++)
+ {
+ for (int k = 0; k < nextRetain[i][j].size(); k++)
+ {
+ nextRetainPart0.add(new DEROctetString(
+ (byte[])nextRetain[i][j].elementAt(k)));
+ }
+ nextRetainPart1.add(new DERSequence(nextRetainPart0));
+ nextRetainPart0 = new ASN1EncodableVector();
+ }
+ nextRetainPart2.add(new DERSequence(nextRetainPart1));
+ nextRetainPart1 = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(nextRetainPart2));
+
+ // --- Encode <nextNextLeaf>.
+ ASN1EncodableVector seqOfLeaf = new ASN1EncodableVector();
+ seqOfStat = new ASN1EncodableVector();
+ seqOfByte = new ASN1EncodableVector();
+ seqOfInt = new ASN1EncodableVector();
+
+ for (int i = 0; i < nextNextLeaf.length; i++)
+ {
+ seqOfStat.add(new DERSequence(algorithms[0]));
+
+ byte[][] tempByte = nextNextLeaf[i].getStatByte();
+ seqOfByte.add(new DEROctetString(tempByte[0]));
+ seqOfByte.add(new DEROctetString(tempByte[1]));
+ seqOfByte.add(new DEROctetString(tempByte[2]));
+ seqOfByte.add(new DEROctetString(tempByte[3]));
+ seqOfStat.add(new DERSequence(seqOfByte));
+ seqOfByte = new ASN1EncodableVector();
+
+ int[] tempInt = nextNextLeaf[i].getStatInt();
+ seqOfInt.add(new ASN1Integer(tempInt[0]));
+ seqOfInt.add(new ASN1Integer(tempInt[1]));
+ seqOfInt.add(new ASN1Integer(tempInt[2]));
+ seqOfInt.add(new ASN1Integer(tempInt[3]));
+ seqOfStat.add(new DERSequence(seqOfInt));
+ seqOfInt = new ASN1EncodableVector();
+
+ seqOfLeaf.add(new DERSequence(seqOfStat));
+ seqOfStat = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(seqOfLeaf));
+
+ // --- Encode <upperLEAF>.
+ ASN1EncodableVector seqOfUpperLeaf = new ASN1EncodableVector();
+ seqOfStat = new ASN1EncodableVector();
+ seqOfByte = new ASN1EncodableVector();
+ seqOfInt = new ASN1EncodableVector();
+
+ for (int i = 0; i < upperLeaf.length; i++)
+ {
+ seqOfStat.add(new DERSequence(algorithms[0]));
+
+ byte[][] tempByte = upperLeaf[i].getStatByte();
+ seqOfByte.add(new DEROctetString(tempByte[0]));
+ seqOfByte.add(new DEROctetString(tempByte[1]));
+ seqOfByte.add(new DEROctetString(tempByte[2]));
+ seqOfByte.add(new DEROctetString(tempByte[3]));
+ seqOfStat.add(new DERSequence(seqOfByte));
+ seqOfByte = new ASN1EncodableVector();
+
+ int[] tempInt = upperLeaf[i].getStatInt();
+ seqOfInt.add(new ASN1Integer(tempInt[0]));
+ seqOfInt.add(new ASN1Integer(tempInt[1]));
+ seqOfInt.add(new ASN1Integer(tempInt[2]));
+ seqOfInt.add(new ASN1Integer(tempInt[3]));
+ seqOfStat.add(new DERSequence(seqOfInt));
+ seqOfInt = new ASN1EncodableVector();
+
+ seqOfUpperLeaf.add(new DERSequence(seqOfStat));
+ seqOfStat = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(seqOfUpperLeaf));
+
+ // encode <upperTreehashLeaf>
+ ASN1EncodableVector seqOfUpperTreehashLeaf = new ASN1EncodableVector();
+ seqOfStat = new ASN1EncodableVector();
+ seqOfByte = new ASN1EncodableVector();
+ seqOfInt = new ASN1EncodableVector();
+
+ for (int i = 0; i < upperTreehashLeaf.length; i++)
+ {
+ seqOfStat.add(new DERSequence(algorithms[0]));
+
+ byte[][] tempByte = upperTreehashLeaf[i].getStatByte();
+ seqOfByte.add(new DEROctetString(tempByte[0]));
+ seqOfByte.add(new DEROctetString(tempByte[1]));
+ seqOfByte.add(new DEROctetString(tempByte[2]));
+ seqOfByte.add(new DEROctetString(tempByte[3]));
+ seqOfStat.add(new DERSequence(seqOfByte));
+ seqOfByte = new ASN1EncodableVector();
+
+ int[] tempInt = upperTreehashLeaf[i].getStatInt();
+ seqOfInt.add(new ASN1Integer(tempInt[0]));
+ seqOfInt.add(new ASN1Integer(tempInt[1]));
+ seqOfInt.add(new ASN1Integer(tempInt[2]));
+ seqOfInt.add(new ASN1Integer(tempInt[3]));
+ seqOfStat.add(new DERSequence(seqOfInt));
+ seqOfInt = new ASN1EncodableVector();
+
+ seqOfUpperTreehashLeaf.add(new DERSequence(seqOfStat));
+ seqOfStat = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(seqOfUpperTreehashLeaf));
+
+ // --- Encode <minTreehash>.
+ ASN1EncodableVector minTreehashPart = new ASN1EncodableVector();
+ for (int i = 0; i < minTreehash.length; i++)
+ {
+ minTreehashPart.add(new ASN1Integer(minTreehash[i]));
+ }
+ result.add(new DERSequence(minTreehashPart));
+
+ // --- Encode <nextRoot>.
+ ASN1EncodableVector nextRootPart = new ASN1EncodableVector();
+ for (int i = 0; i < nextRoot.length; i++)
+ {
+ nextRootPart.add(new DEROctetString(nextRoot[i]));
+ }
+ result.add(new DERSequence(nextRootPart));
+
+ // --- Encode <nextNextRoot>.
+ ASN1EncodableVector seqOfnextNextRoot = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfnnRStats = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfnnRStrings = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfnnRBytes = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfnnRInts = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfnnRTreehash = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfnnRRetain = new ASN1EncodableVector();
+
+ for (int i = 0; i < nextNextRoot.length; i++)
+ {
+ seqOfnnRStats.add(new DERSequence(algorithms[0]));
+ seqOfnnRStrings = new ASN1EncodableVector();
+
+ int heightOfTree = nextNextRoot[i].getStatInt()[0];
+ int tailLength = nextNextRoot[i].getStatInt()[7];
+
+ seqOfnnRBytes.add(new DEROctetString(
+ nextNextRoot[i].getStatByte()[0]));
+ for (int j = 0; j < heightOfTree; j++)
+ {
+ seqOfnnRBytes.add(new DEROctetString(nextNextRoot[i]
+ .getStatByte()[1 + j]));
+ }
+ for (int j = 0; j < tailLength; j++)
+ {
+ seqOfnnRBytes.add(new DEROctetString(nextNextRoot[i]
+ .getStatByte()[1 + heightOfTree + j]));
+ }
+
+ seqOfnnRStats.add(new DERSequence(seqOfnnRBytes));
+ seqOfnnRBytes = new ASN1EncodableVector();
+
+ seqOfnnRInts.add(new ASN1Integer(heightOfTree));
+ seqOfnnRInts.add(new ASN1Integer(nextNextRoot[i].getStatInt()[1]));
+ seqOfnnRInts.add(new ASN1Integer(nextNextRoot[i].getStatInt()[2]));
+ seqOfnnRInts.add(new ASN1Integer(nextNextRoot[i].getStatInt()[3]));
+ seqOfnnRInts.add(new ASN1Integer(nextNextRoot[i].getStatInt()[4]));
+ seqOfnnRInts.add(new ASN1Integer(nextNextRoot[i].getStatInt()[5]));
+ seqOfnnRInts.add(new ASN1Integer(nextNextRoot[i].getStatInt()[6]));
+ seqOfnnRInts.add(new ASN1Integer(tailLength));
+ for (int j = 0; j < heightOfTree; j++)
+ {
+ seqOfnnRInts.add(new ASN1Integer(
+ nextNextRoot[i].getStatInt()[8 + j]));
+ }
+ for (int j = 0; j < tailLength; j++)
+ {
+ seqOfnnRInts.add(new ASN1Integer(nextNextRoot[i].getStatInt()[8
+ + heightOfTree + j]));
+ }
+
+ seqOfnnRStats.add(new DERSequence(seqOfnnRInts));
+ seqOfnnRInts = new ASN1EncodableVector();
+
+ // add treehash of nextNextRoot object
+ // ----------------------------
+ seqOfStat = new ASN1EncodableVector();
+ seqOfByte = new ASN1EncodableVector();
+ seqOfInt = new ASN1EncodableVector();
+
+ if (nextNextRoot[i].getTreehash() != null)
+ {
+ for (int j = 0; j < nextNextRoot[i].getTreehash().length; j++)
+ {
+ seqOfStat.add(new DERSequence(algorithms[0]));
+
+ tailLength = nextNextRoot[i].getTreehash()[j].getStatInt()[1];
+
+ seqOfByte.add(new DEROctetString(nextNextRoot[i]
+ .getTreehash()[j].getStatByte()[0]));
+ seqOfByte.add(new DEROctetString(nextNextRoot[i]
+ .getTreehash()[j].getStatByte()[1]));
+ seqOfByte.add(new DEROctetString(nextNextRoot[i]
+ .getTreehash()[j].getStatByte()[2]));
+ for (int k = 0; k < tailLength; k++)
+ {
+ seqOfByte.add(new DEROctetString(nextNextRoot[i]
+ .getTreehash()[j].getStatByte()[3 + k]));
+ }
+ seqOfStat.add(new DERSequence(seqOfByte));
+ seqOfByte = new ASN1EncodableVector();
+
+ seqOfInt.add(new ASN1Integer(
+ nextNextRoot[i].getTreehash()[j].getStatInt()[0]));
+ seqOfInt.add(new ASN1Integer(tailLength));
+ seqOfInt.add(new ASN1Integer(
+ nextNextRoot[i].getTreehash()[j].getStatInt()[2]));
+ seqOfInt.add(new ASN1Integer(
+ nextNextRoot[i].getTreehash()[j].getStatInt()[3]));
+ seqOfInt.add(new ASN1Integer(
+ nextNextRoot[i].getTreehash()[j].getStatInt()[4]));
+ seqOfInt.add(new ASN1Integer(
+ nextNextRoot[i].getTreehash()[j].getStatInt()[5]));
+ for (int k = 0; k < tailLength; k++)
+ {
+ seqOfInt.add(new ASN1Integer(nextNextRoot[i]
+ .getTreehash()[j].getStatInt()[6 + k]));
+ }
+ seqOfStat.add(new DERSequence(seqOfInt));
+ seqOfInt = new ASN1EncodableVector();
+
+ seqOfnnRTreehash.add(new DERSequence(seqOfStat));
+ seqOfStat = new ASN1EncodableVector();
+ }
+ }
+ // ----------------------------
+ seqOfnnRStats.add(new DERSequence(seqOfnnRTreehash));
+ seqOfnnRTreehash = new ASN1EncodableVector();
+
+ // encode retain of nextNextRoot
+ // ----------------------------
+ // --- Encode <curRetain>.
+ currentRetainPart0 = new ASN1EncodableVector();
+ if (nextNextRoot[i].getRetain() != null)
+ {
+ for (int j = 0; j < nextNextRoot[i].getRetain().length; j++)
+ {
+ for (int k = 0; k < nextNextRoot[i].getRetain()[j].size(); k++)
+ {
+ currentRetainPart0.add(new DEROctetString(
+ (byte[])nextNextRoot[i].getRetain()[j]
+ .elementAt(k)));
+ }
+ seqOfnnRRetain.add(new DERSequence(currentRetainPart0));
+ currentRetainPart0 = new ASN1EncodableVector();
+ }
+ }
+ // ----------------------------
+ seqOfnnRStats.add(new DERSequence(seqOfnnRRetain));
+ seqOfnnRRetain = new ASN1EncodableVector();
+
+ seqOfnextNextRoot.add(new DERSequence(seqOfnnRStats));
+ seqOfnnRStats = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(seqOfnextNextRoot));
+
+ // --- Encode <curRootSig>.
+ ASN1EncodableVector curRootSigPart = new ASN1EncodableVector();
+ for (int i = 0; i < currentRootSig.length; i++)
+ {
+ curRootSigPart.add(new DEROctetString(currentRootSig[i]));
+ }
+ result.add(new DERSequence(curRootSigPart));
+
+ // --- Encode <nextRootSig>.
+ ASN1EncodableVector seqOfnextRootSigs = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfnRSStats = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfnRSStrings = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfnRSBytes = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfnRSInts = new ASN1EncodableVector();
+
+ for (int i = 0; i < nextRootSig.length; i++)
+ {
+ seqOfnRSStats.add(new DERSequence(algorithms[0]));
+ seqOfnRSStrings = new ASN1EncodableVector();
+
+ seqOfnRSBytes.add(new DEROctetString(
+ nextRootSig[i].getStatByte()[0]));
+ seqOfnRSBytes.add(new DEROctetString(
+ nextRootSig[i].getStatByte()[1]));
+ seqOfnRSBytes.add(new DEROctetString(
+ nextRootSig[i].getStatByte()[2]));
+ seqOfnRSBytes.add(new DEROctetString(
+ nextRootSig[i].getStatByte()[3]));
+ seqOfnRSBytes.add(new DEROctetString(
+ nextRootSig[i].getStatByte()[4]));
+
+ seqOfnRSStats.add(new DERSequence(seqOfnRSBytes));
+ seqOfnRSBytes = new ASN1EncodableVector();
+
+ seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[0]));
+ seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[1]));
+ seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[2]));
+ seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[3]));
+ seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[4]));
+ seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[5]));
+ seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[6]));
+ seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[7]));
+ seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[8]));
+
+ seqOfnRSStats.add(new DERSequence(seqOfnRSInts));
+ seqOfnRSInts = new ASN1EncodableVector();
+
+ seqOfnextRootSigs.add(new DERSequence(seqOfnRSStats));
+ seqOfnRSStats = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(seqOfnextRootSigs));
+
+ // --- Encode <parameterset>.
+ ASN1EncodableVector parSetPart0 = new ASN1EncodableVector();
+ ASN1EncodableVector parSetPart1 = new ASN1EncodableVector();
+ ASN1EncodableVector parSetPart2 = new ASN1EncodableVector();
+ ASN1EncodableVector parSetPart3 = new ASN1EncodableVector();
+
+ for (int i = 0; i < gmssParameterset.getHeightOfTrees().length; i++)
+ {
+ parSetPart1.add(new ASN1Integer(
+ gmssParameterset.getHeightOfTrees()[i]));
+ parSetPart2.add(new ASN1Integer(gmssParameterset
+ .getWinternitzParameter()[i]));
+ parSetPart3.add(new ASN1Integer(gmssParameterset.getK()[i]));
+ }
+ parSetPart0.add(new ASN1Integer(gmssParameterset.getNumOfLayers()));
+ parSetPart0.add(new DERSequence(parSetPart1));
+ parSetPart0.add(new DERSequence(parSetPart2));
+ parSetPart0.add(new DERSequence(parSetPart3));
+ result.add(new DERSequence(parSetPart0));
+
+ // --- Encode <names>.
+ ASN1EncodableVector namesPart = new ASN1EncodableVector();
+
+ for (int i = 0; i < algorithms.length; i++)
+ {
+ namesPart.add(algorithms[i]);
+ }
+
+ result.add(new DERSequence(namesPart));
+ return new DERSequence(result);
+
+ }
+
+ private static int checkBigIntegerInIntRange(ASN1Encodable a)
+ {
+ BigInteger b = ((ASN1Integer)a).getValue();
+ if ((b.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) ||
+ (b.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0))
+ {
+ throw new IllegalArgumentException("BigInteger not in Range: " + b.toString());
+ }
+ return b.intValue();
+ }
+
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return this.primitive;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/asn1/GMSSPublicKey.java b/core/src/main/java/org/bouncycastle/pqc/asn1/GMSSPublicKey.java
new file mode 100644
index 00000000..e4f8f502
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/asn1/GMSSPublicKey.java
@@ -0,0 +1,75 @@
+package org.bouncycastle.pqc.asn1;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * This class implements an ASN.1 encoded GMSS public key. The ASN.1 definition
+ * of this structure is:
+ * <p/>
+ * <pre>
+ * GMSSPublicKey ::= SEQUENCE{
+ * version INTEGER
+ * publicKey OCTET STRING
+ * }
+ * </pre>
+ */
+public class GMSSPublicKey
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private byte[] publicKey;
+
+ private GMSSPublicKey(ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("size of seq = " + seq.size());
+ }
+
+ this.version = ASN1Integer.getInstance(seq.getObjectAt(0));
+ this.publicKey = ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets();
+ }
+
+ public GMSSPublicKey(byte[] publicKeyBytes)
+ {
+ this.version = new ASN1Integer(0);
+ this.publicKey = publicKeyBytes;
+ }
+
+ public static GMSSPublicKey getInstance(Object o)
+ {
+ if (o instanceof GMSSPublicKey)
+ {
+ return (GMSSPublicKey)o;
+ }
+ else if (o != null)
+ {
+ return new GMSSPublicKey(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public byte[] getPublicKey()
+ {
+ return Arrays.clone(publicKey);
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+ v.add(new DEROctetString(publicKey));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/asn1/McElieceCCA2PrivateKey.java b/core/src/main/java/org/bouncycastle/pqc/asn1/McElieceCCA2PrivateKey.java
new file mode 100644
index 00000000..192484f8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/asn1/McElieceCCA2PrivateKey.java
@@ -0,0 +1,173 @@
+package org.bouncycastle.pqc.asn1;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+
+import org.bouncycastle.pqc.math.linearalgebra.GF2Matrix;
+import org.bouncycastle.pqc.math.linearalgebra.GF2mField;
+import org.bouncycastle.pqc.math.linearalgebra.Permutation;
+import org.bouncycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
+
+public class McElieceCCA2PrivateKey
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier oid;
+ private int n;
+ private int k;
+ private byte[] encField;
+ private byte[] encGp;
+ private byte[] encP;
+ private byte[] encH;
+ private byte[][] encqInv;
+
+
+ public McElieceCCA2PrivateKey(ASN1ObjectIdentifier oid, int n, int k, GF2mField field, PolynomialGF2mSmallM goppaPoly, Permutation p, GF2Matrix h, PolynomialGF2mSmallM[] qInv)
+ {
+ this.oid = oid;
+ this.n = n;
+ this.k = k;
+ this.encField = field.getEncoded();
+ this.encGp = goppaPoly.getEncoded();
+ this.encP = p.getEncoded();
+ this.encH = h.getEncoded();
+ this.encqInv = new byte[qInv.length][];
+
+ for (int i = 0; i != qInv.length; i++)
+ {
+ encqInv[i] = qInv[i].getEncoded();
+ }
+ }
+
+ private McElieceCCA2PrivateKey(ASN1Sequence seq)
+ {
+ oid = ((ASN1ObjectIdentifier)seq.getObjectAt(0));
+
+ BigInteger bigN = ((ASN1Integer)seq.getObjectAt(1)).getValue();
+ n = bigN.intValue();
+
+ BigInteger bigK = ((ASN1Integer)seq.getObjectAt(2)).getValue();
+ k = bigK.intValue();
+
+ encField = ((ASN1OctetString)seq.getObjectAt(3)).getOctets();
+
+ encGp = ((ASN1OctetString)seq.getObjectAt(4)).getOctets();
+
+ encP = ((ASN1OctetString)seq.getObjectAt(5)).getOctets();
+
+ encH = ((ASN1OctetString)seq.getObjectAt(6)).getOctets();
+
+ ASN1Sequence asnQInv = (ASN1Sequence)seq.getObjectAt(7);
+ encqInv = new byte[asnQInv.size()][];
+ for (int i = 0; i < asnQInv.size(); i++)
+ {
+ encqInv[i] = ((ASN1OctetString)asnQInv.getObjectAt(i)).getOctets();
+ }
+ }
+
+ public ASN1ObjectIdentifier getOID()
+ {
+ return oid;
+ }
+
+ public int getN()
+ {
+ return n;
+ }
+
+ public int getK()
+ {
+ return k;
+ }
+
+ public GF2mField getField()
+ {
+ return new GF2mField(encField);
+ }
+
+ public PolynomialGF2mSmallM getGoppaPoly()
+ {
+ return new PolynomialGF2mSmallM(this.getField(), encGp);
+ }
+
+ public Permutation getP()
+ {
+ return new Permutation(encP);
+ }
+
+ public GF2Matrix getH()
+ {
+ return new GF2Matrix(encH);
+ }
+
+ public PolynomialGF2mSmallM[] getQInv()
+ {
+ PolynomialGF2mSmallM[] qInv = new PolynomialGF2mSmallM[encqInv.length];
+ GF2mField field = this.getField();
+
+ for (int i = 0; i < encqInv.length; i++)
+ {
+ qInv[i] = new PolynomialGF2mSmallM(field, encqInv[i]);
+ }
+
+ return qInv;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ // encode <oidString>
+ v.add(oid);
+ // encode <n>
+ v.add(new ASN1Integer(n));
+
+ // encode <k>
+ v.add(new ASN1Integer(k));
+
+ // encode <field>
+ v.add(new DEROctetString(encField));
+
+ // encode <gp>
+ v.add(new DEROctetString(encGp));
+
+ // encode <p>
+ v.add(new DEROctetString(encP));
+
+ // encode <h>
+ v.add(new DEROctetString(encH));
+
+ // encode <q>
+ ASN1EncodableVector asnQInv = new ASN1EncodableVector();
+ for (int i = 0; i < encqInv.length; i++)
+ {
+ asnQInv.add(new DEROctetString(encqInv[i]));
+ }
+
+ v.add(new DERSequence(asnQInv));
+
+ return new DERSequence(v);
+ }
+
+ public static McElieceCCA2PrivateKey getInstance(Object o)
+ {
+ if (o instanceof McElieceCCA2PrivateKey)
+ {
+ return (McElieceCCA2PrivateKey)o;
+ }
+ else if (o != null)
+ {
+ return new McElieceCCA2PrivateKey(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/asn1/McElieceCCA2PublicKey.java b/core/src/main/java/org/bouncycastle/pqc/asn1/McElieceCCA2PublicKey.java
new file mode 100644
index 00000000..adb5e46a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/asn1/McElieceCCA2PublicKey.java
@@ -0,0 +1,96 @@
+package org.bouncycastle.pqc.asn1;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.pqc.math.linearalgebra.GF2Matrix;
+
+public class McElieceCCA2PublicKey
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier oid;
+ private int n;
+ private int t;
+
+ private byte[] matrixG;
+
+ public McElieceCCA2PublicKey(ASN1ObjectIdentifier oid, int n, int t, GF2Matrix g)
+ {
+ this.oid = oid;
+ this.n = n;
+ this.t = t;
+ this.matrixG = g.getEncoded();
+ }
+
+ private McElieceCCA2PublicKey(ASN1Sequence seq)
+ {
+ oid = ((ASN1ObjectIdentifier)seq.getObjectAt(0));
+ BigInteger bigN = ((ASN1Integer)seq.getObjectAt(1)).getValue();
+ n = bigN.intValue();
+
+ BigInteger bigT = ((ASN1Integer)seq.getObjectAt(2)).getValue();
+ t = bigT.intValue();
+
+ matrixG = ((ASN1OctetString)seq.getObjectAt(3)).getOctets();
+ }
+
+ public ASN1ObjectIdentifier getOID()
+ {
+ return oid;
+ }
+
+ public int getN()
+ {
+ return n;
+ }
+
+ public int getT()
+ {
+ return t;
+ }
+
+ public GF2Matrix getG()
+ {
+ return new GF2Matrix(matrixG);
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ // encode <oidString>
+ v.add(oid);
+
+ // encode <n>
+ v.add(new ASN1Integer(n));
+
+ // encode <t>
+ v.add(new ASN1Integer(t));
+
+ // encode <matrixG>
+ v.add(new DEROctetString(matrixG));
+
+ return new DERSequence(v);
+ }
+
+ public static McElieceCCA2PublicKey getInstance(Object o)
+ {
+ if (o instanceof McElieceCCA2PublicKey)
+ {
+ return (McElieceCCA2PublicKey)o;
+ }
+ else if (o != null)
+ {
+ return new McElieceCCA2PublicKey(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/asn1/McEliecePrivateKey.java b/core/src/main/java/org/bouncycastle/pqc/asn1/McEliecePrivateKey.java
new file mode 100644
index 00000000..4bf2f822
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/asn1/McEliecePrivateKey.java
@@ -0,0 +1,197 @@
+package org.bouncycastle.pqc.asn1;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.pqc.math.linearalgebra.GF2Matrix;
+import org.bouncycastle.pqc.math.linearalgebra.GF2mField;
+import org.bouncycastle.pqc.math.linearalgebra.Permutation;
+import org.bouncycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
+
+public class McEliecePrivateKey
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier oid;
+ private int n;
+ private int k;
+ private byte[] encField;
+ private byte[] encGp;
+ private byte[] encSInv;
+ private byte[] encP1;
+ private byte[] encP2;
+ private byte[] encH;
+ private byte[][] encqInv;
+
+
+ public McEliecePrivateKey(ASN1ObjectIdentifier oid, int n, int k, GF2mField field, PolynomialGF2mSmallM goppaPoly, GF2Matrix sInv, Permutation p1, Permutation p2, GF2Matrix h, PolynomialGF2mSmallM[] qInv)
+ {
+ this.oid = oid;
+ this.n = n;
+ this.k = k;
+ this.encField = field.getEncoded();
+ this.encGp = goppaPoly.getEncoded();
+ this.encSInv = sInv.getEncoded();
+ this.encP1 = p1.getEncoded();
+ this.encP2 = p2.getEncoded();
+ this.encH = h.getEncoded();
+ this.encqInv = new byte[qInv.length][];
+
+ for (int i = 0; i != qInv.length; i++)
+ {
+ encqInv[i] = qInv[i].getEncoded();
+ }
+ }
+
+ public static McEliecePrivateKey getInstance(Object o)
+ {
+ if (o instanceof McEliecePrivateKey)
+ {
+ return (McEliecePrivateKey)o;
+ }
+ else if (o != null)
+ {
+ return new McEliecePrivateKey(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ private McEliecePrivateKey(ASN1Sequence seq)
+ {
+ // <oidString>
+ oid = ((ASN1ObjectIdentifier)seq.getObjectAt(0));
+
+ BigInteger bigN = ((ASN1Integer)seq.getObjectAt(1)).getValue();
+ n = bigN.intValue();
+
+ BigInteger bigK = ((ASN1Integer)seq.getObjectAt(2)).getValue();
+ k = bigK.intValue();
+
+ encField = ((ASN1OctetString)seq.getObjectAt(3)).getOctets();
+
+ encGp = ((ASN1OctetString)seq.getObjectAt(4)).getOctets();
+
+ encSInv = ((ASN1OctetString)seq.getObjectAt(5)).getOctets();
+
+ encP1 = ((ASN1OctetString)seq.getObjectAt(6)).getOctets();
+
+ encP2 = ((ASN1OctetString)seq.getObjectAt(7)).getOctets();
+
+ encH = ((ASN1OctetString)seq.getObjectAt(8)).getOctets();
+
+ ASN1Sequence asnQInv = (ASN1Sequence)seq.getObjectAt(9);
+ encqInv = new byte[asnQInv.size()][];
+ for (int i = 0; i < asnQInv.size(); i++)
+ {
+ encqInv[i] = ((ASN1OctetString)asnQInv.getObjectAt(i)).getOctets();
+ }
+ }
+
+ public ASN1ObjectIdentifier getOID()
+ {
+ return oid;
+ }
+
+ public int getN()
+ {
+ return n;
+ }
+
+ public int getK()
+ {
+ return k;
+ }
+
+ public GF2mField getField()
+ {
+ return new GF2mField(encField);
+ }
+
+ public PolynomialGF2mSmallM getGoppaPoly()
+ {
+ return new PolynomialGF2mSmallM(this.getField(), encGp);
+ }
+
+ public GF2Matrix getSInv()
+ {
+ return new GF2Matrix(encSInv);
+ }
+
+ public Permutation getP1()
+ {
+ return new Permutation(encP1);
+ }
+
+ public Permutation getP2()
+ {
+ return new Permutation(encP2);
+ }
+
+ public GF2Matrix getH()
+ {
+ return new GF2Matrix(encH);
+ }
+
+ public PolynomialGF2mSmallM[] getQInv()
+ {
+ PolynomialGF2mSmallM[] qInv = new PolynomialGF2mSmallM[encqInv.length];
+ GF2mField field = this.getField();
+
+ for (int i = 0; i < encqInv.length; i++)
+ {
+ qInv[i] = new PolynomialGF2mSmallM(field, encqInv[i]);
+ }
+
+ return qInv;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ // encode <oidString>
+ v.add(oid);
+ // encode <n>
+ v.add(new ASN1Integer(n));
+
+ // encode <k>
+ v.add(new ASN1Integer(k));
+
+ // encode <fieldPoly>
+ v.add(new DEROctetString(encField));
+
+ // encode <goppaPoly>
+ v.add(new DEROctetString(encGp));
+
+ // encode <sInv>
+ v.add(new DEROctetString(encSInv));
+
+ // encode <p1>
+ v.add(new DEROctetString(encP1));
+
+ // encode <p2>
+ v.add(new DEROctetString(encP2));
+
+ // encode <h>
+ v.add(new DEROctetString(encH));
+
+ // encode <q>
+ ASN1EncodableVector asnQInv = new ASN1EncodableVector();
+ for (int i = 0; i < encqInv.length; i++)
+ {
+ asnQInv.add(new DEROctetString(encqInv[i]));
+ }
+
+ v.add(new DERSequence(asnQInv));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/asn1/McEliecePublicKey.java b/core/src/main/java/org/bouncycastle/pqc/asn1/McEliecePublicKey.java
new file mode 100644
index 00000000..6f1efc09
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/asn1/McEliecePublicKey.java
@@ -0,0 +1,97 @@
+package org.bouncycastle.pqc.asn1;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.pqc.math.linearalgebra.GF2Matrix;
+
+public class McEliecePublicKey
+ extends ASN1Object
+{
+
+ private ASN1ObjectIdentifier oid;
+ private int n;
+ private int t;
+
+ private byte[] matrixG;
+
+ public McEliecePublicKey(ASN1ObjectIdentifier oid, int n, int t, GF2Matrix g)
+ {
+ this.oid = oid;
+ this.n = n;
+ this.t = t;
+ this.matrixG = g.getEncoded();
+ }
+
+ private McEliecePublicKey(ASN1Sequence seq)
+ {
+ oid = ((ASN1ObjectIdentifier)seq.getObjectAt(0));
+ BigInteger bigN = ((ASN1Integer)seq.getObjectAt(1)).getValue();
+ n = bigN.intValue();
+
+ BigInteger bigT = ((ASN1Integer)seq.getObjectAt(2)).getValue();
+ t = bigT.intValue();
+
+ matrixG = ((ASN1OctetString)seq.getObjectAt(3)).getOctets();
+ }
+
+ public ASN1ObjectIdentifier getOID()
+ {
+ return oid;
+ }
+
+ public int getN()
+ {
+ return n;
+ }
+
+ public int getT()
+ {
+ return t;
+ }
+
+ public GF2Matrix getG()
+ {
+ return new GF2Matrix(matrixG);
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ // encode <oidString>
+ v.add(oid);
+
+ // encode <n>
+ v.add(new ASN1Integer(n));
+
+ // encode <t>
+ v.add(new ASN1Integer(t));
+
+ // encode <matrixG>
+ v.add(new DEROctetString(matrixG));
+
+ return new DERSequence(v);
+ }
+
+ public static McEliecePublicKey getInstance(Object o)
+ {
+ if (o instanceof McEliecePublicKey)
+ {
+ return (McEliecePublicKey)o;
+ }
+ else if (o != null)
+ {
+ return new McEliecePublicKey(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/asn1/PQCObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/pqc/asn1/PQCObjectIdentifiers.java
new file mode 100644
index 00000000..b97a8f30
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/asn1/PQCObjectIdentifiers.java
@@ -0,0 +1,27 @@
+package org.bouncycastle.pqc.asn1;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface PQCObjectIdentifiers
+{
+ public static final ASN1ObjectIdentifier rainbow = new ASN1ObjectIdentifier("1.3.6.1.4.1.8301.3.1.3.5.3.2");
+
+ public static final ASN1ObjectIdentifier rainbowWithSha1 = rainbow.branch("1");
+ public static final ASN1ObjectIdentifier rainbowWithSha224 = rainbow.branch("2");
+ public static final ASN1ObjectIdentifier rainbowWithSha256 = rainbow.branch("3");
+ public static final ASN1ObjectIdentifier rainbowWithSha384 = rainbow.branch("4");
+ public static final ASN1ObjectIdentifier rainbowWithSha512 = rainbow.branch("5");
+
+ public static final ASN1ObjectIdentifier gmss = new ASN1ObjectIdentifier("1.3.6.1.4.1.8301.3.1.3.3");
+
+ public static final ASN1ObjectIdentifier gmssWithSha1 = gmss.branch("1");
+ public static final ASN1ObjectIdentifier gmssWithSha224 = gmss.branch("2");
+ public static final ASN1ObjectIdentifier gmssWithSha256 = gmss.branch("3");
+ public static final ASN1ObjectIdentifier gmssWithSha384 = gmss.branch("4");
+ public static final ASN1ObjectIdentifier gmssWithSha512 = gmss.branch("5");
+
+ public static final ASN1ObjectIdentifier mcEliece = new ASN1ObjectIdentifier("1.3.6.1.4.1.8301.3.1.3.4.1");
+
+ public static final ASN1ObjectIdentifier mcElieceCca2 = new ASN1ObjectIdentifier("1.3.6.1.4.1.8301.3.1.3.4.2");
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/asn1/ParSet.java b/core/src/main/java/org/bouncycastle/pqc/asn1/ParSet.java
new file mode 100644
index 00000000..dee56a52
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/asn1/ParSet.java
@@ -0,0 +1,140 @@
+package org.bouncycastle.pqc.asn1;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * <pre>
+ * ParSet ::= SEQUENCE {
+ * T INTEGER
+ * h SEQUENCE OF INTEGER
+ * w SEQUENCE OF INTEGER
+ * K SEQUENCE OF INTEGER
+ * }
+ * </pre>
+ */
+public class ParSet
+ extends ASN1Object
+{
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+
+ private int t;
+ private int[] h;
+ private int[] w;
+ private int[] k;
+
+ private static int checkBigIntegerInIntRangeAndPositive(BigInteger b)
+ {
+ if ((b.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) ||
+ (b.compareTo(ZERO) <= 0))
+ {
+ throw new IllegalArgumentException("BigInteger not in Range: " + b.toString());
+ }
+ return b.intValue();
+ }
+
+ private ParSet(ASN1Sequence seq)
+ {
+ if (seq.size() != 4)
+ {
+ throw new IllegalArgumentException("sie of seqOfParams = " + seq.size());
+ }
+ BigInteger asn1int = ((ASN1Integer)seq.getObjectAt(0)).getValue();
+
+ t = checkBigIntegerInIntRangeAndPositive(asn1int);
+
+ ASN1Sequence seqOfPSh = (ASN1Sequence)seq.getObjectAt(1);
+ ASN1Sequence seqOfPSw = (ASN1Sequence)seq.getObjectAt(2);
+ ASN1Sequence seqOfPSK = (ASN1Sequence)seq.getObjectAt(3);
+
+ if ((seqOfPSh.size() != t) ||
+ (seqOfPSw.size() != t) ||
+ (seqOfPSK.size() != t))
+ {
+ throw new IllegalArgumentException("invalid size of sequences");
+ }
+
+ h = new int[seqOfPSh.size()];
+ w = new int[seqOfPSw.size()];
+ k = new int[seqOfPSK.size()];
+
+ for (int i = 0; i < t; i++)
+ {
+ h[i] = checkBigIntegerInIntRangeAndPositive((((ASN1Integer)seqOfPSh.getObjectAt(i))).getValue());
+ w[i] = checkBigIntegerInIntRangeAndPositive((((ASN1Integer)seqOfPSw.getObjectAt(i))).getValue());
+ k[i] = checkBigIntegerInIntRangeAndPositive((((ASN1Integer)seqOfPSK.getObjectAt(i))).getValue());
+ }
+ }
+
+ public ParSet(int t, int[] h, int[] w, int[] k)
+ {
+ this.t = t;
+ this.h = h;
+ this.w = w;
+ this.k = k;
+ }
+
+ public static ParSet getInstance(Object o)
+ {
+ if (o instanceof ParSet)
+ {
+ return (ParSet)o;
+ }
+ else if (o != null)
+ {
+ return new ParSet(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public int getT()
+ {
+ return t;
+ }
+
+ public int[] getH()
+ {
+ return Arrays.clone(h);
+ }
+
+ public int[] getW()
+ {
+ return Arrays.clone(w);
+ }
+
+ public int[] getK()
+ {
+ return Arrays.clone(k);
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector seqOfPSh = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfPSw = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfPSK = new ASN1EncodableVector();
+
+ for (int i = 0; i < h.length; i++)
+ {
+ seqOfPSh.add(new ASN1Integer(h[i]));
+ seqOfPSw.add(new ASN1Integer(w[i]));
+ seqOfPSK.add(new ASN1Integer(k[i]));
+ }
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new ASN1Integer(t));
+ v.add(new DERSequence(seqOfPSh));
+ v.add(new DERSequence(seqOfPSw));
+ v.add(new DERSequence(seqOfPSK));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/asn1/RainbowPrivateKey.java b/core/src/main/java/org/bouncycastle/pqc/asn1/RainbowPrivateKey.java
new file mode 100644
index 00000000..06064646
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/asn1/RainbowPrivateKey.java
@@ -0,0 +1,350 @@
+package org.bouncycastle.pqc.asn1;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.pqc.crypto.rainbow.Layer;
+import org.bouncycastle.pqc.crypto.rainbow.util.RainbowUtil;
+
+/**
+ * Return the key data to encode in the PrivateKeyInfo structure.
+ * <p/>
+ * The ASN.1 definition of the key structure is
+ * <p/>
+ * <pre>
+ * RainbowPrivateKey ::= SEQUENCE {
+ * CHOICE
+ * {
+ * oid OBJECT IDENTIFIER -- OID identifying the algorithm
+ * version INTEGER -- 0
+ * }
+ * A1inv SEQUENCE OF OCTET STRING -- inversed matrix of L1
+ * b1 OCTET STRING -- translation vector of L1
+ * A2inv SEQUENCE OF OCTET STRING -- inversed matrix of L2
+ * b2 OCTET STRING -- translation vector of L2
+ * vi OCTET STRING -- num of elmts in each Set S
+ * layers SEQUENCE OF Layer -- layers of F
+ * }
+ *
+ * Layer ::= SEQUENCE OF Poly
+ *
+ * Poly ::= SEQUENCE {
+ * alpha SEQUENCE OF OCTET STRING
+ * beta SEQUENCE OF OCTET STRING
+ * gamma OCTET STRING
+ * eta INTEGER
+ * }
+ * </pre>
+ */
+public class RainbowPrivateKey
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private ASN1ObjectIdentifier oid;
+
+ private byte[][] invA1;
+ private byte[] b1;
+ private byte[][] invA2;
+ private byte[] b2;
+ private byte[] vi;
+ private Layer[] layers;
+
+ private RainbowPrivateKey(ASN1Sequence seq)
+ {
+ // <oidString> or version
+ if (seq.getObjectAt(0) instanceof ASN1Integer)
+ {
+ version = ASN1Integer.getInstance(seq.getObjectAt(0));
+ }
+ else
+ {
+ oid = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+ }
+
+ // <A1inv>
+ ASN1Sequence asnA1 = (ASN1Sequence)seq.getObjectAt(1);
+ invA1 = new byte[asnA1.size()][];
+ for (int i = 0; i < asnA1.size(); i++)
+ {
+ invA1[i] = ((ASN1OctetString)asnA1.getObjectAt(i)).getOctets();
+ }
+
+ // <b1>
+ ASN1Sequence asnb1 = (ASN1Sequence)seq.getObjectAt(2);
+ b1 = ((ASN1OctetString)asnb1.getObjectAt(0)).getOctets();
+
+ // <A2inv>
+ ASN1Sequence asnA2 = (ASN1Sequence)seq.getObjectAt(3);
+ invA2 = new byte[asnA2.size()][];
+ for (int j = 0; j < asnA2.size(); j++)
+ {
+ invA2[j] = ((ASN1OctetString)asnA2.getObjectAt(j)).getOctets();
+ }
+
+ // <b2>
+ ASN1Sequence asnb2 = (ASN1Sequence)seq.getObjectAt(4);
+ b2 = ((ASN1OctetString)asnb2.getObjectAt(0)).getOctets();
+
+ // <vi>
+ ASN1Sequence asnvi = (ASN1Sequence)seq.getObjectAt(5);
+ vi = ((ASN1OctetString)asnvi.getObjectAt(0)).getOctets();
+
+ // <layers>
+ ASN1Sequence asnLayers = (ASN1Sequence)seq.getObjectAt(6);
+
+ byte[][][][] alphas = new byte[asnLayers.size()][][][];
+ byte[][][][] betas = new byte[asnLayers.size()][][][];
+ byte[][][] gammas = new byte[asnLayers.size()][][];
+ byte[][] etas = new byte[asnLayers.size()][];
+ // a layer:
+ for (int l = 0; l < asnLayers.size(); l++)
+ {
+ ASN1Sequence asnLayer = (ASN1Sequence)asnLayers.getObjectAt(l);
+
+ // alphas (num of alpha-2d-array = oi)
+ ASN1Sequence alphas3d = (ASN1Sequence)asnLayer.getObjectAt(0);
+ alphas[l] = new byte[alphas3d.size()][][];
+ for (int m = 0; m < alphas3d.size(); m++)
+ {
+ ASN1Sequence alphas2d = (ASN1Sequence)alphas3d.getObjectAt(m);
+ alphas[l][m] = new byte[alphas2d.size()][];
+ for (int n = 0; n < alphas2d.size(); n++)
+ {
+ alphas[l][m][n] = ((ASN1OctetString)alphas2d.getObjectAt(n)).getOctets();
+ }
+ }
+
+ // betas ....
+ ASN1Sequence betas3d = (ASN1Sequence)asnLayer.getObjectAt(1);
+ betas[l] = new byte[betas3d.size()][][];
+ for (int mb = 0; mb < betas3d.size(); mb++)
+ {
+ ASN1Sequence betas2d = (ASN1Sequence)betas3d.getObjectAt(mb);
+ betas[l][mb] = new byte[betas2d.size()][];
+ for (int nb = 0; nb < betas2d.size(); nb++)
+ {
+ betas[l][mb][nb] = ((ASN1OctetString)betas2d.getObjectAt(nb)).getOctets();
+ }
+ }
+
+ // gammas ...
+ ASN1Sequence gammas2d = (ASN1Sequence)asnLayer.getObjectAt(2);
+ gammas[l] = new byte[gammas2d.size()][];
+ for (int mg = 0; mg < gammas2d.size(); mg++)
+ {
+ gammas[l][mg] = ((ASN1OctetString)gammas2d.getObjectAt(mg)).getOctets();
+ }
+
+ // eta ...
+ etas[l] = ((ASN1OctetString)asnLayer.getObjectAt(3)).getOctets();
+ }
+
+ int numOfLayers = vi.length - 1;
+ this.layers = new Layer[numOfLayers];
+ for (int i = 0; i < numOfLayers; i++)
+ {
+ Layer l = new Layer(vi[i], vi[i + 1], RainbowUtil.convertArray(alphas[i]),
+ RainbowUtil.convertArray(betas[i]), RainbowUtil.convertArray(gammas[i]), RainbowUtil.convertArray(etas[i]));
+ this.layers[i] = l;
+
+ }
+ }
+
+ public RainbowPrivateKey(short[][] invA1, short[] b1, short[][] invA2,
+ short[] b2, int[] vi, Layer[] layers)
+ {
+ this.version = new ASN1Integer(1);
+ this.invA1 = RainbowUtil.convertArray(invA1);
+ this.b1 = RainbowUtil.convertArray(b1);
+ this.invA2 = RainbowUtil.convertArray(invA2);
+ this.b2 = RainbowUtil.convertArray(b2);
+ this.vi = RainbowUtil.convertIntArray(vi);
+ this.layers = layers;
+ }
+
+ public static RainbowPrivateKey getInstance(Object o)
+ {
+ if (o instanceof RainbowPrivateKey)
+ {
+ return (RainbowPrivateKey)o;
+ }
+ else if (o != null)
+ {
+ return new RainbowPrivateKey(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ /**
+ * Getter for the inverse matrix of A1.
+ *
+ * @return the A1inv inverse
+ */
+ public short[][] getInvA1()
+ {
+ return RainbowUtil.convertArray(invA1);
+ }
+
+ /**
+ * Getter for the translation part of the private quadratic map L1.
+ *
+ * @return b1 the translation part of L1
+ */
+ public short[] getB1()
+ {
+ return RainbowUtil.convertArray(b1);
+ }
+
+ /**
+ * Getter for the translation part of the private quadratic map L2.
+ *
+ * @return b2 the translation part of L2
+ */
+ public short[] getB2()
+ {
+ return RainbowUtil.convertArray(b2);
+ }
+
+ /**
+ * Getter for the inverse matrix of A2
+ *
+ * @return the A2inv
+ */
+ public short[][] getInvA2()
+ {
+ return RainbowUtil.convertArray(invA2);
+ }
+
+ /**
+ * Returns the layers contained in the private key
+ *
+ * @return layers
+ */
+ public Layer[] getLayers()
+ {
+ return this.layers;
+ }
+
+ /**
+ * Returns the array of vi-s
+ *
+ * @return the vi
+ */
+ public int[] getVi()
+ {
+ return RainbowUtil.convertArraytoInt(vi);
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ // encode <oidString> or version
+ if (version != null)
+ {
+ v.add(version);
+ }
+ else
+ {
+ v.add(oid);
+ }
+
+ // encode <A1inv>
+ ASN1EncodableVector asnA1 = new ASN1EncodableVector();
+ for (int i = 0; i < invA1.length; i++)
+ {
+ asnA1.add(new DEROctetString(invA1[i]));
+ }
+ v.add(new DERSequence(asnA1));
+
+ // encode <b1>
+ ASN1EncodableVector asnb1 = new ASN1EncodableVector();
+ asnb1.add(new DEROctetString(b1));
+ v.add(new DERSequence(asnb1));
+
+ // encode <A2inv>
+ ASN1EncodableVector asnA2 = new ASN1EncodableVector();
+ for (int i = 0; i < invA2.length; i++)
+ {
+ asnA2.add(new DEROctetString(invA2[i]));
+ }
+ v.add(new DERSequence(asnA2));
+
+ // encode <b2>
+ ASN1EncodableVector asnb2 = new ASN1EncodableVector();
+ asnb2.add(new DEROctetString(b2));
+ v.add(new DERSequence(asnb2));
+
+ // encode <vi>
+ ASN1EncodableVector asnvi = new ASN1EncodableVector();
+ asnvi.add(new DEROctetString(vi));
+ v.add(new DERSequence(asnvi));
+
+ // encode <layers>
+ ASN1EncodableVector asnLayers = new ASN1EncodableVector();
+ // a layer:
+ for (int l = 0; l < layers.length; l++)
+ {
+ ASN1EncodableVector aLayer = new ASN1EncodableVector();
+
+ // alphas (num of alpha-2d-array = oi)
+ byte[][][] alphas = RainbowUtil.convertArray(layers[l].getCoeffAlpha());
+ ASN1EncodableVector alphas3d = new ASN1EncodableVector();
+ for (int i = 0; i < alphas.length; i++)
+ {
+ ASN1EncodableVector alphas2d = new ASN1EncodableVector();
+ for (int j = 0; j < alphas[i].length; j++)
+ {
+ alphas2d.add(new DEROctetString(alphas[i][j]));
+ }
+ alphas3d.add(new DERSequence(alphas2d));
+ }
+ aLayer.add(new DERSequence(alphas3d));
+
+ // betas ....
+ byte[][][] betas = RainbowUtil.convertArray(layers[l].getCoeffBeta());
+ ASN1EncodableVector betas3d = new ASN1EncodableVector();
+ for (int i = 0; i < betas.length; i++)
+ {
+ ASN1EncodableVector betas2d = new ASN1EncodableVector();
+ for (int j = 0; j < betas[i].length; j++)
+ {
+ betas2d.add(new DEROctetString(betas[i][j]));
+ }
+ betas3d.add(new DERSequence(betas2d));
+ }
+ aLayer.add(new DERSequence(betas3d));
+
+ // gammas ...
+ byte[][] gammas = RainbowUtil.convertArray(layers[l].getCoeffGamma());
+ ASN1EncodableVector asnG = new ASN1EncodableVector();
+ for (int i = 0; i < gammas.length; i++)
+ {
+ asnG.add(new DEROctetString(gammas[i]));
+ }
+ aLayer.add(new DERSequence(asnG));
+
+ // eta
+ aLayer.add(new DEROctetString(RainbowUtil.convertArray(layers[l].getCoeffEta())));
+
+ // now, layer built up. add it!
+ asnLayers.add(new DERSequence(aLayer));
+ }
+
+ v.add(new DERSequence(asnLayers));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/asn1/RainbowPublicKey.java b/core/src/main/java/org/bouncycastle/pqc/asn1/RainbowPublicKey.java
new file mode 100644
index 00000000..2073c555
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/asn1/RainbowPublicKey.java
@@ -0,0 +1,175 @@
+package org.bouncycastle.pqc.asn1;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.pqc.crypto.rainbow.util.RainbowUtil;
+
+/**
+ * This class implements an ASN.1 encoded Rainbow public key. The ASN.1 definition
+ * of this structure is:
+ * <p/>
+ * <pre>
+ * RainbowPublicKey ::= SEQUENCE {
+ * CHOICE
+ * {
+ * oid OBJECT IDENTIFIER -- OID identifying the algorithm
+ * version INTEGER -- 0
+ * }
+ * docLength Integer -- length of the code
+ * coeffquadratic SEQUENCE OF OCTET STRING -- quadratic (mixed) coefficients
+ * coeffsingular SEQUENCE OF OCTET STRING -- singular coefficients
+ * coeffscalar SEQUENCE OF OCTET STRING -- scalar coefficients
+ * }
+ * </pre>
+ */
+public class RainbowPublicKey
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private ASN1ObjectIdentifier oid;
+ private ASN1Integer docLength;
+ private byte[][] coeffQuadratic;
+ private byte[][] coeffSingular;
+ private byte[] coeffScalar;
+
+ private RainbowPublicKey(ASN1Sequence seq)
+ {
+ // <oidString> or version
+ if (seq.getObjectAt(0) instanceof ASN1Integer)
+ {
+ version = ASN1Integer.getInstance(seq.getObjectAt(0));
+ }
+ else
+ {
+ oid = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+ }
+
+ docLength = ASN1Integer.getInstance(seq.getObjectAt(1));
+
+ ASN1Sequence asnCoeffQuad = ASN1Sequence.getInstance(seq.getObjectAt(2));
+ coeffQuadratic = new byte[asnCoeffQuad.size()][];
+ for (int quadSize = 0; quadSize < asnCoeffQuad.size(); quadSize++)
+ {
+ coeffQuadratic[quadSize] = ASN1OctetString.getInstance(asnCoeffQuad.getObjectAt(quadSize)).getOctets();
+ }
+
+ ASN1Sequence asnCoeffSing = (ASN1Sequence)seq.getObjectAt(3);
+ coeffSingular = new byte[asnCoeffSing.size()][];
+ for (int singSize = 0; singSize < asnCoeffSing.size(); singSize++)
+ {
+ coeffSingular[singSize] = ASN1OctetString.getInstance(asnCoeffSing.getObjectAt(singSize)).getOctets();
+ }
+
+ ASN1Sequence asnCoeffScalar = (ASN1Sequence)seq.getObjectAt(4);
+ coeffScalar = ASN1OctetString.getInstance(asnCoeffScalar.getObjectAt(0)).getOctets();
+ }
+
+ public RainbowPublicKey(int docLength, short[][] coeffQuadratic, short[][] coeffSingular, short[] coeffScalar)
+ {
+ this.version = new ASN1Integer(0);
+ this.docLength = new ASN1Integer(docLength);
+ this.coeffQuadratic = RainbowUtil.convertArray(coeffQuadratic);
+ this.coeffSingular = RainbowUtil.convertArray(coeffSingular);
+ this.coeffScalar = RainbowUtil.convertArray(coeffScalar);
+ }
+
+ public static RainbowPublicKey getInstance(Object o)
+ {
+ if (o instanceof RainbowPublicKey)
+ {
+ return (RainbowPublicKey)o;
+ }
+ else if (o != null)
+ {
+ return new RainbowPublicKey(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ /**
+ * @return the docLength
+ */
+ public int getDocLength()
+ {
+ return this.docLength.getValue().intValue();
+ }
+
+ /**
+ * @return the coeffquadratic
+ */
+ public short[][] getCoeffQuadratic()
+ {
+ return RainbowUtil.convertArray(coeffQuadratic);
+ }
+
+ /**
+ * @return the coeffsingular
+ */
+ public short[][] getCoeffSingular()
+ {
+ return RainbowUtil.convertArray(coeffSingular);
+ }
+
+ /**
+ * @return the coeffscalar
+ */
+ public short[] getCoeffScalar()
+ {
+ return RainbowUtil.convertArray(coeffScalar);
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ // encode <oidString> or version
+ if (version != null)
+ {
+ v.add(version);
+ }
+ else
+ {
+ v.add(oid);
+ }
+
+ // encode <docLength>
+ v.add(docLength);
+
+ // encode <coeffQuadratic>
+ ASN1EncodableVector asnCoeffQuad = new ASN1EncodableVector();
+ for (int i = 0; i < coeffQuadratic.length; i++)
+ {
+ asnCoeffQuad.add(new DEROctetString(coeffQuadratic[i]));
+ }
+ v.add(new DERSequence(asnCoeffQuad));
+
+ // encode <coeffSingular>
+ ASN1EncodableVector asnCoeffSing = new ASN1EncodableVector();
+ for (int i = 0; i < coeffSingular.length; i++)
+ {
+ asnCoeffSing.add(new DEROctetString(coeffSingular[i]));
+ }
+ v.add(new DERSequence(asnCoeffSing));
+
+ // encode <coeffScalar>
+ ASN1EncodableVector asnCoeffScalar = new ASN1EncodableVector();
+ asnCoeffScalar.add(new DEROctetString(coeffScalar));
+ v.add(new DERSequence(asnCoeffScalar));
+
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/DigestingMessageSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/DigestingMessageSigner.java
new file mode 100644
index 00000000..6b5b2515
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/DigestingMessageSigner.java
@@ -0,0 +1,117 @@
+package org.bouncycastle.pqc.crypto;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+
+
+/**
+ * Implements the sign and verify functions for a Signature Scheme which can use a hash function.
+ */
+public class DigestingMessageSigner
+ implements Signer
+{
+ private final Digest messDigest;
+ private final MessageSigner messSigner;
+ private boolean forSigning;
+
+ public DigestingMessageSigner(MessageSigner messSigner, Digest messDigest)
+ {
+ this.messSigner = messSigner;
+ this.messDigest = messDigest;
+ }
+
+ public void init(boolean forSigning,
+ CipherParameters param)
+ {
+
+ this.forSigning = forSigning;
+ AsymmetricKeyParameter k;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ k = (AsymmetricKeyParameter)((ParametersWithRandom)param).getParameters();
+ }
+ else
+ {
+ k = (AsymmetricKeyParameter)param;
+ }
+
+ if (forSigning && !k.isPrivate())
+ {
+ throw new IllegalArgumentException("Signing Requires Private Key.");
+ }
+
+ if (!forSigning && k.isPrivate())
+ {
+ throw new IllegalArgumentException("Verification Requires Public Key.");
+ }
+
+ reset();
+
+ messSigner.init(forSigning, param);
+ }
+
+
+ /**
+ * This function signs the message that has been updated, making use of the
+ * private key.
+ *
+ * @return the signature of the message.
+ */
+ public byte[] generateSignature()
+ {
+ if (!forSigning)
+ {
+ throw new IllegalStateException("RainbowDigestSigner not initialised for signature generation.");
+ }
+
+ byte[] hash = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hash, 0);
+
+ return messSigner.generateSignature(hash);
+ }
+
+ /**
+ * This function verifies the signature of the message that has been
+ * updated, with the aid of the public key.
+ *
+ * @param signature the signature of the message is given as a byte array.
+ * @return true if the signature has been verified, false otherwise.
+ */
+ public boolean verify(byte[] signature)
+ {
+ if (forSigning)
+ {
+ throw new IllegalStateException("RainbowDigestSigner not initialised for verification");
+ }
+
+ byte[] hash = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hash, 0);
+
+ return messSigner.verifySignature(hash, signature);
+
+ }
+
+ public void update(byte b)
+ {
+ messDigest.update(b);
+ }
+
+ public void update(byte[] in, int off, int len)
+ {
+ messDigest.update(in, off, len);
+ }
+
+ public void reset()
+ {
+ messDigest.reset();
+ }
+
+ public boolean verifySignature(byte[] signature)
+ {
+ return this.verify(signature);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/MessageEncryptor.java b/core/src/main/java/org/bouncycastle/pqc/crypto/MessageEncryptor.java
new file mode 100644
index 00000000..8d67c5cd
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/MessageEncryptor.java
@@ -0,0 +1,30 @@
+package org.bouncycastle.pqc.crypto;
+
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public interface MessageEncryptor
+{
+
+ /**
+ *
+ * @param forEncrypting true if we are encrypting a signature, false
+ * otherwise.
+ * @param param key parameters for encryption or decryption.
+ */
+ public void init(boolean forEncrypting, CipherParameters param);
+
+ /**
+ *
+ * @param message the message to be signed.
+ * @throws Exception
+ */
+ public byte[] messageEncrypt(byte[] message) throws Exception;
+
+ /**
+ *
+ * @param cipher the cipher text of the message
+ * @throws Exception
+ */
+ public byte[] messageDecrypt(byte[] cipher) throws Exception;
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/MessageSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/MessageSigner.java
new file mode 100644
index 00000000..50243f73
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/MessageSigner.java
@@ -0,0 +1,32 @@
+package org.bouncycastle.pqc.crypto;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public interface MessageSigner
+{
+ /**
+ * initialise the signer for signature generation or signature
+ * verification.
+ *
+ * @param forSigning true if we are generating a signature, false
+ * otherwise.
+ * @param param key parameters for signature generation.
+ */
+ public void init(boolean forSigning, CipherParameters param);
+
+ /**
+ * sign the passed in message (usually the output of a hash function).
+ *
+ * @param message the message to be signed.
+ * @return the signature of the message
+ */
+ public byte[] generateSignature(byte[] message);
+
+ /**
+ * verify the message message against the signature values r and s.
+ *
+ * @param message the message that was supposed to have been signed.
+ * @param signature the signature of the message
+ */
+ public boolean verifySignature(byte[] message, byte[] signature);
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSDigestProvider.java b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSDigestProvider.java
new file mode 100644
index 00000000..4af1a8b5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSDigestProvider.java
@@ -0,0 +1,8 @@
+package org.bouncycastle.pqc.crypto.gmss;
+
+import org.bouncycastle.crypto.Digest;
+
+public interface GMSSDigestProvider
+{
+ Digest get();
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSKeyGenerationParameters.java
new file mode 100644
index 00000000..eace4d03
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSKeyGenerationParameters.java
@@ -0,0 +1,26 @@
+package org.bouncycastle.pqc.crypto.gmss;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.KeyGenerationParameters;
+
+public class GMSSKeyGenerationParameters
+ extends KeyGenerationParameters
+{
+
+ private GMSSParameters params;
+
+ public GMSSKeyGenerationParameters(
+ SecureRandom random,
+ GMSSParameters params)
+ {
+ // XXX key size?
+ super(random, 1);
+ this.params = params;
+ }
+
+ public GMSSParameters getParameters()
+ {
+ return params;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSKeyPairGenerator.java
new file mode 100644
index 00000000..f84b7f32
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSKeyPairGenerator.java
@@ -0,0 +1,477 @@
+package org.bouncycastle.pqc.crypto.gmss;
+
+import java.security.SecureRandom;
+import java.util.Vector;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.pqc.crypto.gmss.util.GMSSRandom;
+import org.bouncycastle.pqc.crypto.gmss.util.WinternitzOTSVerify;
+import org.bouncycastle.pqc.crypto.gmss.util.WinternitzOTSignature;
+
+
+/**
+ * This class implements key pair generation of the generalized Merkle signature
+ * scheme (GMSS).
+ *
+ * @see GMSSSigner
+ */
+public class GMSSKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+ /**
+ * The source of randomness for OTS private key generation
+ */
+ private GMSSRandom gmssRandom;
+
+ /**
+ * The hash function used for the construction of the authentication trees
+ */
+ private Digest messDigestTree;
+
+ /**
+ * An array of the seeds for the PRGN (for main tree, and all current
+ * subtrees)
+ */
+ private byte[][] currentSeeds;
+
+ /**
+ * An array of seeds for the PRGN (for all subtrees after next)
+ */
+ private byte[][] nextNextSeeds;
+
+ /**
+ * An array of the RootSignatures
+ */
+ private byte[][] currentRootSigs;
+
+ /**
+ * Class of hash function to use
+ */
+ private GMSSDigestProvider digestProvider;
+
+ /**
+ * The length of the seed for the PRNG
+ */
+ private int mdLength;
+
+ /**
+ * the number of Layers
+ */
+ private int numLayer;
+
+
+ /**
+ * Flag indicating if the class already has been initialized
+ */
+ private boolean initialized = false;
+
+ /**
+ * Instance of GMSSParameterset
+ */
+ private GMSSParameters gmssPS;
+
+ /**
+ * An array of the heights of the authentication trees of each layer
+ */
+ private int[] heightOfTrees;
+
+ /**
+ * An array of the Winternitz parameter 'w' of each layer
+ */
+ private int[] otsIndex;
+
+ /**
+ * The parameter K needed for the authentication path computation
+ */
+ private int[] K;
+
+ private GMSSKeyGenerationParameters gmssParams;
+
+ /**
+ * The GMSS OID.
+ */
+ public static final String OID = "1.3.6.1.4.1.8301.3.1.3.3";
+
+ /**
+ * The standard constructor tries to generate the GMSS algorithm identifier
+ * with the corresponding OID.
+ * <p/>
+ *
+ * @param digestProvider provider for digest implementations.
+ */
+ public GMSSKeyPairGenerator(GMSSDigestProvider digestProvider)
+ {
+ this.digestProvider = digestProvider;
+ messDigestTree = digestProvider.get();
+
+ // set mdLength
+ this.mdLength = messDigestTree.getDigestSize();
+ // construct randomizer
+ this.gmssRandom = new GMSSRandom(messDigestTree);
+
+ }
+
+ /**
+ * Generates the GMSS key pair. The public key is an instance of
+ * JDKGMSSPublicKey, the private key is an instance of JDKGMSSPrivateKey.
+ *
+ * @return Key pair containing a JDKGMSSPublicKey and a JDKGMSSPrivateKey
+ */
+ private AsymmetricCipherKeyPair genKeyPair()
+ {
+ if (!initialized)
+ {
+ initializeDefault();
+ }
+
+ // initialize authenticationPaths and treehash instances
+ byte[][][] currentAuthPaths = new byte[numLayer][][];
+ byte[][][] nextAuthPaths = new byte[numLayer - 1][][];
+ Treehash[][] currentTreehash = new Treehash[numLayer][];
+ Treehash[][] nextTreehash = new Treehash[numLayer - 1][];
+
+ Vector[] currentStack = new Vector[numLayer];
+ Vector[] nextStack = new Vector[numLayer - 1];
+
+ Vector[][] currentRetain = new Vector[numLayer][];
+ Vector[][] nextRetain = new Vector[numLayer - 1][];
+
+ for (int i = 0; i < numLayer; i++)
+ {
+ currentAuthPaths[i] = new byte[heightOfTrees[i]][mdLength];
+ currentTreehash[i] = new Treehash[heightOfTrees[i] - K[i]];
+
+ if (i > 0)
+ {
+ nextAuthPaths[i - 1] = new byte[heightOfTrees[i]][mdLength];
+ nextTreehash[i - 1] = new Treehash[heightOfTrees[i] - K[i]];
+ }
+
+ currentStack[i] = new Vector();
+ if (i > 0)
+ {
+ nextStack[i - 1] = new Vector();
+ }
+ }
+
+ // initialize roots
+ byte[][] currentRoots = new byte[numLayer][mdLength];
+ byte[][] nextRoots = new byte[numLayer - 1][mdLength];
+ // initialize seeds
+ byte[][] seeds = new byte[numLayer][mdLength];
+ // initialize seeds[] by copying starting-seeds of first trees of each
+ // layer
+ for (int i = 0; i < numLayer; i++)
+ {
+ System.arraycopy(currentSeeds[i], 0, seeds[i], 0, mdLength);
+ }
+
+ // initialize rootSigs
+ currentRootSigs = new byte[numLayer - 1][mdLength];
+
+ // -------------------------
+ // -------------------------
+ // --- calculation of current authpaths and current rootsigs (AUTHPATHS,
+ // SIG)------
+ // from bottom up to the root
+ for (int h = numLayer - 1; h >= 0; h--)
+ {
+ GMSSRootCalc tree = new GMSSRootCalc(this.heightOfTrees[h], this.K[h], digestProvider);
+ try
+ {
+ // on lowest layer no lower root is available, so just call
+ // the method with null as first parameter
+ if (h == numLayer - 1)
+ {
+ tree = this.generateCurrentAuthpathAndRoot(null, currentStack[h], seeds[h], h);
+ }
+ else
+ // otherwise call the method with the former computed root
+ // value
+ {
+ tree = this.generateCurrentAuthpathAndRoot(currentRoots[h + 1], currentStack[h], seeds[h], h);
+ }
+
+ }
+ catch (Exception e1)
+ {
+ e1.printStackTrace();
+ }
+
+ // set initial values needed for the private key construction
+ for (int i = 0; i < heightOfTrees[h]; i++)
+ {
+ System.arraycopy(tree.getAuthPath()[i], 0, currentAuthPaths[h][i], 0, mdLength);
+ }
+ currentRetain[h] = tree.getRetain();
+ currentTreehash[h] = tree.getTreehash();
+ System.arraycopy(tree.getRoot(), 0, currentRoots[h], 0, mdLength);
+ }
+
+ // --- calculation of next authpaths and next roots (AUTHPATHS+, ROOTS+)
+ // ------
+ for (int h = numLayer - 2; h >= 0; h--)
+ {
+ GMSSRootCalc tree = this.generateNextAuthpathAndRoot(nextStack[h], seeds[h + 1], h + 1);
+
+ // set initial values needed for the private key construction
+ for (int i = 0; i < heightOfTrees[h + 1]; i++)
+ {
+ System.arraycopy(tree.getAuthPath()[i], 0, nextAuthPaths[h][i], 0, mdLength);
+ }
+ nextRetain[h] = tree.getRetain();
+ nextTreehash[h] = tree.getTreehash();
+ System.arraycopy(tree.getRoot(), 0, nextRoots[h], 0, mdLength);
+
+ // create seed for the Merkle tree after next (nextNextSeeds)
+ // SEEDs++
+ System.arraycopy(seeds[h + 1], 0, this.nextNextSeeds[h], 0, mdLength);
+ }
+ // ------------
+
+ // generate JDKGMSSPublicKey
+ GMSSPublicKeyParameters publicKey = new GMSSPublicKeyParameters(currentRoots[0], gmssPS);
+
+ // generate the JDKGMSSPrivateKey
+ GMSSPrivateKeyParameters privateKey = new GMSSPrivateKeyParameters(currentSeeds, nextNextSeeds, currentAuthPaths,
+ nextAuthPaths, currentTreehash, nextTreehash, currentStack, nextStack, currentRetain, nextRetain, nextRoots, currentRootSigs, gmssPS, digestProvider);
+
+ // return the KeyPair
+ return (new AsymmetricCipherKeyPair(publicKey, privateKey));
+ }
+
+ /**
+ * calculates the authpath for tree in layer h which starts with seed[h]
+ * additionally computes the rootSignature of underlaying root
+ *
+ * @param currentStack stack used for the treehash instance created by this method
+ * @param lowerRoot stores the root of the lower tree
+ * @param seed starting seeds
+ * @param h actual layer
+ */
+ private GMSSRootCalc generateCurrentAuthpathAndRoot(byte[] lowerRoot, Vector currentStack, byte[] seed, int h)
+ {
+ byte[] help = new byte[mdLength];
+
+ byte[] OTSseed = new byte[mdLength];
+ OTSseed = gmssRandom.nextSeed(seed);
+
+ WinternitzOTSignature ots;
+
+ // data structure that constructs the whole tree and stores
+ // the initial values for treehash, Auth and retain
+ GMSSRootCalc treeToConstruct = new GMSSRootCalc(this.heightOfTrees[h], this.K[h], digestProvider);
+
+ treeToConstruct.initialize(currentStack);
+
+ // generate the first leaf
+ if (h == numLayer - 1)
+ {
+ ots = new WinternitzOTSignature(OTSseed, digestProvider.get(), otsIndex[h]);
+ help = ots.getPublicKey();
+ }
+ else
+ {
+ // for all layers except the lowest, generate the signature of the
+ // underlying root
+ // and reuse this signature to compute the first leaf of acual layer
+ // more efficiently (by verifiing the signature)
+ ots = new WinternitzOTSignature(OTSseed, digestProvider.get(), otsIndex[h]);
+ currentRootSigs[h] = ots.getSignature(lowerRoot);
+ WinternitzOTSVerify otsver = new WinternitzOTSVerify(digestProvider.get(), otsIndex[h]);
+ help = otsver.Verify(lowerRoot, currentRootSigs[h]);
+ }
+ // update the tree with the first leaf
+ treeToConstruct.update(help);
+
+ int seedForTreehashIndex = 3;
+ int count = 0;
+
+ // update the tree 2^(H) - 1 times, from the second to the last leaf
+ for (int i = 1; i < (1 << this.heightOfTrees[h]); i++)
+ {
+ // initialize the seeds for the leaf generation with index 3 * 2^h
+ if (i == seedForTreehashIndex && count < this.heightOfTrees[h] - this.K[h])
+ {
+ treeToConstruct.initializeTreehashSeed(seed, count);
+ seedForTreehashIndex *= 2;
+ count++;
+ }
+
+ OTSseed = gmssRandom.nextSeed(seed);
+ ots = new WinternitzOTSignature(OTSseed, digestProvider.get(), otsIndex[h]);
+ treeToConstruct.update(ots.getPublicKey());
+ }
+
+ if (treeToConstruct.wasFinished())
+ {
+ return treeToConstruct;
+ }
+ System.err.println("Baum noch nicht fertig konstruiert!!!");
+ return null;
+ }
+
+ /**
+ * calculates the authpath and root for tree in layer h which starts with
+ * seed[h]
+ *
+ * @param nextStack stack used for the treehash instance created by this method
+ * @param seed starting seeds
+ * @param h actual layer
+ */
+ private GMSSRootCalc generateNextAuthpathAndRoot(Vector nextStack, byte[] seed, int h)
+ {
+ byte[] OTSseed = new byte[numLayer];
+ WinternitzOTSignature ots;
+
+ // data structure that constructs the whole tree and stores
+ // the initial values for treehash, Auth and retain
+ GMSSRootCalc treeToConstruct = new GMSSRootCalc(this.heightOfTrees[h], this.K[h], this.digestProvider);
+ treeToConstruct.initialize(nextStack);
+
+ int seedForTreehashIndex = 3;
+ int count = 0;
+
+ // update the tree 2^(H) times, from the first to the last leaf
+ for (int i = 0; i < (1 << this.heightOfTrees[h]); i++)
+ {
+ // initialize the seeds for the leaf generation with index 3 * 2^h
+ if (i == seedForTreehashIndex && count < this.heightOfTrees[h] - this.K[h])
+ {
+ treeToConstruct.initializeTreehashSeed(seed, count);
+ seedForTreehashIndex *= 2;
+ count++;
+ }
+
+ OTSseed = gmssRandom.nextSeed(seed);
+ ots = new WinternitzOTSignature(OTSseed, digestProvider.get(), otsIndex[h]);
+ treeToConstruct.update(ots.getPublicKey());
+ }
+
+ if (treeToConstruct.wasFinished())
+ {
+ return treeToConstruct;
+ }
+ System.err.println("N�chster Baum noch nicht fertig konstruiert!!!");
+ return null;
+ }
+
+ /**
+ * This method initializes the GMSS KeyPairGenerator using an integer value
+ * <code>keySize</code> as input. It provides a simple use of the GMSS for
+ * testing demands.
+ * <p/>
+ * A given <code>keysize</code> of less than 10 creates an amount 2^10
+ * signatures. A keySize between 10 and 20 creates 2^20 signatures. Given an
+ * integer greater than 20 the key pair generator creates 2^40 signatures.
+ *
+ * @param keySize Assigns the parameters used for the GMSS signatures. There are
+ * 3 choices:<br/>
+ * 1. keysize <= 10: creates 2^10 signatures using the
+ * parameterset<br/>
+ * P = (2, (5, 5), (3, 3), (3, 3))<br/>
+ * 2. keysize > 10 and <= 20: creates 2^20 signatures using the
+ * parameterset<br/>
+ * P = (2, (10, 10), (5, 4), (2, 2))<br/>
+ * 3. keysize > 20: creates 2^40 signatures using the
+ * parameterset<br/>
+ * P = (2, (10, 10, 10, 10), (9, 9, 9, 3), (2, 2, 2, 2))
+ * @param secureRandom not used by GMSS, the SHA1PRNG of the SUN Provider is always
+ * used
+ */
+ public void initialize(int keySize, SecureRandom secureRandom)
+ {
+
+ KeyGenerationParameters kgp;
+ if (keySize <= 10)
+ { // create 2^10 keys
+ int[] defh = {10};
+ int[] defw = {3};
+ int[] defk = {2};
+ // XXX sec random neede?
+ kgp = new GMSSKeyGenerationParameters(secureRandom, new GMSSParameters(defh.length, defh, defw, defk));
+ }
+ else if (keySize <= 20)
+ { // create 2^20 keys
+ int[] defh = {10, 10};
+ int[] defw = {5, 4};
+ int[] defk = {2, 2};
+ kgp = new GMSSKeyGenerationParameters(secureRandom, new GMSSParameters(defh.length, defh, defw, defk));
+ }
+ else
+ { // create 2^40 keys, keygen lasts around 80 seconds
+ int[] defh = {10, 10, 10, 10};
+ int[] defw = {9, 9, 9, 3};
+ int[] defk = {2, 2, 2, 2};
+ kgp = new GMSSKeyGenerationParameters(secureRandom, new GMSSParameters(defh.length, defh, defw, defk));
+ }
+
+ // call the initializer with the chosen parameters
+ this.initialize(kgp);
+
+ }
+
+
+ /**
+ * Initalizes the key pair generator using a parameter set as input
+ */
+ public void initialize(KeyGenerationParameters param)
+ {
+
+ this.gmssParams = (GMSSKeyGenerationParameters)param;
+
+ // generate GMSSParameterset
+ this.gmssPS = new GMSSParameters(gmssParams.getParameters().getNumOfLayers(), gmssParams.getParameters().getHeightOfTrees(),
+ gmssParams.getParameters().getWinternitzParameter(), gmssParams.getParameters().getK());
+
+ this.numLayer = gmssPS.getNumOfLayers();
+ this.heightOfTrees = gmssPS.getHeightOfTrees();
+ this.otsIndex = gmssPS.getWinternitzParameter();
+ this.K = gmssPS.getK();
+
+ // seeds
+ this.currentSeeds = new byte[numLayer][mdLength];
+ this.nextNextSeeds = new byte[numLayer - 1][mdLength];
+
+ // construct SecureRandom for initial seed generation
+ SecureRandom secRan = new SecureRandom();
+
+ // generation of initial seeds
+ for (int i = 0; i < numLayer; i++)
+ {
+ secRan.nextBytes(currentSeeds[i]);
+ gmssRandom.nextSeed(currentSeeds[i]);
+ }
+
+ this.initialized = true;
+ }
+
+ /**
+ * This method is called by generateKeyPair() in case that no other
+ * initialization method has been called by the user
+ */
+ private void initializeDefault()
+ {
+ int[] defh = {10, 10, 10, 10};
+ int[] defw = {3, 3, 3, 3};
+ int[] defk = {2, 2, 2, 2};
+
+ KeyGenerationParameters kgp = new GMSSKeyGenerationParameters(new SecureRandom(), new GMSSParameters(defh.length, defh, defw, defk));
+ this.initialize(kgp);
+
+ }
+
+ public void init(KeyGenerationParameters param)
+ {
+ this.initialize(param);
+
+ }
+
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ return genKeyPair();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSKeyParameters.java
new file mode 100644
index 00000000..53f6e432
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSKeyParameters.java
@@ -0,0 +1,22 @@
+package org.bouncycastle.pqc.crypto.gmss;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+
+public class GMSSKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private GMSSParameters params;
+
+ public GMSSKeyParameters(
+ boolean isPrivate,
+ GMSSParameters params)
+ {
+ super(isPrivate);
+ this.params = params;
+ }
+
+ public GMSSParameters getParameters()
+ {
+ return params;
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSLeaf.java b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSLeaf.java
new file mode 100644
index 00000000..6823ce3f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSLeaf.java
@@ -0,0 +1,376 @@
+package org.bouncycastle.pqc.crypto.gmss;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.pqc.crypto.gmss.util.GMSSRandom;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+
+
+/**
+ * This class implements the distributed computation of the public key of the
+ * Winternitz one-time signature scheme (OTSS). The class is used by the GMSS
+ * classes for calculation of upcoming leafs.
+ */
+public class GMSSLeaf
+{
+
+ /**
+ * The hash function used by the OTS and the PRNG
+ */
+ private Digest messDigestOTS;
+
+ /**
+ * The length of the message digest and private key
+ */
+ private int mdsize, keysize;
+
+ /**
+ * The source of randomness for OTS private key generation
+ */
+ private GMSSRandom gmssRandom;
+
+ /**
+ * Byte array for distributed computation of the upcoming leaf
+ */
+ private byte[] leaf;
+
+ /**
+ * Byte array for storing the concatenated hashes of private key parts
+ */
+ private byte[] concHashs;
+
+ /**
+ * indices for distributed computation
+ */
+ private int i, j;
+
+ /**
+ * storing 2^w
+ */
+ private int two_power_w;
+
+ /**
+ * Winternitz parameter w
+ */
+ private int w;
+
+ /**
+ * the amount of distributed computation steps when updateLeaf is called
+ */
+ private int steps;
+
+ /**
+ * the internal seed
+ */
+ private byte[] seed;
+
+ /**
+ * the OTS privateKey parts
+ */
+ byte[] privateKeyOTS;
+
+ /**
+ * This constructor regenerates a prior GMSSLeaf object
+ *
+ * @param digest an array of strings, containing the name of the used hash
+ * function and PRNG and the name of the corresponding
+ * provider
+ * @param otsIndex status bytes
+ * @param numLeafs status ints
+ */
+ public GMSSLeaf(Digest digest, byte[][] otsIndex, int[] numLeafs)
+ {
+ this.i = numLeafs[0];
+ this.j = numLeafs[1];
+ this.steps = numLeafs[2];
+ this.w = numLeafs[3];
+
+ messDigestOTS = digest;
+
+ gmssRandom = new GMSSRandom(messDigestOTS);
+
+ // calulate keysize for private key and the help array
+ mdsize = messDigestOTS.getDigestSize();
+ int mdsizeBit = mdsize << 3;
+ int messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w);
+ int checksumsize = getLog((messagesize << w) + 1);
+ this.keysize = messagesize
+ + (int)Math.ceil((double)checksumsize / (double)w);
+ this.two_power_w = 1 << w;
+
+ // calculate steps
+ // ((2^w)-1)*keysize + keysize + 1 / (2^h -1)
+
+ // initialize arrays
+ this.privateKeyOTS = otsIndex[0];
+ this.seed = otsIndex[1];
+ this.concHashs = otsIndex[2];
+ this.leaf = otsIndex[3];
+ }
+
+ /**
+ * The constructor precomputes some needed variables for distributed leaf
+ * calculation
+ *
+ * @param digest an array of strings, containing the digest of the used hash
+ * function and PRNG and the digest of the corresponding
+ * provider
+ * @param w the winterniz parameter of that tree the leaf is computed
+ * for
+ * @param numLeafs the number of leafs of the tree from where the distributed
+ * computation is called
+ */
+ GMSSLeaf(Digest digest, int w, int numLeafs)
+ {
+ this.w = w;
+
+ messDigestOTS = digest;
+
+ gmssRandom = new GMSSRandom(messDigestOTS);
+
+ // calulate keysize for private key and the help array
+ mdsize = messDigestOTS.getDigestSize();
+ int mdsizeBit = mdsize << 3;
+ int messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w);
+ int checksumsize = getLog((messagesize << w) + 1);
+ this.keysize = messagesize
+ + (int)Math.ceil((double)checksumsize / (double)w);
+ this.two_power_w = 1 << w;
+
+ // calculate steps
+ // ((2^w)-1)*keysize + keysize + 1 / (2^h -1)
+ this.steps = (int)Math
+ .ceil((double)(((1 << w) - 1) * keysize + 1 + keysize)
+ / (double)(numLeafs));
+
+ // initialize arrays
+ this.seed = new byte[mdsize];
+ this.leaf = new byte[mdsize];
+ this.privateKeyOTS = new byte[mdsize];
+ this.concHashs = new byte[mdsize * keysize];
+ }
+
+ public GMSSLeaf(Digest digest, int w, int numLeafs, byte[] seed0)
+ {
+ this.w = w;
+
+ messDigestOTS = digest;
+
+ gmssRandom = new GMSSRandom(messDigestOTS);
+
+ // calulate keysize for private key and the help array
+ mdsize = messDigestOTS.getDigestSize();
+ int mdsizeBit = mdsize << 3;
+ int messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w);
+ int checksumsize = getLog((messagesize << w) + 1);
+ this.keysize = messagesize
+ + (int)Math.ceil((double)checksumsize / (double)w);
+ this.two_power_w = 1 << w;
+
+ // calculate steps
+ // ((2^w)-1)*keysize + keysize + 1 / (2^h -1)
+ this.steps = (int)Math
+ .ceil((double)(((1 << w) - 1) * keysize + 1 + keysize)
+ / (double)(numLeafs));
+
+ // initialize arrays
+ this.seed = new byte[mdsize];
+ this.leaf = new byte[mdsize];
+ this.privateKeyOTS = new byte[mdsize];
+ this.concHashs = new byte[mdsize * keysize];
+
+ initLeafCalc(seed0);
+ }
+
+ private GMSSLeaf(GMSSLeaf original)
+ {
+ this.messDigestOTS = original.messDigestOTS;
+ this.mdsize = original.mdsize;
+ this.keysize = original.keysize;
+ this.gmssRandom = original.gmssRandom;
+ this.leaf = Arrays.clone(original.leaf);
+ this.concHashs = Arrays.clone(original.concHashs);
+ this.i = original.i;
+ this.j = original.j;
+ this.two_power_w = original.two_power_w;
+ this.w = original.w;
+ this.steps = original.steps;
+ this.seed = Arrays.clone(original.seed);
+ this.privateKeyOTS = Arrays.clone(original.privateKeyOTS);
+ }
+
+ /**
+ * initialize the distributed leaf calculation reset i,j and compute OTSseed
+ * with seed0
+ *
+ * @param seed0 the starting seed
+ */
+ // TODO: this really looks like it should be either always called from a constructor or nextLeaf.
+ void initLeafCalc(byte[] seed0)
+ {
+ this.i = 0;
+ this.j = 0;
+ byte[] dummy = new byte[mdsize];
+ System.arraycopy(seed0, 0, dummy, 0, seed.length);
+ this.seed = gmssRandom.nextSeed(dummy);
+ }
+
+ GMSSLeaf nextLeaf()
+ {
+ GMSSLeaf nextLeaf = new GMSSLeaf(this);
+
+ nextLeaf.updateLeafCalc();
+
+ return nextLeaf;
+ }
+
+ /**
+ * Processes <code>steps</code> steps of distributed leaf calculation
+ *
+ * @return true if leaf is completed, else false
+ */
+ private void updateLeafCalc()
+ {
+ byte[] buf = new byte[messDigestOTS.getDigestSize()];
+
+ // steps times do
+ // TODO: this really needs to be looked at, the 10000 has been added as
+ // prior to this the leaf value always ended up as zeros.
+ for (int s = 0; s < steps + 10000; s++)
+ {
+ if (i == keysize && j == two_power_w - 1)
+ { // [3] at last hash the
+ // concatenation
+ messDigestOTS.update(concHashs, 0, concHashs.length);
+ leaf = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(leaf, 0);
+ return;
+ }
+ else if (i == 0 || j == two_power_w - 1)
+ { // [1] at the
+ // beginning and
+ // when [2] is
+ // finished: get the
+ // next private key
+ // part
+ i++;
+ j = 0;
+ // get next privKey part
+ this.privateKeyOTS = gmssRandom.nextSeed(seed);
+ }
+ else
+ { // [2] hash the privKey part
+ messDigestOTS.update(privateKeyOTS, 0, privateKeyOTS.length);
+ privateKeyOTS = buf;
+ messDigestOTS.doFinal(privateKeyOTS, 0);
+ j++;
+ if (j == two_power_w - 1)
+ { // after w hashes add to the
+ // concatenated array
+ System.arraycopy(privateKeyOTS, 0, concHashs, mdsize
+ * (i - 1), mdsize);
+ }
+ }
+ }
+
+ throw new IllegalStateException("unable to updateLeaf in steps: " + steps + " " + i + " " + j);
+ }
+
+ /**
+ * Returns the leaf value.
+ *
+ * @return the leaf value
+ */
+ public byte[] getLeaf()
+ {
+ return Arrays.clone(leaf);
+ }
+
+ /**
+ * This method returns the least integer that is greater or equal to the
+ * logarithm to the base 2 of an integer <code>intValue</code>.
+ *
+ * @param intValue an integer
+ * @return The least integer greater or equal to the logarithm to the base 2
+ * of <code>intValue</code>
+ */
+ private int getLog(int intValue)
+ {
+ int log = 1;
+ int i = 2;
+ while (i < intValue)
+ {
+ i <<= 1;
+ log++;
+ }
+ return log;
+ }
+
+ /**
+ * Returns the status byte array used by the GMSSPrivateKeyASN.1 class
+ *
+ * @return The status bytes
+ */
+ public byte[][] getStatByte()
+ {
+
+ byte[][] statByte = new byte[4][];
+ statByte[0] = new byte[mdsize];
+ statByte[1] = new byte[mdsize];
+ statByte[2] = new byte[mdsize * keysize];
+ statByte[3] = new byte[mdsize];
+ statByte[0] = privateKeyOTS;
+ statByte[1] = seed;
+ statByte[2] = concHashs;
+ statByte[3] = leaf;
+
+ return statByte;
+ }
+
+ /**
+ * Returns the status int array used by the GMSSPrivateKeyASN.1 class
+ *
+ * @return The status ints
+ */
+ public int[] getStatInt()
+ {
+
+ int[] statInt = new int[4];
+ statInt[0] = i;
+ statInt[1] = j;
+ statInt[2] = steps;
+ statInt[3] = w;
+ return statInt;
+ }
+
+ /**
+ * Returns a String representation of the main part of this element
+ *
+ * @return a String representation of the main part of this element
+ */
+ public String toString()
+ {
+ String out = "";
+
+ for (int i = 0; i < 4; i++)
+ {
+ out = out + this.getStatInt()[i] + " ";
+ }
+ out = out + " " + this.mdsize + " " + this.keysize + " "
+ + this.two_power_w + " ";
+
+ byte[][] temp = this.getStatByte();
+ for (int i = 0; i < 4; i++)
+ {
+ if (temp[i] != null)
+ {
+ out = out + new String(Hex.encode(temp[i])) + " ";
+ }
+ else
+ {
+ out = out + "null ";
+ }
+ }
+ return out;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSParameters.java
new file mode 100644
index 00000000..04332619
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSParameters.java
@@ -0,0 +1,156 @@
+package org.bouncycastle.pqc.crypto.gmss;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * This class provides a specification for the GMSS parameters that are used by
+ * the GMSSKeyPairGenerator and GMSSSignature classes.
+ *
+ * @see org.bouncycastle.pqc.crypto.gmss.GMSSKeyPairGenerator
+ */
+public class GMSSParameters
+{
+ /**
+ * The number of authentication tree layers.
+ */
+ private int numOfLayers;
+
+ /**
+ * The height of the authentication trees of each layer.
+ */
+ private int[] heightOfTrees;
+
+ /**
+ * The Winternitz Parameter 'w' of each layer.
+ */
+ private int[] winternitzParameter;
+
+ /**
+ * The parameter K needed for the authentication path computation
+ */
+ private int[] K;
+
+ /**
+ * The constructor for the parameters of the GMSSKeyPairGenerator.
+ * <p/>
+ *
+ * @param layers the number of authentication tree layers
+ * @param heightOfTrees the height of the authentication trees
+ * @param winternitzParameter the Winternitz Parameter 'w' of each layer
+ * @param K parameter for authpath computation
+ */
+ public GMSSParameters(int layers, int[] heightOfTrees, int[] winternitzParameter, int[] K)
+ throws IllegalArgumentException
+ {
+ init(layers, heightOfTrees, winternitzParameter, K);
+ }
+
+ private void init(int layers, int[] heightOfTrees,
+ int[] winternitzParameter, int[] K)
+ throws IllegalArgumentException
+ {
+ boolean valid = true;
+ String errMsg = "";
+ this.numOfLayers = layers;
+ if ((numOfLayers != winternitzParameter.length)
+ || (numOfLayers != heightOfTrees.length)
+ || (numOfLayers != K.length))
+ {
+ valid = false;
+ errMsg = "Unexpected parameterset format";
+ }
+ for (int i = 0; i < numOfLayers; i++)
+ {
+ if ((K[i] < 2) || ((heightOfTrees[i] - K[i]) % 2 != 0))
+ {
+ valid = false;
+ errMsg = "Wrong parameter K (K >= 2 and H-K even required)!";
+ }
+
+ if ((heightOfTrees[i] < 4) || (winternitzParameter[i] < 2))
+ {
+ valid = false;
+ errMsg = "Wrong parameter H or w (H > 3 and w > 1 required)!";
+ }
+ }
+
+ if (valid)
+ {
+ this.heightOfTrees = Arrays.clone(heightOfTrees);
+ this.winternitzParameter = Arrays.clone(winternitzParameter);
+ this.K = Arrays.clone(K);
+ }
+ else
+ {
+ throw new IllegalArgumentException(errMsg);
+ }
+ }
+
+ public GMSSParameters(int keySize)
+ throws IllegalArgumentException
+ {
+ if (keySize <= 10)
+ { // create 2^10 keys
+ int[] defh = {10};
+ int[] defw = {3};
+ int[] defk = {2};
+ this.init(defh.length, defh, defw, defk);
+ }
+ else if (keySize <= 20)
+ { // create 2^20 keys
+ int[] defh = {10, 10};
+ int[] defw = {5, 4};
+ int[] defk = {2, 2};
+ this.init(defh.length, defh, defw, defk);
+ }
+ else
+ { // create 2^40 keys, keygen lasts around 80 seconds
+ int[] defh = {10, 10, 10, 10};
+ int[] defw = {9, 9, 9, 3};
+ int[] defk = {2, 2, 2, 2};
+ this.init(defh.length, defh, defw, defk);
+ }
+ }
+
+ /**
+ * Returns the number of levels of the authentication trees.
+ *
+ * @return The number of levels of the authentication trees.
+ */
+ public int getNumOfLayers()
+ {
+ return numOfLayers;
+ }
+
+ /**
+ * Returns the array of height (for each layer) of the authentication trees
+ *
+ * @return The array of height (for each layer) of the authentication trees
+ */
+ public int[] getHeightOfTrees()
+ {
+ return Arrays.clone(heightOfTrees);
+ }
+
+ /**
+ * Returns the array of WinternitzParameter (for each layer) of the
+ * authentication trees
+ *
+ * @return The array of WinternitzParameter (for each layer) of the
+ * authentication trees
+ */
+ public int[] getWinternitzParameter()
+ {
+ return Arrays.clone(winternitzParameter);
+ }
+
+ /**
+ * Returns the parameter K needed for authentication path computation
+ *
+ * @return The parameter K needed for authentication path computation
+ */
+ public int[] getK()
+ {
+ return Arrays.clone(K);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSPrivateKeyParameters.java
new file mode 100644
index 00000000..83cf7972
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSPrivateKeyParameters.java
@@ -0,0 +1,1041 @@
+package org.bouncycastle.pqc.crypto.gmss;
+
+import java.util.Vector;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.pqc.crypto.gmss.util.GMSSRandom;
+import org.bouncycastle.pqc.crypto.gmss.util.WinternitzOTSignature;
+import org.bouncycastle.util.Arrays;
+
+
+/**
+ * This class provides a specification for a GMSS private key.
+ */
+public class GMSSPrivateKeyParameters
+ extends GMSSKeyParameters
+{
+ private int[] index;
+
+ private byte[][] currentSeeds;
+ private byte[][] nextNextSeeds;
+
+ private byte[][][] currentAuthPaths;
+ private byte[][][] nextAuthPaths;
+
+ private Treehash[][] currentTreehash;
+ private Treehash[][] nextTreehash;
+
+ private Vector[] currentStack;
+ private Vector[] nextStack;
+
+ private Vector[][] currentRetain;
+ private Vector[][] nextRetain;
+
+ private byte[][][] keep;
+
+ private GMSSLeaf[] nextNextLeaf;
+ private GMSSLeaf[] upperLeaf;
+ private GMSSLeaf[] upperTreehashLeaf;
+
+ private int[] minTreehash;
+
+ private GMSSParameters gmssPS;
+
+ private byte[][] nextRoot;
+ private GMSSRootCalc[] nextNextRoot;
+
+ private byte[][] currentRootSig;
+ private GMSSRootSig[] nextRootSig;
+
+ private GMSSDigestProvider digestProvider;
+
+ private boolean used = false;
+
+ /**
+ * An array of the heights of the authentication trees of each layer
+ */
+ private int[] heightOfTrees;
+
+ /**
+ * An array of the Winternitz parameter 'w' of each layer
+ */
+ private int[] otsIndex;
+
+ /**
+ * The parameter K needed for the authentication path computation
+ */
+ private int[] K;
+
+ /**
+ * the number of Layers
+ */
+ private int numLayer;
+
+ /**
+ * The hash function used to construct the authentication trees
+ */
+ private Digest messDigestTrees;
+
+ /**
+ * The message digest length
+ */
+ private int mdLength;
+
+ /**
+ * The PRNG used for private key generation
+ */
+ private GMSSRandom gmssRandom;
+
+
+ /**
+ * The number of leafs of one tree of each layer
+ */
+ private int[] numLeafs;
+
+
+ /**
+ * Generates a new GMSS private key
+ *
+ * @param currentSeed seed for the generation of private OTS keys for the
+ * current subtrees
+ * @param nextNextSeed seed for the generation of private OTS keys for the next
+ * subtrees
+ * @param currentAuthPath array of current authentication paths
+ * @param nextAuthPath array of next authentication paths
+ * @param currentTreehash array of current treehash instances
+ * @param nextTreehash array of next treehash instances
+ * @param currentStack array of current shared stacks
+ * @param nextStack array of next shared stacks
+ * @param currentRetain array of current retain stacks
+ * @param nextRetain array of next retain stacks
+ * @param nextRoot the roots of the next subtree
+ * @param currentRootSig array of signatures of the roots of the current subtrees
+ * @param gmssParameterset the GMSS Parameterset
+ * @see org.bouncycastle.pqc.crypto.gmss.GMSSKeyPairGenerator
+ */
+
+ public GMSSPrivateKeyParameters(byte[][] currentSeed, byte[][] nextNextSeed,
+ byte[][][] currentAuthPath, byte[][][] nextAuthPath,
+ Treehash[][] currentTreehash, Treehash[][] nextTreehash,
+ Vector[] currentStack, Vector[] nextStack,
+ Vector[][] currentRetain, Vector[][] nextRetain, byte[][] nextRoot,
+ byte[][] currentRootSig, GMSSParameters gmssParameterset,
+ GMSSDigestProvider digestProvider)
+ {
+ this(null, currentSeed, nextNextSeed, currentAuthPath, nextAuthPath,
+ null, currentTreehash, nextTreehash, currentStack, nextStack,
+ currentRetain, nextRetain, null, null, null, null, nextRoot,
+ null, currentRootSig, null, gmssParameterset, digestProvider);
+ }
+
+ /**
+ * /**
+ *
+ * @param index tree indices
+ * @param keep keep array for the authPath algorithm
+ * @param currentTreehash treehash for authPath algorithm of current tree
+ * @param nextTreehash treehash for authPath algorithm of next tree (TREE+)
+ * @param currentStack shared stack for authPath algorithm of current tree
+ * @param nextStack shared stack for authPath algorithm of next tree (TREE+)
+ * @param currentRetain retain stack for authPath algorithm of current tree
+ * @param nextRetain retain stack for authPath algorithm of next tree (TREE+)
+ * @param nextNextLeaf array of upcoming leafs of the tree after next (LEAF++) of
+ * each layer
+ * @param upperLeaf needed for precomputation of upper nodes
+ * @param upperTreehashLeaf needed for precomputation of upper treehash nodes
+ * @param minTreehash index of next treehash instance to receive an update
+ * @param nextRoot the roots of the next trees (ROOT+)
+ * @param nextNextRoot the roots of the tree after next (ROOT++)
+ * @param currentRootSig array of signatures of the roots of the current subtrees
+ * (SIG)
+ * @param nextRootSig array of signatures of the roots of the next subtree
+ * (SIG+)
+ * @param gmssParameterset the GMSS Parameterset
+ */
+ public GMSSPrivateKeyParameters(int[] index, byte[][] currentSeeds,
+ byte[][] nextNextSeeds, byte[][][] currentAuthPaths,
+ byte[][][] nextAuthPaths, byte[][][] keep,
+ Treehash[][] currentTreehash, Treehash[][] nextTreehash,
+ Vector[] currentStack, Vector[] nextStack,
+ Vector[][] currentRetain, Vector[][] nextRetain,
+ GMSSLeaf[] nextNextLeaf, GMSSLeaf[] upperLeaf,
+ GMSSLeaf[] upperTreehashLeaf, int[] minTreehash, byte[][] nextRoot,
+ GMSSRootCalc[] nextNextRoot, byte[][] currentRootSig,
+ GMSSRootSig[] nextRootSig, GMSSParameters gmssParameterset,
+ GMSSDigestProvider digestProvider)
+ {
+
+ super(true, gmssParameterset);
+
+ // construct message digest
+
+ this.messDigestTrees = digestProvider.get();
+ this.mdLength = messDigestTrees.getDigestSize();
+
+
+ // Parameter
+ this.gmssPS = gmssParameterset;
+ this.otsIndex = gmssParameterset.getWinternitzParameter();
+ this.K = gmssParameterset.getK();
+ this.heightOfTrees = gmssParameterset.getHeightOfTrees();
+ // initialize numLayer
+ this.numLayer = gmssPS.getNumOfLayers();
+
+ // initialize index if null
+ if (index == null)
+ {
+ this.index = new int[numLayer];
+ for (int i = 0; i < numLayer; i++)
+ {
+ this.index[i] = 0;
+ }
+ }
+ else
+ {
+ this.index = index;
+ }
+
+ this.currentSeeds = currentSeeds;
+ this.nextNextSeeds = nextNextSeeds;
+
+ this.currentAuthPaths = currentAuthPaths;
+ this.nextAuthPaths = nextAuthPaths;
+
+ // initialize keep if null
+ if (keep == null)
+ {
+ this.keep = new byte[numLayer][][];
+ for (int i = 0; i < numLayer; i++)
+ {
+ this.keep[i] = new byte[(int)Math.floor(heightOfTrees[i] / 2)][mdLength];
+ }
+ }
+ else
+ {
+ this.keep = keep;
+ }
+
+ // initialize stack if null
+ if (currentStack == null)
+ {
+ this.currentStack = new Vector[numLayer];
+ for (int i = 0; i < numLayer; i++)
+ {
+ this.currentStack[i] = new Vector();
+ }
+ }
+ else
+ {
+ this.currentStack = currentStack;
+ }
+
+ // initialize nextStack if null
+ if (nextStack == null)
+ {
+ this.nextStack = new Vector[numLayer - 1];
+ for (int i = 0; i < numLayer - 1; i++)
+ {
+ this.nextStack[i] = new Vector();
+ }
+ }
+ else
+ {
+ this.nextStack = nextStack;
+ }
+
+ this.currentTreehash = currentTreehash;
+ this.nextTreehash = nextTreehash;
+
+ this.currentRetain = currentRetain;
+ this.nextRetain = nextRetain;
+
+ this.nextRoot = nextRoot;
+
+ this.digestProvider = digestProvider;
+
+ if (nextNextRoot == null)
+ {
+ this.nextNextRoot = new GMSSRootCalc[numLayer - 1];
+ for (int i = 0; i < numLayer - 1; i++)
+ {
+ this.nextNextRoot[i] = new GMSSRootCalc(
+ this.heightOfTrees[i + 1], this.K[i + 1], this.digestProvider);
+ }
+ }
+ else
+ {
+ this.nextNextRoot = nextNextRoot;
+ }
+ this.currentRootSig = currentRootSig;
+
+ // calculate numLeafs
+ numLeafs = new int[numLayer];
+ for (int i = 0; i < numLayer; i++)
+ {
+ numLeafs[i] = 1 << heightOfTrees[i];
+ }
+ // construct PRNG
+ this.gmssRandom = new GMSSRandom(messDigestTrees);
+
+ if (numLayer > 1)
+ {
+ // construct the nextNextLeaf (LEAFs++) array for upcoming leafs in
+ // tree after next (TREE++)
+ if (nextNextLeaf == null)
+ {
+ this.nextNextLeaf = new GMSSLeaf[numLayer - 2];
+ for (int i = 0; i < numLayer - 2; i++)
+ {
+ this.nextNextLeaf[i] = new GMSSLeaf(digestProvider.get(), otsIndex[i + 1], numLeafs[i + 2], this.nextNextSeeds[i]);
+ }
+ }
+ else
+ {
+ this.nextNextLeaf = nextNextLeaf;
+ }
+ }
+ else
+ {
+ this.nextNextLeaf = new GMSSLeaf[0];
+ }
+
+ // construct the upperLeaf array for upcoming leafs in tree over the
+ // actual
+ if (upperLeaf == null)
+ {
+ this.upperLeaf = new GMSSLeaf[numLayer - 1];
+ for (int i = 0; i < numLayer - 1; i++)
+ {
+ this.upperLeaf[i] = new GMSSLeaf(digestProvider.get(), otsIndex[i],
+ numLeafs[i + 1], this.currentSeeds[i]);
+ }
+ }
+ else
+ {
+ this.upperLeaf = upperLeaf;
+ }
+
+ // construct the leafs for upcoming leafs in treehashs in tree over the
+ // actual
+ if (upperTreehashLeaf == null)
+ {
+ this.upperTreehashLeaf = new GMSSLeaf[numLayer - 1];
+ for (int i = 0; i < numLayer - 1; i++)
+ {
+ this.upperTreehashLeaf[i] = new GMSSLeaf(digestProvider.get(), otsIndex[i], numLeafs[i + 1]);
+ }
+ }
+ else
+ {
+ this.upperTreehashLeaf = upperTreehashLeaf;
+ }
+
+ if (minTreehash == null)
+ {
+ this.minTreehash = new int[numLayer - 1];
+ for (int i = 0; i < numLayer - 1; i++)
+ {
+ this.minTreehash[i] = -1;
+ }
+ }
+ else
+ {
+ this.minTreehash = minTreehash;
+ }
+
+ // construct the nextRootSig (RootSig++)
+ byte[] dummy = new byte[mdLength];
+ byte[] OTSseed = new byte[mdLength];
+ if (nextRootSig == null)
+ {
+ this.nextRootSig = new GMSSRootSig[numLayer - 1];
+ for (int i = 0; i < numLayer - 1; i++)
+ {
+ System.arraycopy(currentSeeds[i], 0, dummy, 0, mdLength);
+ gmssRandom.nextSeed(dummy);
+ OTSseed = gmssRandom.nextSeed(dummy);
+ this.nextRootSig[i] = new GMSSRootSig(digestProvider.get(), otsIndex[i],
+ heightOfTrees[i + 1]);
+ this.nextRootSig[i].initSign(OTSseed, nextRoot[i]);
+ }
+ }
+ else
+ {
+ this.nextRootSig = nextRootSig;
+ }
+ }
+
+ // we assume this only gets called from nextKey so used is never copied.
+ private GMSSPrivateKeyParameters(GMSSPrivateKeyParameters original)
+ {
+ super(true, original.getParameters());
+
+ this.index = Arrays.clone(original.index);
+ this.currentSeeds = Arrays.clone(original.currentSeeds);
+ this.nextNextSeeds = Arrays.clone(original.nextNextSeeds);
+ this.currentAuthPaths = Arrays.clone(original.currentAuthPaths);
+ this.nextAuthPaths = Arrays.clone(original.nextAuthPaths);
+ this.currentTreehash = original.currentTreehash;
+ this.nextTreehash = original.nextTreehash;
+ this.currentStack = original.currentStack;
+ this.nextStack = original.nextStack;
+ this.currentRetain = original.currentRetain;
+ this.nextRetain = original.nextRetain;
+ this.keep = Arrays.clone(original.keep);
+ this.nextNextLeaf = original.nextNextLeaf;
+ this.upperLeaf = original.upperLeaf;
+ this.upperTreehashLeaf = original.upperTreehashLeaf;
+ this.minTreehash = original.minTreehash;
+ this.gmssPS = original.gmssPS;
+ this.nextRoot = Arrays.clone(original.nextRoot);
+ this.nextNextRoot = original.nextNextRoot;
+ this.currentRootSig = original.currentRootSig;
+ this.nextRootSig = original.nextRootSig;
+ this.digestProvider = original.digestProvider;
+ this.heightOfTrees = original.heightOfTrees;
+ this.otsIndex = original.otsIndex;
+ this.K = original.K;
+ this.numLayer = original.numLayer;
+ this.messDigestTrees = original.messDigestTrees;
+ this.mdLength = original.mdLength;
+ this.gmssRandom = original.gmssRandom;
+ this.numLeafs = original.numLeafs;
+ }
+
+ public boolean isUsed()
+ {
+ return this.used;
+ }
+
+ public void markUsed()
+ {
+ this.used = true;
+ }
+
+ public GMSSPrivateKeyParameters nextKey()
+ {
+ GMSSPrivateKeyParameters nKey = new GMSSPrivateKeyParameters(this);
+
+ nKey.nextKey(gmssPS.getNumOfLayers() - 1);
+
+ return nKey;
+ }
+
+ /**
+ * This method updates the GMSS private key for the next signature
+ *
+ * @param layer the layer where the next key is processed
+ */
+ private void nextKey(int layer)
+ {
+ // only for lowest layer ( other layers indices are raised in nextTree()
+ // method )
+ if (layer == numLayer - 1)
+ {
+ index[layer]++;
+ } // else System.out.println(" --- nextKey on layer " + layer + "
+ // index is now : " + index[layer]);
+
+ // if tree of this layer is depleted
+ if (index[layer] == numLeafs[layer])
+ {
+ if (numLayer != 1)
+ {
+ nextTree(layer);
+ index[layer] = 0;
+ }
+ }
+ else
+ {
+ updateKey(layer);
+ }
+ }
+
+ /**
+ * Switch to next subtree if the current one is depleted
+ *
+ * @param layer the layer where the next tree is processed
+ */
+ private void nextTree(int layer)
+ {
+ // System.out.println("NextTree method called on layer " + layer);
+ // dont create next tree for the top layer
+ if (layer > 0)
+ {
+ // raise index for upper layer
+ index[layer - 1]++;
+
+ // test if it is already the last tree
+ boolean lastTree = true;
+ int z = layer;
+ do
+ {
+ z--;
+ if (index[z] < numLeafs[z])
+ {
+ lastTree = false;
+ }
+ }
+ while (lastTree && (z > 0));
+
+ // only construct next subtree if last one is not already in use
+ if (!lastTree)
+ {
+ gmssRandom.nextSeed(currentSeeds[layer]);
+
+ // last step of distributed signature calculation
+ nextRootSig[layer - 1].updateSign();
+
+ // last step of distributed leaf calculation for nextNextLeaf
+ if (layer > 1)
+ {
+ nextNextLeaf[layer - 1 - 1] = nextNextLeaf[layer - 1 - 1].nextLeaf();
+ }
+
+ // last step of distributed leaf calculation for upper leaf
+ upperLeaf[layer - 1] = upperLeaf[layer - 1].nextLeaf();
+
+ // last step of distributed leaf calculation for all treehashs
+
+ if (minTreehash[layer - 1] >= 0)
+ {
+ upperTreehashLeaf[layer - 1] = upperTreehashLeaf[layer - 1].nextLeaf();
+ byte[] leaf = this.upperTreehashLeaf[layer - 1].getLeaf();
+ // if update is required use the precomputed leaf to update
+ // treehash
+ try
+ {
+ currentTreehash[layer - 1][minTreehash[layer - 1]]
+ .update(this.gmssRandom, leaf);
+ // System.out.println("UUUpdated TH " +
+ // minTreehash[layer - 1]);
+ if (currentTreehash[layer - 1][minTreehash[layer - 1]]
+ .wasFinished())
+ {
+ // System.out.println("FFFinished TH " +
+ // minTreehash[layer - 1]);
+ }
+ }
+ catch (Exception e)
+ {
+ System.out.println(e);
+ }
+ }
+
+ // last step of nextNextAuthRoot calculation
+ this.updateNextNextAuthRoot(layer);
+
+ // ******************************************************** /
+
+ // NOW: advance to next tree on layer 'layer'
+
+ // NextRootSig --> currentRootSigs
+ this.currentRootSig[layer - 1] = nextRootSig[layer - 1]
+ .getSig();
+
+ // -----------------------
+
+ // nextTreehash --> currentTreehash
+ // nextNextTreehash --> nextTreehash
+ for (int i = 0; i < heightOfTrees[layer] - K[layer]; i++)
+ {
+ this.currentTreehash[layer][i] = this.nextTreehash[layer - 1][i];
+ this.nextTreehash[layer - 1][i] = this.nextNextRoot[layer - 1]
+ .getTreehash()[i];
+ }
+
+ // NextAuthPath --> currentAuthPath
+ // nextNextAuthPath --> nextAuthPath
+ for (int i = 0; i < heightOfTrees[layer]; i++)
+ {
+ System.arraycopy(nextAuthPaths[layer - 1][i], 0,
+ currentAuthPaths[layer][i], 0, mdLength);
+ System.arraycopy(nextNextRoot[layer - 1].getAuthPath()[i],
+ 0, nextAuthPaths[layer - 1][i], 0, mdLength);
+ }
+
+ // nextRetain --> currentRetain
+ // nextNextRetain --> nextRetain
+ for (int i = 0; i < K[layer] - 1; i++)
+ {
+ this.currentRetain[layer][i] = this.nextRetain[layer - 1][i];
+ this.nextRetain[layer - 1][i] = this.nextNextRoot[layer - 1]
+ .getRetain()[i];
+ }
+
+ // nextStack --> currentStack
+ this.currentStack[layer] = this.nextStack[layer - 1];
+ // nextNextStack --> nextStack
+ this.nextStack[layer - 1] = this.nextNextRoot[layer - 1]
+ .getStack();
+
+ // nextNextRoot --> nextRoot
+ this.nextRoot[layer - 1] = this.nextNextRoot[layer - 1]
+ .getRoot();
+ // -----------------------
+
+ // -----------------
+ byte[] OTSseed = new byte[mdLength];
+ byte[] dummy = new byte[mdLength];
+ // gmssRandom.setSeed(currentSeeds[layer]);
+ System
+ .arraycopy(currentSeeds[layer - 1], 0, dummy, 0,
+ mdLength);
+ OTSseed = gmssRandom.nextSeed(dummy); // only need OTSSeed
+ OTSseed = gmssRandom.nextSeed(dummy);
+ OTSseed = gmssRandom.nextSeed(dummy);
+ // nextWinSig[layer-1]=new
+ // GMSSWinSig(OTSseed,algNames,otsIndex[layer-1],heightOfTrees[layer],nextRoot[layer-1]);
+ nextRootSig[layer - 1].initSign(OTSseed, nextRoot[layer - 1]);
+
+ // nextKey for upper layer
+ nextKey(layer - 1);
+ }
+ }
+ }
+
+ /**
+ * This method computes the authpath (AUTH) for the current tree,
+ * Additionally the root signature for the next tree (SIG+), the authpath
+ * (AUTH++) and root (ROOT++) for the tree after next in layer
+ * <code>layer</code>, and the LEAF++^1 for the next next tree in the
+ * layer above are updated This method is used by nextKey()
+ *
+ * @param layer
+ */
+ private void updateKey(int layer)
+ {
+ // ----------current tree processing of actual layer---------
+ // compute upcoming authpath for current Tree (AUTH)
+ computeAuthPaths(layer);
+
+ // -----------distributed calculations part------------
+ // not for highest tree layer
+ if (layer > 0)
+ {
+
+ // compute (partial) next leaf on TREE++ (not on layer 1 and 0)
+ if (layer > 1)
+ {
+ nextNextLeaf[layer - 1 - 1] = nextNextLeaf[layer - 1 - 1].nextLeaf();
+ }
+
+ // compute (partial) next leaf on tree above (not on layer 0)
+ upperLeaf[layer - 1] = upperLeaf[layer - 1].nextLeaf();
+
+ // compute (partial) next leaf for all treehashs on tree above (not
+ // on layer 0)
+
+ int t = (int)Math
+ .floor((double)(this.getNumLeafs(layer) * 2)
+ / (double)(this.heightOfTrees[layer - 1] - this.K[layer - 1]));
+
+ if (index[layer] % t == 1)
+ {
+ // System.out.println(" layer: " + layer + " index: " +
+ // index[layer] + " t : " + t);
+
+ // take precomputed node for treehash update
+ // ------------------------------------------------
+ if (index[layer] > 1 && minTreehash[layer - 1] >= 0)
+ {
+ byte[] leaf = this.upperTreehashLeaf[layer - 1].getLeaf();
+ // if update is required use the precomputed leaf to update
+ // treehash
+ try
+ {
+ currentTreehash[layer - 1][minTreehash[layer - 1]]
+ .update(this.gmssRandom, leaf);
+ // System.out.println("Updated TH " + minTreehash[layer
+ // - 1]);
+ if (currentTreehash[layer - 1][minTreehash[layer - 1]]
+ .wasFinished())
+ {
+ // System.out.println("Finished TH " +
+ // minTreehash[layer - 1]);
+ }
+ }
+ catch (Exception e)
+ {
+ System.out.println(e);
+ }
+ // ------------------------------------------------
+ }
+
+ // initialize next leaf precomputation
+ // ------------------------------------------------
+
+ // get lowest index of treehashs
+ this.minTreehash[layer - 1] = getMinTreehashIndex(layer - 1);
+
+ if (this.minTreehash[layer - 1] >= 0)
+ {
+ // initialize leaf
+ byte[] seed = this.currentTreehash[layer - 1][this.minTreehash[layer - 1]]
+ .getSeedActive();
+ this.upperTreehashLeaf[layer - 1] = new GMSSLeaf(
+ this.digestProvider.get(), this.otsIndex[layer - 1], t, seed);
+ this.upperTreehashLeaf[layer - 1] = this.upperTreehashLeaf[layer - 1].nextLeaf();
+ // System.out.println("restarted treehashleaf (" + (layer -
+ // 1) + "," + this.minTreehash[layer - 1] + ")");
+ }
+ // ------------------------------------------------
+
+ }
+ else
+ {
+ // update the upper leaf for the treehash one step
+ if (this.minTreehash[layer - 1] >= 0)
+ {
+ this.upperTreehashLeaf[layer - 1] = this.upperTreehashLeaf[layer - 1].nextLeaf();
+ // if (minTreehash[layer - 1] > 3)
+ // System.out.print("#");
+ }
+ }
+
+ // compute (partial) the signature of ROOT+ (RootSig+) (not on top
+ // layer)
+ nextRootSig[layer - 1].updateSign();
+
+ // compute (partial) AUTHPATH++ & ROOT++ (not on top layer)
+ if (index[layer] == 1)
+ {
+ // init root and authpath calculation for tree after next
+ // (AUTH++, ROOT++)
+ this.nextNextRoot[layer - 1].initialize(new Vector());
+ }
+
+ // update root and authpath calculation for tree after next (AUTH++,
+ // ROOT++)
+ this.updateNextNextAuthRoot(layer);
+ }
+ // ----------- end distributed calculations part-----------------
+ }
+
+ /**
+ * This method returns the index of the next Treehash instance that should
+ * receive an update
+ *
+ * @param layer the layer of the GMSS tree
+ * @return index of the treehash instance that should get the update
+ */
+ private int getMinTreehashIndex(int layer)
+ {
+ int minTreehash = -1;
+ for (int h = 0; h < heightOfTrees[layer] - K[layer]; h++)
+ {
+ if (currentTreehash[layer][h].wasInitialized()
+ && !currentTreehash[layer][h].wasFinished())
+ {
+ if (minTreehash == -1)
+ {
+ minTreehash = h;
+ }
+ else if (currentTreehash[layer][h].getLowestNodeHeight() < currentTreehash[layer][minTreehash]
+ .getLowestNodeHeight())
+ {
+ minTreehash = h;
+ }
+ }
+ }
+ return minTreehash;
+ }
+
+ /**
+ * Computes the upcoming currentAuthpath of layer <code>layer</code> using
+ * the revisited authentication path computation of Dahmen/Schneider 2008
+ *
+ * @param layer the actual layer
+ */
+ private void computeAuthPaths(int layer)
+ {
+
+ int Phi = index[layer];
+ int H = heightOfTrees[layer];
+ int K = this.K[layer];
+
+ // update all nextSeeds for seed scheduling
+ for (int i = 0; i < H - K; i++)
+ {
+ currentTreehash[layer][i].updateNextSeed(gmssRandom);
+ }
+
+ // STEP 1 of Algorithm
+ int Tau = heightOfPhi(Phi);
+
+ byte[] OTSseed = new byte[mdLength];
+ OTSseed = gmssRandom.nextSeed(currentSeeds[layer]);
+
+ // STEP 2 of Algorithm
+ // if phi's parent on height tau + 1 if left node, store auth_tau
+ // in keep_tau.
+ // TODO check it, formerly was
+ // int L = Phi / (int) Math.floor(Math.pow(2, Tau + 1));
+ // L %= 2;
+ int L = (Phi >>> (Tau + 1)) & 1;
+
+ byte[] tempKeep = new byte[mdLength];
+ // store the keep node not in keep[layer][tau/2] because it might be in
+ // use
+ // wait until the space is freed in step 4a
+ if (Tau < H - 1 && L == 0)
+ {
+ System.arraycopy(currentAuthPaths[layer][Tau], 0, tempKeep, 0,
+ mdLength);
+ }
+
+ byte[] help = new byte[mdLength];
+ // STEP 3 of Algorithm
+ // if phi is left child, compute and store leaf for next currentAuthPath
+ // path,
+ // (obtained by veriying current signature)
+ if (Tau == 0)
+ {
+ // LEAFCALC !!!
+ if (layer == numLayer - 1)
+ { // lowest layer computes the
+ // necessary leaf completely at this
+ // time
+ WinternitzOTSignature ots = new WinternitzOTSignature(OTSseed,
+ digestProvider.get(), otsIndex[layer]);
+ help = ots.getPublicKey();
+ }
+ else
+ { // other layers use the precomputed leafs in
+ // nextNextLeaf
+ byte[] dummy = new byte[mdLength];
+ System.arraycopy(currentSeeds[layer], 0, dummy, 0, mdLength);
+ gmssRandom.nextSeed(dummy);
+ help = upperLeaf[layer].getLeaf();
+ this.upperLeaf[layer].initLeafCalc(dummy);
+
+ // WinternitzOTSVerify otsver = new
+ // WinternitzOTSVerify(algNames, otsIndex[layer]);
+ // byte[] help2 = otsver.Verify(currentRoot[layer],
+ // currentRootSig[layer]);
+ // System.out.println(" --- " + layer + " " +
+ // ByteUtils.toHexString(help) + " " +
+ // ByteUtils.toHexString(help2));
+ }
+ System.arraycopy(help, 0, currentAuthPaths[layer][0], 0, mdLength);
+ }
+ else
+ {
+ // STEP 4a of Algorithm
+ // get new left currentAuthPath node on height tau
+ byte[] toBeHashed = new byte[mdLength << 1];
+ System.arraycopy(currentAuthPaths[layer][Tau - 1], 0, toBeHashed,
+ 0, mdLength);
+ // free the shared keep[layer][tau/2]
+ System.arraycopy(keep[layer][(int)Math.floor((Tau - 1) / 2)], 0,
+ toBeHashed, mdLength, mdLength);
+ messDigestTrees.update(toBeHashed, 0, toBeHashed.length);
+ currentAuthPaths[layer][Tau] = new byte[messDigestTrees.getDigestSize()];
+ messDigestTrees.doFinal(currentAuthPaths[layer][Tau], 0);
+
+ // STEP 4b and 4c of Algorithm
+ // copy right nodes to currentAuthPath on height 0..Tau-1
+ for (int i = 0; i < Tau; i++)
+ {
+
+ // STEP 4b of Algorithm
+ // 1st: copy from treehashs
+ if (i < H - K)
+ {
+ if (currentTreehash[layer][i].wasFinished())
+ {
+ System.arraycopy(currentTreehash[layer][i]
+ .getFirstNode(), 0, currentAuthPaths[layer][i],
+ 0, mdLength);
+ currentTreehash[layer][i].destroy();
+ }
+ else
+ {
+ System.err
+ .println("Treehash ("
+ + layer
+ + ","
+ + i
+ + ") not finished when needed in AuthPathComputation");
+ }
+ }
+
+ // 2nd: copy precomputed values from Retain
+ if (i < H - 1 && i >= H - K)
+ {
+ if (currentRetain[layer][i - (H - K)].size() > 0)
+ {
+ // pop element from retain
+ System.arraycopy(currentRetain[layer][i - (H - K)]
+ .lastElement(), 0, currentAuthPaths[layer][i],
+ 0, mdLength);
+ currentRetain[layer][i - (H - K)]
+ .removeElementAt(currentRetain[layer][i
+ - (H - K)].size() - 1);
+ }
+ }
+
+ // STEP 4c of Algorithm
+ // initialize new stack at heights 0..Tau-1
+ if (i < H - K)
+ {
+ // create stacks anew
+ int startPoint = Phi + 3 * (1 << i);
+ if (startPoint < numLeafs[layer])
+ {
+ // if (layer < 2) {
+ // System.out.println("initialized TH " + i + " on layer
+ // " + layer);
+ // }
+ currentTreehash[layer][i].initialize();
+ }
+ }
+ }
+ }
+
+ // now keep space is free to use
+ if (Tau < H - 1 && L == 0)
+ {
+ System.arraycopy(tempKeep, 0,
+ keep[layer][(int)Math.floor(Tau / 2)], 0, mdLength);
+ }
+
+ // only update empty stack at height h if all other stacks have
+ // tailnodes with height >h
+ // finds active stack with lowest node height, choses lower index in
+ // case of tie
+
+ // on the lowest layer leafs must be computed at once, no precomputation
+ // is possible. So all treehash updates are done at once here
+ if (layer == numLayer - 1)
+ {
+ for (int tmp = 1; tmp <= (H - K) / 2; tmp++)
+ {
+ // index of the treehash instance that receives the next update
+ int minTreehash = getMinTreehashIndex(layer);
+
+ // if active treehash is found update with a leaf
+ if (minTreehash >= 0)
+ {
+ try
+ {
+ byte[] seed = new byte[mdLength];
+ System.arraycopy(
+ this.currentTreehash[layer][minTreehash]
+ .getSeedActive(), 0, seed, 0, mdLength);
+ byte[] seed2 = gmssRandom.nextSeed(seed);
+ WinternitzOTSignature ots = new WinternitzOTSignature(
+ seed2, this.digestProvider.get(), this.otsIndex[layer]);
+ byte[] leaf = ots.getPublicKey();
+ currentTreehash[layer][minTreehash].update(
+ this.gmssRandom, leaf);
+ }
+ catch (Exception e)
+ {
+ System.out.println(e);
+ }
+ }
+ }
+ }
+ else
+ { // on higher layers the updates are done later
+ this.minTreehash[layer] = getMinTreehashIndex(layer);
+ }
+ }
+
+ /**
+ * Returns the largest h such that 2^h | Phi
+ *
+ * @param Phi the leaf index
+ * @return The largest <code>h</code> with <code>2^h | Phi</code> if
+ * <code>Phi!=0</code> else return <code>-1</code>
+ */
+ private int heightOfPhi(int Phi)
+ {
+ if (Phi == 0)
+ {
+ return -1;
+ }
+ int Tau = 0;
+ int modul = 1;
+ while (Phi % modul == 0)
+ {
+ modul *= 2;
+ Tau += 1;
+ }
+ return Tau - 1;
+ }
+
+ /**
+ * Updates the authentication path and root calculation for the tree after
+ * next (AUTH++, ROOT++) in layer <code>layer</code>
+ *
+ * @param layer
+ */
+ private void updateNextNextAuthRoot(int layer)
+ {
+
+ byte[] OTSseed = new byte[mdLength];
+ OTSseed = gmssRandom.nextSeed(nextNextSeeds[layer - 1]);
+
+ // get the necessary leaf
+ if (layer == numLayer - 1)
+ { // lowest layer computes the necessary
+ // leaf completely at this time
+ WinternitzOTSignature ots = new WinternitzOTSignature(OTSseed,
+ digestProvider.get(), otsIndex[layer]);
+ this.nextNextRoot[layer - 1].update(nextNextSeeds[layer - 1], ots
+ .getPublicKey());
+ }
+ else
+ { // other layers use the precomputed leafs in nextNextLeaf
+ this.nextNextRoot[layer - 1].update(nextNextSeeds[layer - 1], nextNextLeaf[layer - 1].getLeaf());
+ this.nextNextLeaf[layer - 1].initLeafCalc(nextNextSeeds[layer - 1]);
+ }
+ }
+
+ public int[] getIndex()
+ {
+ return index;
+ }
+
+ /**
+ * @return The current index of layer i
+ */
+ public int getIndex(int i)
+ {
+ return index[i];
+ }
+
+ public byte[][] getCurrentSeeds()
+ {
+ return Arrays.clone(currentSeeds);
+ }
+
+ public byte[][][] getCurrentAuthPaths()
+ {
+ return Arrays.clone(currentAuthPaths);
+ }
+
+ /**
+ * @return The one-time signature of the root of the current subtree
+ */
+ public byte[] getSubtreeRootSig(int i)
+ {
+ return currentRootSig[i];
+ }
+
+
+ public GMSSDigestProvider getName()
+ {
+ return digestProvider;
+ }
+
+ /**
+ * @return The number of leafs of each tree of layer i
+ */
+ public int getNumLeafs(int i)
+ {
+ return numLeafs[i];
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSPublicKeyParameters.java
new file mode 100644
index 00000000..492802d4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSPublicKeyParameters.java
@@ -0,0 +1,33 @@
+package org.bouncycastle.pqc.crypto.gmss;
+
+
+public class GMSSPublicKeyParameters
+ extends GMSSKeyParameters
+{
+ /**
+ * The GMSS public key
+ */
+ private byte[] gmssPublicKey;
+
+ /**
+ * The constructor.
+ *
+ * @param key a raw GMSS public key
+ * @param gmssParameterSet an instance of GMSSParameterset
+ */
+ public GMSSPublicKeyParameters(byte[] key, GMSSParameters gmssParameterSet)
+ {
+ super(false, gmssParameterSet);
+ this.gmssPublicKey = key;
+ }
+
+ /**
+ * Returns the GMSS public key
+ *
+ * @return The GMSS public key
+ */
+ public byte[] getPublicKey()
+ {
+ return gmssPublicKey;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSRootCalc.java b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSRootCalc.java
new file mode 100644
index 00000000..35ac2e32
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSRootCalc.java
@@ -0,0 +1,596 @@
+package org.bouncycastle.pqc.crypto.gmss;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Integers;
+import org.bouncycastle.util.encoders.Hex;
+
+
+/**
+ * This class computes a whole Merkle tree and saves the needed values for
+ * AuthPath computation. It is used for precomputation of the root of a
+ * following tree. After initialization, 2^H updates are required to complete
+ * the root. Every update requires one leaf value as parameter. While computing
+ * the root all initial values for the authentication path algorithm (treehash,
+ * auth, retain) are stored for later use.
+ */
+public class GMSSRootCalc
+{
+
+ /**
+ * max height of the tree
+ */
+ private int heightOfTree;
+
+ /**
+ * length of the messageDigest
+ */
+ private int mdLength;
+
+ /**
+ * the treehash instances of the tree
+ */
+ private Treehash[] treehash;
+
+ /**
+ * stores the retain nodes for authPath computation
+ */
+ private Vector[] retain;
+
+ /**
+ * finally stores the root of the tree when finished
+ */
+ private byte[] root;
+
+ /**
+ * stores the authentication path y_1(i), i = 0..H-1
+ */
+ private byte[][] AuthPath;
+
+ /**
+ * the value K for the authentication path computation
+ */
+ private int K;
+
+ /**
+ * Vector element that stores the nodes on the stack
+ */
+ private Vector tailStack;
+
+ /**
+ * stores the height of all nodes laying on the tailStack
+ */
+ private Vector heightOfNodes;
+ /**
+ * The hash function used for the construction of the authentication trees
+ */
+ private Digest messDigestTree;
+
+ /**
+ * An array of strings containing the name of the hash function used to
+ * construct the authentication trees and used by the OTS.
+ */
+ private GMSSDigestProvider digestProvider;
+
+ /**
+ * stores the index of the current node on each height of the tree
+ */
+ private int[] index;
+
+ /**
+ * true if instance was already initialized, false otherwise
+ */
+ private boolean isInitialized;
+
+ /**
+ * true it instance was finished
+ */
+ private boolean isFinished;
+
+ /**
+ * Integer that stores the index of the next seed that has to be omitted to
+ * the treehashs
+ */
+ private int indexForNextSeed;
+
+ /**
+ * temporary integer that stores the height of the next treehash instance
+ * that gets initialized with a seed
+ */
+ private int heightOfNextSeed;
+
+ /**
+ * This constructor regenerates a prior treehash object
+ *
+ * @param digest an array of strings, containing the digest of the used hash
+ * function and PRNG and the digest of the corresponding
+ * provider
+ * @param statByte status bytes
+ * @param statInt status ints
+ */
+ public GMSSRootCalc(Digest digest, byte[][] statByte, int[] statInt,
+ Treehash[] treeH, Vector[] ret)
+ {
+ this.messDigestTree = digestProvider.get();
+ this.digestProvider = digestProvider;
+ // decode statInt
+ this.heightOfTree = statInt[0];
+ this.mdLength = statInt[1];
+ this.K = statInt[2];
+ this.indexForNextSeed = statInt[3];
+ this.heightOfNextSeed = statInt[4];
+ if (statInt[5] == 1)
+ {
+ this.isFinished = true;
+ }
+ else
+ {
+ this.isFinished = false;
+ }
+ if (statInt[6] == 1)
+ {
+ this.isInitialized = true;
+ }
+ else
+ {
+ this.isInitialized = false;
+ }
+
+ int tailLength = statInt[7];
+
+ this.index = new int[heightOfTree];
+ for (int i = 0; i < heightOfTree; i++)
+ {
+ this.index[i] = statInt[8 + i];
+ }
+
+ this.heightOfNodes = new Vector();
+ for (int i = 0; i < tailLength; i++)
+ {
+ this.heightOfNodes.addElement(Integers.valueOf(statInt[8 + heightOfTree
+ + i]));
+ }
+
+ // decode statByte
+ this.root = statByte[0];
+
+ this.AuthPath = new byte[heightOfTree][mdLength];
+ for (int i = 0; i < heightOfTree; i++)
+ {
+ this.AuthPath[i] = statByte[1 + i];
+ }
+
+ this.tailStack = new Vector();
+ for (int i = 0; i < tailLength; i++)
+ {
+ this.tailStack.addElement(statByte[1 + heightOfTree + i]);
+ }
+
+ // decode treeH
+ this.treehash = GMSSUtils.clone(treeH);
+
+ // decode ret
+ this.retain = GMSSUtils.clone(ret);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param heightOfTree maximal height of the tree
+ * @param digestProvider an array of strings, containing the name of the used hash
+ * function and PRNG and the name of the corresponding
+ * provider
+ */
+ public GMSSRootCalc(int heightOfTree, int K, GMSSDigestProvider digestProvider)
+ {
+ this.heightOfTree = heightOfTree;
+ this.digestProvider = digestProvider;
+ this.messDigestTree = digestProvider.get();
+ this.mdLength = messDigestTree.getDigestSize();
+ this.K = K;
+ this.index = new int[heightOfTree];
+ this.AuthPath = new byte[heightOfTree][mdLength];
+ this.root = new byte[mdLength];
+ // this.treehash = new Treehash[this.heightOfTree - this.K];
+ this.retain = new Vector[this.K - 1];
+ for (int i = 0; i < K - 1; i++)
+ {
+ this.retain[i] = new Vector();
+ }
+
+ }
+
+ /**
+ * Initializes the calculation of a new root
+ *
+ * @param sharedStack the stack shared by all treehash instances of this tree
+ */
+ public void initialize(Vector sharedStack)
+ {
+ this.treehash = new Treehash[this.heightOfTree - this.K];
+ for (int i = 0; i < this.heightOfTree - this.K; i++)
+ {
+ this.treehash[i] = new Treehash(sharedStack, i, this.digestProvider.get());
+ }
+
+ this.index = new int[heightOfTree];
+ this.AuthPath = new byte[heightOfTree][mdLength];
+ this.root = new byte[mdLength];
+
+ this.tailStack = new Vector();
+ this.heightOfNodes = new Vector();
+ this.isInitialized = true;
+ this.isFinished = false;
+
+ for (int i = 0; i < heightOfTree; i++)
+ {
+ this.index[i] = -1;
+ }
+
+ this.retain = new Vector[this.K - 1];
+ for (int i = 0; i < K - 1; i++)
+ {
+ this.retain[i] = new Vector();
+ }
+
+ this.indexForNextSeed = 3;
+ this.heightOfNextSeed = 0;
+ }
+
+ /**
+ * updates the root with one leaf and stores needed values in retain,
+ * treehash or authpath. Additionally counts the seeds used. This method is
+ * used when performing the updates for TREE++.
+ *
+ * @param seed the initial seed for treehash: seedNext
+ * @param leaf the height of the treehash
+ */
+ public void update(byte[] seed, byte[] leaf)
+ {
+ if (this.heightOfNextSeed < (this.heightOfTree - this.K)
+ && this.indexForNextSeed - 2 == index[0])
+ {
+ this.initializeTreehashSeed(seed, this.heightOfNextSeed);
+ this.heightOfNextSeed++;
+ this.indexForNextSeed *= 2;
+ }
+ // now call the simple update
+ this.update(leaf);
+ }
+
+ /**
+ * Updates the root with one leaf and stores the needed values in retain,
+ * treehash or authpath
+ */
+ public void update(byte[] leaf)
+ {
+
+ if (isFinished)
+ {
+ System.out.print("Too much updates for Tree!!");
+ return;
+ }
+ if (!isInitialized)
+ {
+ System.err.println("GMSSRootCalc not initialized!");
+ return;
+ }
+
+ // a new leaf was omitted, so raise index on lowest layer
+ index[0]++;
+
+ // store the nodes on the lowest layer in treehash or authpath
+ if (index[0] == 1)
+ {
+ System.arraycopy(leaf, 0, AuthPath[0], 0, mdLength);
+ }
+ else if (index[0] == 3)
+ {
+ // store in treehash only if K < H
+ if (heightOfTree > K)
+ {
+ treehash[0].setFirstNode(leaf);
+ }
+ }
+
+ if ((index[0] - 3) % 2 == 0 && index[0] >= 3)
+ {
+ // store in retain if K = H
+ if (heightOfTree == K)
+ // TODO: check it
+ {
+ retain[0].insertElementAt(leaf, 0);
+ }
+ }
+
+ // if first update to this tree is made
+ if (index[0] == 0)
+ {
+ tailStack.addElement(leaf);
+ heightOfNodes.addElement(Integers.valueOf(0));
+ }
+ else
+ {
+
+ byte[] help = new byte[mdLength];
+ byte[] toBeHashed = new byte[mdLength << 1];
+
+ // store the new leaf in help
+ System.arraycopy(leaf, 0, help, 0, mdLength);
+ int helpHeight = 0;
+ // while top to nodes have same height
+ while (tailStack.size() > 0
+ && helpHeight == ((Integer)heightOfNodes.lastElement())
+ .intValue())
+ {
+
+ // help <-- hash(stack top element || help)
+ System.arraycopy(tailStack.lastElement(), 0, toBeHashed, 0,
+ mdLength);
+ tailStack.removeElementAt(tailStack.size() - 1);
+ heightOfNodes.removeElementAt(heightOfNodes.size() - 1);
+ System.arraycopy(help, 0, toBeHashed, mdLength, mdLength);
+
+ messDigestTree.update(toBeHashed, 0, toBeHashed.length);
+ help = new byte[messDigestTree.getDigestSize()];
+ messDigestTree.doFinal(help, 0);
+
+ // the new help node is one step higher
+ helpHeight++;
+ if (helpHeight < heightOfTree)
+ {
+ index[helpHeight]++;
+
+ // add index 1 element to initial authpath
+ if (index[helpHeight] == 1)
+ {
+ System.arraycopy(help, 0, AuthPath[helpHeight], 0,
+ mdLength);
+ }
+
+ if (helpHeight >= heightOfTree - K)
+ {
+ if (helpHeight == 0)
+ {
+ System.out.println("M���P");
+ }
+ // add help element to retain stack if it is a right
+ // node
+ // and not stored in treehash
+ if ((index[helpHeight] - 3) % 2 == 0
+ && index[helpHeight] >= 3)
+ // TODO: check it
+ {
+ retain[helpHeight - (heightOfTree - K)]
+ .insertElementAt(help, 0);
+ }
+ }
+ else
+ {
+ // if element is third in his line add it to treehash
+ if (index[helpHeight] == 3)
+ {
+ treehash[helpHeight].setFirstNode(help);
+ }
+ }
+ }
+ }
+ // push help element to the stack
+ tailStack.addElement(help);
+ heightOfNodes.addElement(Integers.valueOf(helpHeight));
+
+ // is the root calculation finished?
+ if (helpHeight == heightOfTree)
+ {
+ isFinished = true;
+ isInitialized = false;
+ root = (byte[])tailStack.lastElement();
+ }
+ }
+
+ }
+
+ /**
+ * initializes the seeds for the treehashs of the tree precomputed by this
+ * class
+ *
+ * @param seed the initial seed for treehash: seedNext
+ * @param index the height of the treehash
+ */
+ public void initializeTreehashSeed(byte[] seed, int index)
+ {
+ treehash[index].initializeSeed(seed);
+ }
+
+ /**
+ * Method to check whether the instance has been initialized or not
+ *
+ * @return true if treehash was already initialized
+ */
+ public boolean wasInitialized()
+ {
+ return isInitialized;
+ }
+
+ /**
+ * Method to check whether the instance has been finished or not
+ *
+ * @return true if tree has reached its maximum height
+ */
+ public boolean wasFinished()
+ {
+ return isFinished;
+ }
+
+ /**
+ * returns the authentication path of the first leaf of the tree
+ *
+ * @return the authentication path of the first leaf of the tree
+ */
+ public byte[][] getAuthPath()
+ {
+ return GMSSUtils.clone(AuthPath);
+ }
+
+ /**
+ * returns the initial treehash instances, storing value y_3(i)
+ *
+ * @return the initial treehash instances, storing value y_3(i)
+ */
+ public Treehash[] getTreehash()
+ {
+ return GMSSUtils.clone(treehash);
+ }
+
+ /**
+ * returns the retain stacks storing all right nodes near to the root
+ *
+ * @return the retain stacks storing all right nodes near to the root
+ */
+ public Vector[] getRetain()
+ {
+ return GMSSUtils.clone(retain);
+ }
+
+ /**
+ * returns the finished root value
+ *
+ * @return the finished root value
+ */
+ public byte[] getRoot()
+ {
+ return Arrays.clone(root);
+ }
+
+ /**
+ * returns the shared stack
+ *
+ * @return the shared stack
+ */
+ public Vector getStack()
+ {
+ Vector copy = new Vector();
+ for (Enumeration en = tailStack.elements(); en.hasMoreElements();)
+ {
+ copy.addElement(en.nextElement());
+ }
+ return copy;
+ }
+
+ /**
+ * Returns the status byte array used by the GMSSPrivateKeyASN.1 class
+ *
+ * @return The status bytes
+ */
+ public byte[][] getStatByte()
+ {
+
+ int tailLength;
+ if (tailStack == null)
+ {
+ tailLength = 0;
+ }
+ else
+ {
+ tailLength = tailStack.size();
+ }
+ byte[][] statByte = new byte[1 + heightOfTree + tailLength][64]; //FIXME: messDigestTree.getByteLength()
+ statByte[0] = root;
+
+ for (int i = 0; i < heightOfTree; i++)
+ {
+ statByte[1 + i] = AuthPath[i];
+ }
+ for (int i = 0; i < tailLength; i++)
+ {
+ statByte[1 + heightOfTree + i] = (byte[])tailStack.elementAt(i);
+ }
+
+ return statByte;
+ }
+
+ /**
+ * Returns the status int array used by the GMSSPrivateKeyASN.1 class
+ *
+ * @return The status ints
+ */
+ public int[] getStatInt()
+ {
+
+ int tailLength;
+ if (tailStack == null)
+ {
+ tailLength = 0;
+ }
+ else
+ {
+ tailLength = tailStack.size();
+ }
+ int[] statInt = new int[8 + heightOfTree + tailLength];
+ statInt[0] = heightOfTree;
+ statInt[1] = mdLength;
+ statInt[2] = K;
+ statInt[3] = indexForNextSeed;
+ statInt[4] = heightOfNextSeed;
+ if (isFinished)
+ {
+ statInt[5] = 1;
+ }
+ else
+ {
+ statInt[5] = 0;
+ }
+ if (isInitialized)
+ {
+ statInt[6] = 1;
+ }
+ else
+ {
+ statInt[6] = 0;
+ }
+ statInt[7] = tailLength;
+
+ for (int i = 0; i < heightOfTree; i++)
+ {
+ statInt[8 + i] = index[i];
+ }
+ for (int i = 0; i < tailLength; i++)
+ {
+ statInt[8 + heightOfTree + i] = ((Integer)heightOfNodes
+ .elementAt(i)).intValue();
+ }
+
+ return statInt;
+ }
+
+ /**
+ * @return a human readable version of the structure
+ */
+ public String toString()
+ {
+ String out = "";
+ int tailLength;
+ if (tailStack == null)
+ {
+ tailLength = 0;
+ }
+ else
+ {
+ tailLength = tailStack.size();
+ }
+
+ for (int i = 0; i < 8 + heightOfTree + tailLength; i++)
+ {
+ out = out + getStatInt()[i] + " ";
+ }
+ for (int i = 0; i < 1 + heightOfTree + tailLength; i++)
+ {
+ out = out + new String(Hex.encode(getStatByte()[i])) + " ";
+ }
+ out = out + " " + digestProvider.get().getDigestSize();
+ return out;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSRootSig.java b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSRootSig.java
new file mode 100644
index 00000000..8a4796f0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSRootSig.java
@@ -0,0 +1,666 @@
+package org.bouncycastle.pqc.crypto.gmss;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.pqc.crypto.gmss.util.GMSSRandom;
+import org.bouncycastle.util.encoders.Hex;
+
+
+/**
+ * This class implements the distributed signature generation of the Winternitz
+ * one-time signature scheme (OTSS), described in C.Dods, N.P. Smart, and M.
+ * Stam, "Hash Based Digital Signature Schemes", LNCS 3796, pages 96&#8211;115,
+ * 2005. The class is used by the GMSS classes.
+ */
+public class GMSSRootSig
+{
+
+ /**
+ * The hash function used by the OTS
+ */
+ private Digest messDigestOTS;
+
+ /**
+ * The length of the message digest and private key
+ */
+ private int mdsize, keysize;
+
+ /**
+ * The private key
+ */
+ private byte[] privateKeyOTS;
+
+ /**
+ * The message bytes
+ */
+ private byte[] hash;
+
+ /**
+ * The signature bytes
+ */
+ private byte[] sign;
+
+ /**
+ * The Winternitz parameter
+ */
+ private int w;
+
+ /**
+ * The source of randomness for OTS private key generation
+ */
+ private GMSSRandom gmssRandom;
+
+ /**
+ * Sizes of the message
+ */
+ private int messagesize;
+
+ /**
+ * Some precalculated values
+ */
+ private int k;
+
+ /**
+ * Some variables for storing the actual status of distributed signing
+ */
+ private int r, test, counter, ii;
+
+ /**
+ * variables for storing big numbers for the actual status of distributed
+ * signing
+ */
+ private long test8, big8;
+
+ /**
+ * The necessary steps of each updateSign() call
+ */
+ private int steps;
+
+ /**
+ * The checksum part
+ */
+ private int checksum;
+
+ /**
+ * The height of the tree
+ */
+ private int height;
+
+ /**
+ * The current intern OTSseed
+ */
+ private byte[] seed;
+
+ /**
+ * This constructor regenerates a prior GMSSRootSig object used by the
+ * GMSSPrivateKeyASN.1 class
+ *
+ * @param digest an array of strings, containing the digest of the used hash
+ * function, the digest of the PRGN and the names of the
+ * corresponding providers
+ * @param statByte status byte array
+ * @param statInt status int array
+ */
+ public GMSSRootSig(Digest digest, byte[][] statByte, int[] statInt)
+ {
+ messDigestOTS = digest;
+ gmssRandom = new GMSSRandom(messDigestOTS);
+
+ this.counter = statInt[0];
+ this.test = statInt[1];
+ this.ii = statInt[2];
+ this.r = statInt[3];
+ this.steps = statInt[4];
+ this.keysize = statInt[5];
+ this.height = statInt[6];
+ this.w = statInt[7];
+ this.checksum = statInt[8];
+
+ this.mdsize = messDigestOTS.getDigestSize();
+
+ this.k = (1 << w) - 1;
+
+ int mdsizeBit = mdsize << 3;
+ this.messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w);
+
+ this.privateKeyOTS = statByte[0];
+ this.seed = statByte[1];
+ this.hash = statByte[2];
+
+ this.sign = statByte[3];
+
+ this.test8 = ((statByte[4][0] & 0xff))
+ | ((long)(statByte[4][1] & 0xff) << 8)
+ | ((long)(statByte[4][2] & 0xff) << 16)
+ | ((long)(statByte[4][3] & 0xff)) << 24
+ | ((long)(statByte[4][4] & 0xff)) << 32
+ | ((long)(statByte[4][5] & 0xff)) << 40
+ | ((long)(statByte[4][6] & 0xff)) << 48
+ | ((long)(statByte[4][7] & 0xff)) << 56;
+
+ this.big8 = ((statByte[4][8] & 0xff))
+ | ((long)(statByte[4][9] & 0xff) << 8)
+ | ((long)(statByte[4][10] & 0xff) << 16)
+ | ((long)(statByte[4][11] & 0xff)) << 24
+ | ((long)(statByte[4][12] & 0xff)) << 32
+ | ((long)(statByte[4][13] & 0xff)) << 40
+ | ((long)(statByte[4][14] & 0xff)) << 48
+ | ((long)(statByte[4][15] & 0xff)) << 56;
+ }
+
+ /**
+ * The constructor generates the PRNG and initializes some variables
+ *
+ * @param digest an array of strings, containing the digest of the used hash
+ * function, the digest of the PRGN and the names of the
+ * corresponding providers
+ * @param w the winternitz parameter
+ * @param height the heigth of the tree
+ */
+ public GMSSRootSig(Digest digest, int w, int height)
+ {
+ messDigestOTS = digest;
+ gmssRandom = new GMSSRandom(messDigestOTS);
+
+ this.mdsize = messDigestOTS.getDigestSize();
+ this.w = w;
+ this.height = height;
+
+ this.k = (1 << w) - 1;
+
+ int mdsizeBit = mdsize << 3;
+ this.messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w);
+ }
+
+ /**
+ * This method initializes the distributed sigature calculation. Variables
+ * are reseted and necessary steps are calculated
+ *
+ * @param seed0 the initial OTSseed
+ * @param message the massage which will be signed
+ */
+ public void initSign(byte[] seed0, byte[] message)
+ {
+
+ // create hash of message m
+ this.hash = new byte[mdsize];
+ messDigestOTS.update(message, 0, message.length);
+ this.hash = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(this.hash, 0);
+
+ // variables for calculation of steps
+ byte[] messPart = new byte[mdsize];
+ System.arraycopy(hash, 0, messPart, 0, mdsize);
+ int checkPart = 0;
+ int sumH = 0;
+ int checksumsize = getLog((messagesize << w) + 1);
+
+ // ------- calculation of necessary steps ------
+ if (8 % w == 0)
+ {
+ int dt = 8 / w;
+ // message part
+ for (int a = 0; a < mdsize; a++)
+ {
+ // count necessary hashs in 'sumH'
+ for (int b = 0; b < dt; b++)
+ {
+ sumH += messPart[a] & k;
+ messPart[a] = (byte)(messPart[a] >>> w);
+ }
+ }
+ // checksum part
+ this.checksum = (messagesize << w) - sumH;
+ checkPart = checksum;
+ // count necessary hashs in 'sumH'
+ for (int b = 0; b < checksumsize; b += w)
+ {
+ sumH += checkPart & k;
+ checkPart >>>= w;
+ }
+ } // end if ( 8 % w == 0 )
+ else if (w < 8)
+ {
+ long big8;
+ int ii = 0;
+ int dt = mdsize / w;
+
+ // first d*w bytes of hash (main message part)
+ for (int i = 0; i < dt; i++)
+ {
+ big8 = 0;
+ for (int j = 0; j < w; j++)
+ {
+ big8 ^= (messPart[ii] & 0xff) << (j << 3);
+ ii++;
+ }
+ // count necessary hashs in 'sumH'
+ for (int j = 0; j < 8; j++)
+ {
+ sumH += (int)(big8 & k);
+ big8 >>>= w;
+ }
+ }
+ // rest of message part
+ dt = mdsize % w;
+ big8 = 0;
+ for (int j = 0; j < dt; j++)
+ {
+ big8 ^= (messPart[ii] & 0xff) << (j << 3);
+ ii++;
+ }
+ dt <<= 3;
+ // count necessary hashs in 'sumH'
+ for (int j = 0; j < dt; j += w)
+ {
+ sumH += (int)(big8 & k);
+ big8 >>>= w;
+ }
+ // checksum part
+ this.checksum = (messagesize << w) - sumH;
+ checkPart = checksum;
+ // count necessary hashs in 'sumH'
+ for (int i = 0; i < checksumsize; i += w)
+ {
+ sumH += checkPart & k;
+ checkPart >>>= w;
+ }
+ }// end if(w<8)
+ else if (w < 57)
+ {
+ long big8;
+ int r = 0;
+ int s, f, rest, ii;
+
+ // first a*w bits of hash where a*w <= 8*mdsize < (a+1)*w (main
+ // message part)
+ while (r <= ((mdsize << 3) - w))
+ {
+ s = r >>> 3;
+ rest = r % 8;
+ r += w;
+ f = (r + 7) >>> 3;
+ big8 = 0;
+ ii = 0;
+ for (int j = s; j < f; j++)
+ {
+ big8 ^= (messPart[j] & 0xff) << (ii << 3);
+ ii++;
+ }
+ big8 >>>= rest;
+ // count necessary hashs in 'sumH'
+ sumH += (big8 & k);
+
+ }
+ // rest of message part
+ s = r >>> 3;
+ if (s < mdsize)
+ {
+ rest = r % 8;
+ big8 = 0;
+ ii = 0;
+ for (int j = s; j < mdsize; j++)
+ {
+ big8 ^= (messPart[j] & 0xff) << (ii << 3);
+ ii++;
+ }
+
+ big8 >>>= rest;
+ // count necessary hashs in 'sumH'
+ sumH += (big8 & k);
+ }
+ // checksum part
+ this.checksum = (messagesize << w) - sumH;
+ checkPart = checksum;
+ // count necessary hashs in 'sumH'
+ for (int i = 0; i < checksumsize; i += w)
+ {
+ sumH += (checkPart & k);
+ checkPart >>>= w;
+ }
+ }// end if(w<57)
+
+ // calculate keysize
+ this.keysize = messagesize
+ + (int)Math.ceil((double)checksumsize / (double)w);
+
+ // calculate steps: 'keysize' times PRNG, 'sumH' times hashing,
+ // (1<<height)-1 updateSign() calls
+ this.steps = (int)Math.ceil((double)(keysize + sumH)
+ / (double)((1 << height)));
+ // ----------------------------
+
+ // reset variables
+ this.sign = new byte[keysize * mdsize];
+ this.counter = 0;
+ this.test = 0;
+ this.ii = 0;
+ this.test8 = 0;
+ this.r = 0;
+ // define the private key messagesize
+ this.privateKeyOTS = new byte[mdsize];
+ // copy the seed
+ this.seed = new byte[mdsize];
+ System.arraycopy(seed0, 0, this.seed, 0, mdsize);
+
+ }
+
+ /**
+ * This Method performs <code>steps</code> steps of distributed signature
+ * calculaion
+ *
+ * @return true if signature is generated completly, else false
+ */
+ public boolean updateSign()
+ {
+ // steps times do
+
+ for (int s = 0; s < steps; s++)
+ { // do 'step' times
+
+ if (counter < keysize)
+ { // generate the private key or perform
+ // the next hash
+ oneStep();
+ }
+ if (counter == keysize)
+ {// finish
+ return true;
+ }
+ }
+
+ return false; // leaf not finished yet
+ }
+
+ /**
+ * @return The private OTS key
+ */
+ public byte[] getSig()
+ {
+
+ return sign;
+ }
+
+ /**
+ * @return The one-time signature of the message, generated step by step
+ */
+ private void oneStep()
+ {
+ // -------- if (8 % w == 0) ----------
+ if (8 % w == 0)
+ {
+ if (test == 0)
+ {
+ // get current OTSprivateKey
+ this.privateKeyOTS = gmssRandom.nextSeed(seed);
+ // System.arraycopy(privateKeyOTS, 0, hlp, 0, mdsize);
+
+ if (ii < mdsize)
+ { // for main message part
+ test = hash[ii] & k;
+ hash[ii] = (byte)(hash[ii] >>> w);
+ }
+ else
+ { // for checksum part
+ test = checksum & k;
+ checksum >>>= w;
+ }
+ }
+ else if (test > 0)
+ { // hash the private Key 'test' times (on
+ // time each step)
+ messDigestOTS.update(privateKeyOTS, 0, privateKeyOTS.length);
+ privateKeyOTS = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(privateKeyOTS, 0);
+ test--;
+ }
+ if (test == 0)
+ { // if all hashes done copy result to siganture
+ // array
+ System.arraycopy(privateKeyOTS, 0, sign, counter * mdsize,
+ mdsize);
+ counter++;
+
+ if (counter % (8 / w) == 0)
+ { // raise array index for main
+ // massage part
+ ii++;
+ }
+ }
+
+ }// ----- end if (8 % w == 0) -----
+ // ---------- if ( w < 8 ) ----------------
+ else if (w < 8)
+ {
+
+ if (test == 0)
+ {
+ if (counter % 8 == 0 && ii < mdsize)
+ { // after every 8th "add
+ // to signature"-step
+ big8 = 0;
+ if (counter < ((mdsize / w) << 3))
+ {// main massage
+ // (generate w*8 Bits
+ // every time) part
+ for (int j = 0; j < w; j++)
+ {
+ big8 ^= (hash[ii] & 0xff) << (j << 3);
+ ii++;
+ }
+ }
+ else
+ { // rest of massage part (once)
+ for (int j = 0; j < mdsize % w; j++)
+ {
+ big8 ^= (hash[ii] & 0xff) << (j << 3);
+ ii++;
+ }
+ }
+ }
+ if (counter == messagesize)
+ { // checksum part (once)
+ big8 = checksum;
+ }
+
+ test = (int)(big8 & k);
+ // generate current OTSprivateKey
+ this.privateKeyOTS = gmssRandom.nextSeed(seed);
+ // System.arraycopy(privateKeyOTS, 0, hlp, 0, mdsize);
+
+ }
+ else if (test > 0)
+ { // hash the private Key 'test' times (on
+ // time each step)
+ messDigestOTS.update(privateKeyOTS, 0, privateKeyOTS.length);
+ privateKeyOTS = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(privateKeyOTS, 0);
+ test--;
+ }
+ if (test == 0)
+ { // if all hashes done copy result to siganture
+ // array
+ System.arraycopy(privateKeyOTS, 0, sign, counter * mdsize,
+ mdsize);
+ big8 >>>= w;
+ counter++;
+ }
+
+ }// ------- end if(w<8)--------------------------------
+ // --------- if w < 57 -----------------------------
+ else if (w < 57)
+ {
+
+ if (test8 == 0)
+ {
+ int s, f, rest;
+ big8 = 0;
+ ii = 0;
+ rest = r % 8;
+ s = r >>> 3;
+ // --- message part---
+ if (s < mdsize)
+ {
+ if (r <= ((mdsize << 3) - w))
+ { // first message part
+ r += w;
+ f = (r + 7) >>> 3;
+ }
+ else
+ { // rest of message part (once)
+ f = mdsize;
+ r += w;
+ }
+ // generate long 'big8' with minimum w next bits of the
+ // message array
+ for (int i = s; i < f; i++)
+ {
+ big8 ^= (hash[i] & 0xff) << (ii << 3);
+ ii++;
+ }
+ // delete bits on the right side, which were used already by
+ // the last loop
+ big8 >>>= rest;
+ test8 = (big8 & k);
+ }
+ // --- checksum part
+ else
+ {
+ test8 = (checksum & k);
+ checksum >>>= w;
+ }
+ // generate current OTSprivateKey
+ this.privateKeyOTS = gmssRandom.nextSeed(seed);
+ // System.arraycopy(privateKeyOTS, 0, hlp, 0, mdsize);
+
+ }
+ else if (test8 > 0)
+ { // hash the private Key 'test' times (on
+ // time each step)
+ messDigestOTS.update(privateKeyOTS, 0, privateKeyOTS.length);
+ privateKeyOTS = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(privateKeyOTS, 0);
+ test8--;
+ }
+ if (test8 == 0)
+ { // if all hashes done copy result to siganture
+ // array
+ System.arraycopy(privateKeyOTS, 0, sign, counter * mdsize,
+ mdsize);
+ counter++;
+ }
+
+ }
+ }
+
+ /**
+ * This method returns the least integer that is greater or equal to the
+ * logarithm to the base 2 of an integer <code>intValue</code>.
+ *
+ * @param intValue an integer
+ * @return The least integer greater or equal to the logarithm to the base 2
+ * of <code>intValue</code>
+ */
+ public int getLog(int intValue)
+ {
+ int log = 1;
+ int i = 2;
+ while (i < intValue)
+ {
+ i <<= 1;
+ log++;
+ }
+ return log;
+ }
+
+ /**
+ * This method returns the status byte array
+ *
+ * @return statBytes
+ */
+ public byte[][] getStatByte()
+ {
+
+ byte[][] statByte = new byte[5][mdsize];
+ statByte[0] = privateKeyOTS;
+ statByte[1] = seed;
+ statByte[2] = hash;
+ statByte[3] = sign;
+ statByte[4] = this.getStatLong();
+
+ return statByte;
+ }
+
+ /**
+ * This method returns the status int array
+ *
+ * @return statInt
+ */
+ public int[] getStatInt()
+ {
+ int[] statInt = new int[9];
+ statInt[0] = counter;
+ statInt[1] = test;
+ statInt[2] = ii;
+ statInt[3] = r;
+ statInt[4] = steps;
+ statInt[5] = keysize;
+ statInt[6] = height;
+ statInt[7] = w;
+ statInt[8] = checksum;
+ return statInt;
+ }
+
+ /**
+ * Converts the long parameters into byte arrays to store it in
+ * statByte-Array
+ */
+ public byte[] getStatLong()
+ {
+ byte[] bytes = new byte[16];
+
+ bytes[0] = (byte)((test8) & 0xff);
+ bytes[1] = (byte)((test8 >> 8) & 0xff);
+ bytes[2] = (byte)((test8 >> 16) & 0xff);
+ bytes[3] = (byte)((test8 >> 24) & 0xff);
+ bytes[4] = (byte)((test8) >> 32 & 0xff);
+ bytes[5] = (byte)((test8 >> 40) & 0xff);
+ bytes[6] = (byte)((test8 >> 48) & 0xff);
+ bytes[7] = (byte)((test8 >> 56) & 0xff);
+
+ bytes[8] = (byte)((big8) & 0xff);
+ bytes[9] = (byte)((big8 >> 8) & 0xff);
+ bytes[10] = (byte)((big8 >> 16) & 0xff);
+ bytes[11] = (byte)((big8 >> 24) & 0xff);
+ bytes[12] = (byte)((big8) >> 32 & 0xff);
+ bytes[13] = (byte)((big8 >> 40) & 0xff);
+ bytes[14] = (byte)((big8 >> 48) & 0xff);
+ bytes[15] = (byte)((big8 >> 56) & 0xff);
+
+ return bytes;
+ }
+
+ /**
+ * returns a string representation of the instance
+ *
+ * @return a string representation of the instance
+ */
+ public String toString()
+ {
+ String out = "" + this.big8 + " ";
+ int[] statInt = new int[9];
+ statInt = this.getStatInt();
+ byte[][] statByte = new byte[5][mdsize];
+ statByte = this.getStatByte();
+ for (int i = 0; i < 9; i++)
+ {
+ out = out + statInt[i] + " ";
+ }
+ for (int i = 0; i < 5; i++)
+ {
+ out = out + new String(Hex.encode(statByte[i])) + " ";
+ }
+
+ return out;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSSigner.java
new file mode 100644
index 00000000..7cedf120
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSSigner.java
@@ -0,0 +1,404 @@
+package org.bouncycastle.pqc.crypto.gmss;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.pqc.crypto.MessageSigner;
+import org.bouncycastle.pqc.crypto.gmss.util.GMSSRandom;
+import org.bouncycastle.pqc.crypto.gmss.util.GMSSUtil;
+import org.bouncycastle.pqc.crypto.gmss.util.WinternitzOTSVerify;
+import org.bouncycastle.pqc.crypto.gmss.util.WinternitzOTSignature;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * This class implements the GMSS signature scheme.
+ */
+public class GMSSSigner
+ implements MessageSigner
+{
+
+ /**
+ * Instance of GMSSParameterSpec
+ */
+ //private GMSSParameterSpec gmssParameterSpec;
+
+ /**
+ * Instance of GMSSUtilities
+ */
+ private GMSSUtil gmssUtil = new GMSSUtil();
+
+
+ /**
+ * The raw GMSS public key
+ */
+ private byte[] pubKeyBytes;
+
+ /**
+ * Hash function for the construction of the authentication trees
+ */
+ private Digest messDigestTrees;
+
+ /**
+ * The length of the hash function output
+ */
+ private int mdLength;
+
+ /**
+ * The number of tree layers
+ */
+ private int numLayer;
+
+ /**
+ * The hash function used by the OTS
+ */
+ private Digest messDigestOTS;
+
+ /**
+ * An instance of the Winternitz one-time signature
+ */
+ private WinternitzOTSignature ots;
+
+ /**
+ * Array of strings containing the name of the hash function used by the OTS
+ * and the corresponding provider name
+ */
+ private GMSSDigestProvider digestProvider;
+
+ /**
+ * The current main tree and subtree indices
+ */
+ private int[] index;
+
+ /**
+ * Array of the authentication paths for the current trees of all layers
+ */
+ private byte[][][] currentAuthPaths;
+
+ /**
+ * The one-time signature of the roots of the current subtrees
+ */
+ private byte[][] subtreeRootSig;
+
+
+ /**
+ * The GMSSParameterset
+ */
+ private GMSSParameters gmssPS;
+
+ /**
+ * The PRNG
+ */
+ private GMSSRandom gmssRandom;
+
+ GMSSKeyParameters key;
+
+ // XXX needed? Source of randomness
+ private SecureRandom random;
+
+
+ /**
+ * The standard constructor tries to generate the MerkleTree Algorithm
+ * identifier with the corresponding OID.
+ *
+ * @param digest the digest to use
+ */
+ // TODO
+ public GMSSSigner(GMSSDigestProvider digest)
+ {
+ digestProvider = digest;
+ messDigestTrees = digest.get();
+ messDigestOTS = messDigestTrees;
+ mdLength = messDigestTrees.getDigestSize();
+ gmssRandom = new GMSSRandom(messDigestTrees);
+ }
+
+ public void init(boolean forSigning,
+ CipherParameters param)
+ {
+
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ // XXX random needed?
+ this.random = rParam.getRandom();
+ this.key = (GMSSPrivateKeyParameters)rParam.getParameters();
+ initSign();
+
+ }
+ else
+ {
+
+ this.random = new SecureRandom();
+ this.key = (GMSSPrivateKeyParameters)param;
+ initSign();
+ }
+ }
+ else
+ {
+ this.key = (GMSSPublicKeyParameters)param;
+ initVerify();
+
+ }
+
+ }
+
+
+ /**
+ * Initializes the signature algorithm for signing a message.
+ */
+ private void initSign()
+ {
+ messDigestTrees.reset();
+ // set private key and take from it ots key, auth, tree and key
+ // counter, rootSign
+ GMSSPrivateKeyParameters gmssPrivateKey = (GMSSPrivateKeyParameters)key;
+
+ if (gmssPrivateKey.isUsed())
+ {
+ throw new IllegalStateException("Private key already used");
+ }
+
+ // check if last signature has been generated
+ if (gmssPrivateKey.getIndex(0) >= gmssPrivateKey.getNumLeafs(0))
+ {
+ throw new IllegalStateException("No more signatures can be generated");
+ }
+
+ // get Parameterset
+ this.gmssPS = gmssPrivateKey.getParameters();
+ // get numLayer
+ this.numLayer = gmssPS.getNumOfLayers();
+
+ // get OTS Instance of lowest layer
+ byte[] seed = gmssPrivateKey.getCurrentSeeds()[numLayer - 1];
+ byte[] OTSSeed = new byte[mdLength];
+ byte[] dummy = new byte[mdLength];
+ System.arraycopy(seed, 0, dummy, 0, mdLength);
+ OTSSeed = gmssRandom.nextSeed(dummy); // secureRandom.nextBytes(currentSeeds[currentSeeds.length-1]);secureRandom.nextBytes(OTSseed);
+ this.ots = new WinternitzOTSignature(OTSSeed, digestProvider.get(), gmssPS.getWinternitzParameter()[numLayer - 1]);
+
+ byte[][][] helpCurrentAuthPaths = gmssPrivateKey.getCurrentAuthPaths();
+ currentAuthPaths = new byte[numLayer][][];
+
+ // copy the main tree authentication path
+ for (int j = 0; j < numLayer; j++)
+ {
+ currentAuthPaths[j] = new byte[helpCurrentAuthPaths[j].length][mdLength];
+ for (int i = 0; i < helpCurrentAuthPaths[j].length; i++)
+ {
+ System.arraycopy(helpCurrentAuthPaths[j][i], 0, currentAuthPaths[j][i], 0, mdLength);
+ }
+ }
+
+ // copy index
+ index = new int[numLayer];
+ System.arraycopy(gmssPrivateKey.getIndex(), 0, index, 0, numLayer);
+
+ // copy subtreeRootSig
+ byte[] helpSubtreeRootSig;
+ subtreeRootSig = new byte[numLayer - 1][];
+ for (int i = 0; i < numLayer - 1; i++)
+ {
+ helpSubtreeRootSig = gmssPrivateKey.getSubtreeRootSig(i);
+ subtreeRootSig[i] = new byte[helpSubtreeRootSig.length];
+ System.arraycopy(helpSubtreeRootSig, 0, subtreeRootSig[i], 0, helpSubtreeRootSig.length);
+ }
+
+ gmssPrivateKey.markUsed();
+ }
+
+ /**
+ * Signs a message.
+ * <p/>
+ *
+ * @return the signature.
+ */
+ public byte[] generateSignature(byte[] message)
+ {
+
+ byte[] otsSig = new byte[mdLength];
+ byte[] authPathBytes;
+ byte[] indexBytes;
+
+ otsSig = ots.getSignature(message);
+
+ // get concatenated lowest layer tree authentication path
+ authPathBytes = gmssUtil.concatenateArray(currentAuthPaths[numLayer - 1]);
+
+ // put lowest layer index into a byte array
+ indexBytes = gmssUtil.intToBytesLittleEndian(index[numLayer - 1]);
+
+ // create first part of GMSS signature
+ byte[] gmssSigFirstPart = new byte[indexBytes.length + otsSig.length + authPathBytes.length];
+ System.arraycopy(indexBytes, 0, gmssSigFirstPart, 0, indexBytes.length);
+ System.arraycopy(otsSig, 0, gmssSigFirstPart, indexBytes.length, otsSig.length);
+ System.arraycopy(authPathBytes, 0, gmssSigFirstPart, (indexBytes.length + otsSig.length), authPathBytes.length);
+ // --- end first part
+
+ // --- next parts of the signature
+ // create initial array with length 0 for iteration
+ byte[] gmssSigNextPart = new byte[0];
+
+ for (int i = numLayer - 1 - 1; i >= 0; i--)
+ {
+
+ // get concatenated next tree authentication path
+ authPathBytes = gmssUtil.concatenateArray(currentAuthPaths[i]);
+
+ // put next tree index into a byte array
+ indexBytes = gmssUtil.intToBytesLittleEndian(index[i]);
+
+ // create next part of GMSS signature
+
+ // create help array and copy actual gmssSig into it
+ byte[] helpGmssSig = new byte[gmssSigNextPart.length];
+ System.arraycopy(gmssSigNextPart, 0, helpGmssSig, 0, gmssSigNextPart.length);
+ // adjust length of gmssSigNextPart for adding next part
+ gmssSigNextPart = new byte[helpGmssSig.length + indexBytes.length + subtreeRootSig[i].length + authPathBytes.length];
+
+ // copy old data (help array) and new data in gmssSigNextPart
+ System.arraycopy(helpGmssSig, 0, gmssSigNextPart, 0, helpGmssSig.length);
+ System.arraycopy(indexBytes, 0, gmssSigNextPart, helpGmssSig.length, indexBytes.length);
+ System.arraycopy(subtreeRootSig[i], 0, gmssSigNextPart, (helpGmssSig.length + indexBytes.length), subtreeRootSig[i].length);
+ System.arraycopy(authPathBytes, 0, gmssSigNextPart, (helpGmssSig.length + indexBytes.length + subtreeRootSig[i].length), authPathBytes.length);
+
+ }
+ // --- end next parts
+
+ // concatenate the two parts of the GMSS signature
+ byte[] gmssSig = new byte[gmssSigFirstPart.length + gmssSigNextPart.length];
+ System.arraycopy(gmssSigFirstPart, 0, gmssSig, 0, gmssSigFirstPart.length);
+ System.arraycopy(gmssSigNextPart, 0, gmssSig, gmssSigFirstPart.length, gmssSigNextPart.length);
+
+ // return the GMSS signature
+ return gmssSig;
+ }
+
+ /**
+ * Initializes the signature algorithm for verifying a signature.
+ */
+ private void initVerify()
+ {
+ messDigestTrees.reset();
+
+ GMSSPublicKeyParameters gmssPublicKey = (GMSSPublicKeyParameters)key;
+ pubKeyBytes = gmssPublicKey.getPublicKey();
+ gmssPS = gmssPublicKey.getParameters();
+ // get numLayer
+ this.numLayer = gmssPS.getNumOfLayers();
+
+
+ }
+
+ /**
+ * This function verifies the signature of the message that has been
+ * updated, with the aid of the public key.
+ *
+ * @param message the message
+ * @param signature the signature associated with the message
+ * @return true if the signature has been verified, false otherwise.
+ */
+ public boolean verifySignature(byte[] message, byte[] signature)
+ {
+
+ boolean success = false;
+ // int halfSigLength = signature.length >>> 1;
+ messDigestOTS.reset();
+ WinternitzOTSVerify otsVerify;
+ int otsSigLength;
+
+ byte[] help = message;
+
+ byte[] otsSig;
+ byte[] otsPublicKey;
+ byte[][] authPath;
+ byte[] dest;
+ int nextEntry = 0;
+ int index;
+ // Verify signature
+
+ // --- begin with message = 'message that was signed'
+ // and then in each step message = subtree root
+ for (int j = numLayer - 1; j >= 0; j--)
+ {
+ otsVerify = new WinternitzOTSVerify(digestProvider.get(), gmssPS.getWinternitzParameter()[j]);
+ otsSigLength = otsVerify.getSignatureLength();
+
+ message = help;
+ // get the subtree index
+ index = gmssUtil.bytesToIntLittleEndian(signature, nextEntry);
+
+ // 4 is the number of bytes in integer
+ nextEntry += 4;
+
+ // get one-time signature
+ otsSig = new byte[otsSigLength];
+ System.arraycopy(signature, nextEntry, otsSig, 0, otsSigLength);
+ nextEntry += otsSigLength;
+
+ // compute public OTS key from the one-time signature
+ otsPublicKey = otsVerify.Verify(message, otsSig);
+
+ // test if OTSsignature is correct
+ if (otsPublicKey == null)
+ {
+ System.err.println("OTS Public Key is null in GMSSSignature.verify");
+ return false;
+ }
+
+ // get authentication path from the signature
+ authPath = new byte[gmssPS.getHeightOfTrees()[j]][mdLength];
+ for (int i = 0; i < authPath.length; i++)
+ {
+ System.arraycopy(signature, nextEntry, authPath[i], 0, mdLength);
+ nextEntry = nextEntry + mdLength;
+ }
+
+ // compute the root of the subtree from the authentication path
+ help = new byte[mdLength];
+
+ help = otsPublicKey;
+
+ int count = 1 << authPath.length;
+ count = count + index;
+
+ for (int i = 0; i < authPath.length; i++)
+ {
+ dest = new byte[mdLength << 1];
+
+ if ((count % 2) == 0)
+ {
+ System.arraycopy(help, 0, dest, 0, mdLength);
+ System.arraycopy(authPath[i], 0, dest, mdLength, mdLength);
+ count = count / 2;
+ }
+ else
+ {
+ System.arraycopy(authPath[i], 0, dest, 0, mdLength);
+ System.arraycopy(help, 0, dest, mdLength, help.length);
+ count = (count - 1) / 2;
+ }
+ messDigestTrees.update(dest, 0, dest.length);
+ help = new byte[messDigestTrees.getDigestSize()];
+ messDigestTrees.doFinal(help, 0);
+ }
+ }
+
+ // now help contains the root of the maintree
+
+ // test if help is equal to the GMSS public key
+ if (Arrays.areEqual(pubKeyBytes, help))
+ {
+ success = true;
+ }
+
+ return success;
+ }
+
+
+} \ No newline at end of file
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSUtils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSUtils.java
new file mode 100644
index 00000000..9d289513
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSUtils.java
@@ -0,0 +1,145 @@
+package org.bouncycastle.pqc.crypto.gmss;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.bouncycastle.util.Arrays;
+
+class GMSSUtils
+{
+ static GMSSLeaf[] clone(GMSSLeaf[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ GMSSLeaf[] copy = new GMSSLeaf[data.length];
+
+ System.arraycopy(data, 0, copy, 0, data.length);
+
+ return copy;
+ }
+
+ static GMSSRootCalc[] clone(GMSSRootCalc[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ GMSSRootCalc[] copy = new GMSSRootCalc[data.length];
+
+ System.arraycopy(data, 0, copy, 0, data.length);
+
+ return copy;
+ }
+
+ static GMSSRootSig[] clone(GMSSRootSig[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ GMSSRootSig[] copy = new GMSSRootSig[data.length];
+
+ System.arraycopy(data, 0, copy, 0, data.length);
+
+ return copy;
+ }
+
+ static byte[][] clone(byte[][] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ byte[][] copy = new byte[data.length][];
+
+ for (int i = 0; i != data.length; i++)
+ {
+ copy[i] = Arrays.clone(data[i]);
+ }
+
+ return copy;
+ }
+
+ static byte[][][] clone(byte[][][] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ byte[][][] copy = new byte[data.length][][];
+
+ for (int i = 0; i != data.length; i++)
+ {
+ copy[i] = clone(data[i]);
+ }
+
+ return copy;
+ }
+
+ static Treehash[] clone(Treehash[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ Treehash[] copy = new Treehash[data.length];
+
+ System.arraycopy(data, 0, copy, 0, data.length);
+
+ return copy;
+ }
+
+ static Treehash[][] clone(Treehash[][] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ Treehash[][] copy = new Treehash[data.length][];
+
+ for (int i = 0; i != data.length; i++)
+ {
+ copy[i] = clone(data[i]);
+ }
+
+ return copy;
+ }
+
+ static Vector[] clone(Vector[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ Vector[] copy = new Vector[data.length];
+
+ for (int i = 0; i != data.length; i++)
+ {
+ copy[i] = new Vector();
+ for (Enumeration en = data[i].elements(); en.hasMoreElements();)
+ {
+ copy[i].addElement(en.nextElement());
+ }
+ }
+
+ return copy;
+ }
+
+ static Vector[][] clone(Vector[][] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ Vector[][] copy = new Vector[data.length][];
+
+ for (int i = 0; i != data.length; i++)
+ {
+ copy[i] = clone(data[i]);
+ }
+
+ return copy;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/Treehash.java b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/Treehash.java
new file mode 100644
index 00000000..797355cb
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/Treehash.java
@@ -0,0 +1,525 @@
+package org.bouncycastle.pqc.crypto.gmss;
+
+import java.util.Vector;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.pqc.crypto.gmss.util.GMSSRandom;
+import org.bouncycastle.util.Integers;
+import org.bouncycastle.util.encoders.Hex;
+
+
+/**
+ * This class implements a treehash instance for the Merkle tree traversal
+ * algorithm. The first node of the stack is stored in this instance itself,
+ * additional tail nodes are stored on a tailstack.
+ */
+public class Treehash
+{
+
+ /**
+ * max height of current treehash instance.
+ */
+ private int maxHeight;
+
+ /**
+ * Vector element that stores the nodes on the stack
+ */
+ private Vector tailStack;
+
+ /**
+ * Vector element that stores the height of the nodes on the stack
+ */
+ private Vector heightOfNodes;
+
+ /**
+ * the first node is stored in the treehash instance itself, not on stack
+ */
+ private byte[] firstNode;
+
+ /**
+ * seedActive needed for the actual node
+ */
+ private byte[] seedActive;
+
+ /**
+ * the seed needed for the next re-initialization of the treehash instance
+ */
+ private byte[] seedNext;
+
+ /**
+ * number of nodes stored on the stack and belonging to this treehash
+ * instance
+ */
+ private int tailLength;
+
+ /**
+ * the height in the tree of the first node stored in treehash
+ */
+ private int firstNodeHeight;
+
+ /**
+ * true if treehash instance was already initialized, false otherwise
+ */
+ private boolean isInitialized;
+
+ /**
+ * true if the first node's height equals the maxHeight of the treehash
+ */
+ private boolean isFinished;
+
+ /**
+ * true if the nextSeed has been initialized with index 3*2^h needed for the
+ * seed scheduling
+ */
+ private boolean seedInitialized;
+
+ /**
+ * denotes the Message Digest used by the tree to create nodes
+ */
+ private Digest messDigestTree;
+
+ /**
+ * This constructor regenerates a prior treehash object
+ *
+ * @param name an array of strings, containing the name of the used hash
+ * function and PRNG and the name of the corresponding provider
+ * @param statByte status bytes
+ * @param statInt status ints
+ */
+ public Treehash(Digest name, byte[][] statByte, int[] statInt)
+ {
+ this.messDigestTree = name;
+
+ // decode statInt
+ this.maxHeight = statInt[0];
+ this.tailLength = statInt[1];
+ this.firstNodeHeight = statInt[2];
+
+ if (statInt[3] == 1)
+ {
+ this.isFinished = true;
+ }
+ else
+ {
+ this.isFinished = false;
+ }
+ if (statInt[4] == 1)
+ {
+ this.isInitialized = true;
+ }
+ else
+ {
+ this.isInitialized = false;
+ }
+ if (statInt[5] == 1)
+ {
+ this.seedInitialized = true;
+ }
+ else
+ {
+ this.seedInitialized = false;
+ }
+
+ this.heightOfNodes = new Vector();
+ for (int i = 0; i < tailLength; i++)
+ {
+ this.heightOfNodes.addElement(Integers.valueOf(statInt[6 + i]));
+ }
+
+ // decode statByte
+ this.firstNode = statByte[0];
+ this.seedActive = statByte[1];
+ this.seedNext = statByte[2];
+
+ this.tailStack = new Vector();
+ for (int i = 0; i < tailLength; i++)
+ {
+ this.tailStack.addElement(statByte[3 + i]);
+ }
+ }
+
+ /**
+ * Constructor
+ *
+ * @param tailStack a vector element where the stack nodes are stored
+ * @param maxHeight maximal height of the treehash instance
+ * @param digest an array of strings, containing the name of the used hash
+ * function and PRNG and the name of the corresponding provider
+ */
+ public Treehash(Vector tailStack, int maxHeight, Digest digest)
+ {
+ this.tailStack = tailStack;
+ this.maxHeight = maxHeight;
+ this.firstNode = null;
+ this.isInitialized = false;
+ this.isFinished = false;
+ this.seedInitialized = false;
+ this.messDigestTree = digest;
+
+ this.seedNext = new byte[messDigestTree.getDigestSize()];
+ this.seedActive = new byte[messDigestTree.getDigestSize()];
+ }
+
+ /**
+ * Method to initialize the seeds needed for the precomputation of right
+ * nodes. Should be initialized with index 3*2^i for treehash_i
+ *
+ * @param seedIn
+ */
+ public void initializeSeed(byte[] seedIn)
+ {
+ System.arraycopy(seedIn, 0, this.seedNext, 0, this.messDigestTree
+ .getDigestSize());
+ this.seedInitialized = true;
+ }
+
+ /**
+ * initializes the treehash instance. The seeds must already have been
+ * initialized to work correctly.
+ */
+ public void initialize()
+ {
+ if (!this.seedInitialized)
+ {
+ System.err.println("Seed " + this.maxHeight + " not initialized");
+ return;
+ }
+
+ this.heightOfNodes = new Vector();
+ this.tailLength = 0;
+ this.firstNode = null;
+ this.firstNodeHeight = -1;
+ this.isInitialized = true;
+ System.arraycopy(this.seedNext, 0, this.seedActive, 0, messDigestTree
+ .getDigestSize());
+ }
+
+ /**
+ * Calculates one update of the treehash instance, i.e. creates a new leaf
+ * and hashes if possible
+ *
+ * @param gmssRandom an instance of the PRNG
+ * @param leaf The byte value of the leaf needed for the update
+ */
+ public void update(GMSSRandom gmssRandom, byte[] leaf)
+ {
+
+ if (this.isFinished)
+ {
+ System.err
+ .println("No more update possible for treehash instance!");
+ return;
+ }
+ if (!this.isInitialized)
+ {
+ System.err
+ .println("Treehash instance not initialized before update");
+ return;
+ }
+
+ byte[] help = new byte[this.messDigestTree.getDigestSize()];
+ int helpHeight = -1;
+
+ gmssRandom.nextSeed(this.seedActive);
+
+ // if treehash gets first update
+ if (this.firstNode == null)
+ {
+ this.firstNode = leaf;
+ this.firstNodeHeight = 0;
+ }
+ else
+ {
+ // store the new node in help array, do not push it on the stack
+ help = leaf;
+ helpHeight = 0;
+
+ // hash the nodes on the stack if possible
+ while (this.tailLength > 0
+ && helpHeight == ((Integer)heightOfNodes.lastElement())
+ .intValue())
+ {
+ // put top element of the stack and help node in array
+ // 'tobehashed'
+ // and hash them together, put result again in help array
+ byte[] toBeHashed = new byte[this.messDigestTree
+ .getDigestSize() << 1];
+
+ // pop element from stack
+ System.arraycopy(this.tailStack.lastElement(), 0, toBeHashed,
+ 0, this.messDigestTree.getDigestSize());
+ this.tailStack.removeElementAt(this.tailStack.size() - 1);
+ this.heightOfNodes
+ .removeElementAt(this.heightOfNodes.size() - 1);
+
+ System.arraycopy(help, 0, toBeHashed, this.messDigestTree
+ .getDigestSize(), this.messDigestTree
+ .getDigestSize());
+ messDigestTree.update(toBeHashed, 0, toBeHashed.length);
+ help = new byte[messDigestTree.getDigestSize()];
+ messDigestTree.doFinal(help, 0);
+
+ // increase help height, stack was reduced by one element
+ helpHeight++;
+ this.tailLength--;
+ }
+
+ // push the new node on the stack
+ this.tailStack.addElement(help);
+ this.heightOfNodes.addElement(Integers.valueOf(helpHeight));
+ this.tailLength++;
+
+ // finally check whether the top node on stack and the first node
+ // in treehash have same height. If so hash them together
+ // and store them in treehash
+ if (((Integer)heightOfNodes.lastElement()).intValue() == this.firstNodeHeight)
+ {
+ byte[] toBeHashed = new byte[this.messDigestTree
+ .getDigestSize() << 1];
+ System.arraycopy(this.firstNode, 0, toBeHashed, 0,
+ this.messDigestTree.getDigestSize());
+
+ // pop element from tailStack and copy it into help2 array
+ System.arraycopy(this.tailStack.lastElement(), 0, toBeHashed,
+ this.messDigestTree.getDigestSize(),
+ this.messDigestTree.getDigestSize());
+ this.tailStack.removeElementAt(this.tailStack.size() - 1);
+ this.heightOfNodes
+ .removeElementAt(this.heightOfNodes.size() - 1);
+
+ // store new element in firstNode, stack is then empty
+ messDigestTree.update(toBeHashed, 0, toBeHashed.length);
+ this.firstNode = new byte[messDigestTree.getDigestSize()];
+ messDigestTree.doFinal(this.firstNode, 0);
+ this.firstNodeHeight++;
+
+ // empty the stack
+ this.tailLength = 0;
+ }
+ }
+
+ // check if treehash instance is completed
+ if (this.firstNodeHeight == this.maxHeight)
+ {
+ this.isFinished = true;
+ }
+ }
+
+ /**
+ * Destroys a treehash instance after the top node was taken for
+ * authentication path.
+ */
+ public void destroy()
+ {
+ this.isInitialized = false;
+ this.isFinished = false;
+ this.firstNode = null;
+ this.tailLength = 0;
+ this.firstNodeHeight = -1;
+ }
+
+ /**
+ * Returns the height of the lowest node stored either in treehash or on the
+ * stack. It must not be set to infinity (as mentioned in the paper) because
+ * this cases are considered in the computeAuthPaths method of
+ * JDKGMSSPrivateKey
+ *
+ * @return Height of the lowest node
+ */
+ public int getLowestNodeHeight()
+ {
+ if (this.firstNode == null)
+ {
+ return this.maxHeight;
+ }
+ else if (this.tailLength == 0)
+ {
+ return this.firstNodeHeight;
+ }
+ else
+ {
+ return Math.min(this.firstNodeHeight, ((Integer)heightOfNodes
+ .lastElement()).intValue());
+ }
+ }
+
+ /**
+ * Returns the top node height
+ *
+ * @return Height of the first node, the top node
+ */
+ public int getFirstNodeHeight()
+ {
+ if (firstNode == null)
+ {
+ return maxHeight;
+ }
+ return firstNodeHeight;
+ }
+
+ /**
+ * Method to check whether the instance has been initialized or not
+ *
+ * @return true if treehash was already initialized
+ */
+ public boolean wasInitialized()
+ {
+ return this.isInitialized;
+ }
+
+ /**
+ * Method to check whether the instance has been finished or not
+ *
+ * @return true if treehash has reached its maximum height
+ */
+ public boolean wasFinished()
+ {
+ return this.isFinished;
+ }
+
+ /**
+ * returns the first node stored in treehash instance itself
+ *
+ * @return the first node stored in treehash instance itself
+ */
+ public byte[] getFirstNode()
+ {
+ return this.firstNode;
+ }
+
+ /**
+ * returns the active seed
+ *
+ * @return the active seed
+ */
+ public byte[] getSeedActive()
+ {
+ return this.seedActive;
+ }
+
+ /**
+ * This method sets the first node stored in the treehash instance itself
+ *
+ * @param hash
+ */
+ public void setFirstNode(byte[] hash)
+ {
+ if (!this.isInitialized)
+ {
+ this.initialize();
+ }
+ this.firstNode = hash;
+ this.firstNodeHeight = this.maxHeight;
+ this.isFinished = true;
+ }
+
+ /**
+ * updates the nextSeed of this treehash instance one step needed for the
+ * schedulng of the seeds
+ *
+ * @param gmssRandom the prng used for the seeds
+ */
+ public void updateNextSeed(GMSSRandom gmssRandom)
+ {
+ gmssRandom.nextSeed(seedNext);
+ }
+
+ /**
+ * Returns the tailstack
+ *
+ * @return the tailstack
+ */
+ public Vector getTailStack()
+ {
+ return this.tailStack;
+ }
+
+ /**
+ * Returns the status byte array used by the GMSSPrivateKeyASN.1 class
+ *
+ * @return The status bytes
+ */
+ public byte[][] getStatByte()
+ {
+
+ byte[][] statByte = new byte[3 + tailLength][this.messDigestTree
+ .getDigestSize()];
+ statByte[0] = firstNode;
+ statByte[1] = seedActive;
+ statByte[2] = seedNext;
+ for (int i = 0; i < tailLength; i++)
+ {
+ statByte[3 + i] = (byte[])tailStack.elementAt(i);
+ }
+ return statByte;
+ }
+
+ /**
+ * Returns the status int array used by the GMSSPrivateKeyASN.1 class
+ *
+ * @return The status ints
+ */
+ public int[] getStatInt()
+ {
+
+ int[] statInt = new int[6 + tailLength];
+ statInt[0] = maxHeight;
+ statInt[1] = tailLength;
+ statInt[2] = firstNodeHeight;
+ if (this.isFinished)
+ {
+ statInt[3] = 1;
+ }
+ else
+ {
+ statInt[3] = 0;
+ }
+ if (this.isInitialized)
+ {
+ statInt[4] = 1;
+ }
+ else
+ {
+ statInt[4] = 0;
+ }
+ if (this.seedInitialized)
+ {
+ statInt[5] = 1;
+ }
+ else
+ {
+ statInt[5] = 0;
+ }
+ for (int i = 0; i < tailLength; i++)
+ {
+ statInt[6 + i] = ((Integer)heightOfNodes.elementAt(i)).intValue();
+ }
+ return statInt;
+ }
+
+ /**
+ * returns a String representation of the treehash instance
+ */
+ public String toString()
+ {
+ String out = "Treehash : ";
+ for (int i = 0; i < 6 + tailLength; i++)
+ {
+ out = out + this.getStatInt()[i] + " ";
+ }
+ for (int i = 0; i < 3 + tailLength; i++)
+ {
+ if (this.getStatByte()[i] != null)
+ {
+ out = out + new String(Hex.encode((this.getStatByte()[i]))) + " ";
+ }
+ else
+ {
+ out = out + "null ";
+ }
+ }
+ out = out + " " + this.messDigestTree.getDigestSize();
+ return out;
+ }
+
+} \ No newline at end of file
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/util/GMSSRandom.java b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/util/GMSSRandom.java
new file mode 100644
index 00000000..c6d30227
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/util/GMSSRandom.java
@@ -0,0 +1,78 @@
+package org.bouncycastle.pqc.crypto.gmss.util;
+
+import org.bouncycastle.crypto.Digest;
+
+/**
+ * This class provides a PRNG for GMSS
+ */
+public class GMSSRandom
+{
+ /**
+ * Hash function for the construction of the authentication trees
+ */
+ private Digest messDigestTree;
+
+ /**
+ * Constructor
+ *
+ * @param messDigestTree2
+ */
+ public GMSSRandom(Digest messDigestTree2)
+ {
+
+ this.messDigestTree = messDigestTree2;
+ }
+
+ /**
+ * computes the next seed value, returns a random byte array and sets
+ * outseed to the next value
+ *
+ * @param outseed byte array in which ((1 + SEEDin +RAND) mod 2^n) will be
+ * stored
+ * @return byte array of H(SEEDin)
+ */
+ public byte[] nextSeed(byte[] outseed)
+ {
+ // RAND <-- H(SEEDin)
+ byte[] rand = new byte[outseed.length];
+ messDigestTree.update(outseed, 0, outseed.length);
+ rand = new byte[messDigestTree.getDigestSize()];
+ messDigestTree.doFinal(rand, 0);
+
+ // SEEDout <-- (1 + SEEDin +RAND) mod 2^n
+ addByteArrays(outseed, rand);
+ addOne(outseed);
+
+ // System.arraycopy(outseed, 0, outseed, 0, outseed.length);
+
+ return rand;
+ }
+
+ private void addByteArrays(byte[] a, byte[] b)
+ {
+
+ byte overflow = 0;
+ int temp;
+
+ for (int i = 0; i < a.length; i++)
+ {
+ temp = (0xFF & a[i]) + (0xFF & b[i]) + overflow;
+ a[i] = (byte)temp;
+ overflow = (byte)(temp >> 8);
+ }
+ }
+
+ private void addOne(byte[] a)
+ {
+
+ byte overflow = 1;
+ int temp;
+
+ for (int i = 0; i < a.length; i++)
+ {
+ temp = (0xFF & a[i]) + overflow;
+ a[i] = (byte)temp;
+ overflow = (byte)(temp >> 8);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/util/GMSSUtil.java b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/util/GMSSUtil.java
new file mode 100644
index 00000000..80f8828b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/util/GMSSUtil.java
@@ -0,0 +1,151 @@
+package org.bouncycastle.pqc.crypto.gmss.util;
+
+/**
+ * This class provides several methods that are required by the GMSS classes.
+ */
+public class GMSSUtil
+{
+ /**
+ * Converts a 32 bit integer into a byte array beginning at
+ * <code>offset</code> (little-endian representation)
+ *
+ * @param value the integer to convert
+ */
+ public byte[] intToBytesLittleEndian(int value)
+ {
+ byte[] bytes = new byte[4];
+
+ bytes[0] = (byte)((value) & 0xff);
+ bytes[1] = (byte)((value >> 8) & 0xff);
+ bytes[2] = (byte)((value >> 16) & 0xff);
+ bytes[3] = (byte)((value >> 24) & 0xff);
+ return bytes;
+ }
+
+ /**
+ * Converts a byte array beginning at <code>offset</code> into a 32 bit
+ * integer (little-endian representation)
+ *
+ * @param bytes the byte array
+ * @return The resulting integer
+ */
+ public int bytesToIntLittleEndian(byte[] bytes)
+ {
+
+ return ((bytes[0] & 0xff)) | ((bytes[1] & 0xff) << 8)
+ | ((bytes[2] & 0xff) << 16) | ((bytes[3] & 0xff)) << 24;
+ }
+
+ /**
+ * Converts a byte array beginning at <code>offset</code> into a 32 bit
+ * integer (little-endian representation)
+ *
+ * @param bytes the byte array
+ * @param offset the integer offset into the byte array
+ * @return The resulting integer
+ */
+ public int bytesToIntLittleEndian(byte[] bytes, int offset)
+ {
+ return ((bytes[offset++] & 0xff)) | ((bytes[offset++] & 0xff) << 8)
+ | ((bytes[offset++] & 0xff) << 16)
+ | ((bytes[offset] & 0xff)) << 24;
+ }
+
+ /**
+ * This method concatenates a 2-dimensional byte array into a 1-dimensional
+ * byte array
+ *
+ * @param arraycp a 2-dimensional byte array.
+ * @return 1-dimensional byte array with concatenated input array
+ */
+ public byte[] concatenateArray(byte[][] arraycp)
+ {
+ byte[] dest = new byte[arraycp.length * arraycp[0].length];
+ int indx = 0;
+ for (int i = 0; i < arraycp.length; i++)
+ {
+ System.arraycopy(arraycp[i], 0, dest, indx, arraycp[i].length);
+ indx = indx + arraycp[i].length;
+ }
+ return dest;
+ }
+
+ /**
+ * This method prints the values of a 2-dimensional byte array
+ *
+ * @param text a String
+ * @param array a 2-dimensional byte array
+ */
+ public void printArray(String text, byte[][] array)
+ {
+ System.out.println(text);
+ int counter = 0;
+ for (int i = 0; i < array.length; i++)
+ {
+ for (int j = 0; j < array[0].length; j++)
+ {
+ System.out.println(counter + "; " + array[i][j]);
+ counter++;
+ }
+ }
+ }
+
+ /**
+ * This method prints the values of a 1-dimensional byte array
+ *
+ * @param text a String
+ * @param array a 1-dimensional byte array.
+ */
+ public void printArray(String text, byte[] array)
+ {
+ System.out.println(text);
+ int counter = 0;
+ for (int i = 0; i < array.length; i++)
+ {
+ System.out.println(counter + "; " + array[i]);
+ counter++;
+ }
+ }
+
+ /**
+ * This method tests if an integer is a power of 2.
+ *
+ * @param testValue an integer
+ * @return <code>TRUE</code> if <code>testValue</code> is a power of 2,
+ * <code>FALSE</code> otherwise
+ */
+ public boolean testPowerOfTwo(int testValue)
+ {
+ int a = 1;
+ while (a < testValue)
+ {
+ a <<= 1;
+ }
+ if (testValue == a)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * This method returns the least integer that is greater or equal to the
+ * logarithm to the base 2 of an integer <code>intValue</code>.
+ *
+ * @param intValue an integer
+ * @return The least integer greater or equal to the logarithm to the base 2
+ * of <code>intValue</code>
+ */
+ public int getLog(int intValue)
+ {
+ int log = 1;
+ int i = 2;
+ while (i < intValue)
+ {
+ i <<= 1;
+ log++;
+ }
+ return log;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/util/WinternitzOTSVerify.java b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/util/WinternitzOTSVerify.java
new file mode 100644
index 00000000..096de757
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/util/WinternitzOTSVerify.java
@@ -0,0 +1,345 @@
+package org.bouncycastle.pqc.crypto.gmss.util;
+
+import org.bouncycastle.crypto.Digest;
+
+/**
+ * This class implements signature verification of the Winternitz one-time
+ * signature scheme (OTSS), described in C.Dods, N.P. Smart, and M. Stam, "Hash
+ * Based Digital Signature Schemes", LNCS 3796, pages 96&#8211;115, 2005. The
+ * class is used by the GMSS classes.
+ */
+public class WinternitzOTSVerify
+{
+
+ private Digest messDigestOTS;
+
+ /**
+ * The Winternitz parameter
+ */
+ private int w;
+
+ /**
+ * The constructor
+ * <p/>
+ *
+ * @param digest the name of the hash function used by the OTS and the provider
+ * name of the hash function
+ * @param w the Winternitz parameter
+ */
+ public WinternitzOTSVerify(Digest digest, int w)
+ {
+ this.w = w;
+
+ messDigestOTS = digest;
+ }
+
+ /**
+ * @return The length of the one-time signature
+ */
+ public int getSignatureLength()
+ {
+ int mdsize = messDigestOTS.getDigestSize();
+ int size = ((mdsize << 3) + (w - 1)) / w;
+ int logs = getLog((size << w) + 1);
+ size += (logs + w - 1) / w;
+
+ return mdsize * size;
+ }
+
+ /**
+ * This method computes the public OTS key from the one-time signature of a
+ * message. This is *NOT* a complete OTS signature verification, but it
+ * suffices for usage with CMSS.
+ *
+ * @param message the message
+ * @param signature the one-time signature
+ * @return The public OTS key
+ */
+ public byte[] Verify(byte[] message, byte[] signature)
+ {
+
+ int mdsize = messDigestOTS.getDigestSize();
+ byte[] hash = new byte[mdsize]; // hash of message m
+
+ // create hash of message m
+ messDigestOTS.update(message, 0, message.length);
+ hash = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hash, 0);
+
+ int size = ((mdsize << 3) + (w - 1)) / w;
+ int logs = getLog((size << w) + 1);
+ int keysize = size + (logs + w - 1) / w;
+
+ int testKeySize = mdsize * keysize;
+
+ if (testKeySize != signature.length)
+ {
+ return null;
+ }
+
+ byte[] testKey = new byte[testKeySize];
+
+ int c = 0;
+ int counter = 0;
+ int test;
+
+ if (8 % w == 0)
+ {
+ int d = 8 / w;
+ int k = (1 << w) - 1;
+ byte[] hlp = new byte[mdsize];
+
+ // verify signature
+ for (int i = 0; i < hash.length; i++)
+ {
+ for (int j = 0; j < d; j++)
+ {
+ test = hash[i] & k;
+ c += test;
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test++;
+ }
+
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ hash[i] = (byte)(hash[i] >>> w);
+ counter++;
+ }
+ }
+
+ c = (size << w) - c;
+ for (int i = 0; i < logs; i += w)
+ {
+ test = c & k;
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test++;
+ }
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ c >>>= w;
+ counter++;
+ }
+ }
+ else if (w < 8)
+ {
+ int d = mdsize / w;
+ int k = (1 << w) - 1;
+ byte[] hlp = new byte[mdsize];
+ long big8;
+ int ii = 0;
+ // create signature
+ // first d*w bytes of hash
+ for (int i = 0; i < d; i++)
+ {
+ big8 = 0;
+ for (int j = 0; j < w; j++)
+ {
+ big8 ^= (hash[ii] & 0xff) << (j << 3);
+ ii++;
+ }
+ for (int j = 0; j < 8; j++)
+ {
+ test = (int)(big8 & k);
+ c += test;
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test++;
+ }
+
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ big8 >>>= w;
+ counter++;
+ }
+ }
+ // rest of hash
+ d = mdsize % w;
+ big8 = 0;
+ for (int j = 0; j < d; j++)
+ {
+ big8 ^= (hash[ii] & 0xff) << (j << 3);
+ ii++;
+ }
+ d <<= 3;
+ for (int j = 0; j < d; j += w)
+ {
+ test = (int)(big8 & k);
+ c += test;
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test++;
+ }
+
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ big8 >>>= w;
+ counter++;
+ }
+
+ // check bytes
+ c = (size << w) - c;
+ for (int i = 0; i < logs; i += w)
+ {
+ test = c & k;
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test++;
+ }
+
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ c >>>= w;
+ counter++;
+ }
+ }// end if(w<8)
+ else if (w < 57)
+ {
+ int d = (mdsize << 3) - w;
+ int k = (1 << w) - 1;
+ byte[] hlp = new byte[mdsize];
+ long big8, test8;
+ int r = 0;
+ int s, f, rest, ii;
+ // create signature
+ // first a*w bits of hash where a*w <= 8*mdsize < (a+1)*w
+ while (r <= d)
+ {
+ s = r >>> 3;
+ rest = r % 8;
+ r += w;
+ f = (r + 7) >>> 3;
+ big8 = 0;
+ ii = 0;
+ for (int j = s; j < f; j++)
+ {
+ big8 ^= (hash[j] & 0xff) << (ii << 3);
+ ii++;
+ }
+
+ big8 >>>= rest;
+ test8 = (big8 & k);
+ c += test8;
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test8 < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test8++;
+ }
+
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ counter++;
+
+ }
+ // rest of hash
+ s = r >>> 3;
+ if (s < mdsize)
+ {
+ rest = r % 8;
+ big8 = 0;
+ ii = 0;
+ for (int j = s; j < mdsize; j++)
+ {
+ big8 ^= (hash[j] & 0xff) << (ii << 3);
+ ii++;
+ }
+
+ big8 >>>= rest;
+ test8 = (big8 & k);
+ c += test8;
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test8 < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test8++;
+ }
+
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ counter++;
+ }
+ // check bytes
+ c = (size << w) - c;
+ for (int i = 0; i < logs; i += w)
+ {
+ test8 = (c & k);
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test8 < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test8++;
+ }
+
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ c >>>= w;
+ counter++;
+ }
+ }// end if(w<57)
+
+ byte[] TKey = new byte[mdsize];
+ messDigestOTS.update(testKey, 0, testKey.length);
+ TKey = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(TKey, 0);
+
+ return TKey;
+
+ }
+
+ /**
+ * This method returns the least integer that is greater or equal to the
+ * logarithm to the base 2 of an integer <code>intValue</code>.
+ *
+ * @param intValue an integer
+ * @return The least integer greater or equal to the logarithm to the base
+ * 256 of <code>intValue</code>
+ */
+ public int getLog(int intValue)
+ {
+ int log = 1;
+ int i = 2;
+ while (i < intValue)
+ {
+ i <<= 1;
+ log++;
+ }
+ return log;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/util/WinternitzOTSignature.java b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/util/WinternitzOTSignature.java
new file mode 100644
index 00000000..51eaf53c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/gmss/util/WinternitzOTSignature.java
@@ -0,0 +1,405 @@
+package org.bouncycastle.pqc.crypto.gmss.util;
+
+import org.bouncycastle.crypto.Digest;
+
+/**
+ * This class implements key pair generation and signature generation of the
+ * Winternitz one-time signature scheme (OTSS), described in C.Dods, N.P. Smart,
+ * and M. Stam, "Hash Based Digital Signature Schemes", LNCS 3796, pages
+ * 96&#8211;115, 2005. The class is used by the GMSS classes.
+ */
+
+public class WinternitzOTSignature
+{
+
+ /**
+ * The hash function used by the OTS
+ */
+ private Digest messDigestOTS;
+
+ /**
+ * The length of the message digest and private key
+ */
+ private int mdsize, keysize;
+
+ /**
+ * An array of strings, containing the name of the used hash function, the
+ * name of the PRGN and the names of the corresponding providers
+ */
+ // private String[] name = new String[2];
+ /**
+ * The private key
+ */
+ private byte[][] privateKeyOTS;
+
+ /**
+ * The Winternitz parameter
+ */
+ private int w;
+
+ /**
+ * The source of randomness for OTS private key generation
+ */
+ private GMSSRandom gmssRandom;
+
+ /**
+ * Sizes of the message and the checksum, both
+ */
+ private int messagesize, checksumsize;
+
+ /**
+ * The constructor generates an OTS key pair, using <code>seed0</code> and
+ * the PRNG
+ * <p/>
+ *
+ * @param seed0 the seed for the PRGN
+ * @param digest an array of strings, containing the name of the used hash
+ * function, the name of the PRGN and the names of the
+ * corresponding providers
+ * @param w the Winternitz parameter
+ */
+ public WinternitzOTSignature(byte[] seed0, Digest digest, int w)
+ {
+ // this.name = name;
+ this.w = w;
+
+ messDigestOTS = digest;
+
+ gmssRandom = new GMSSRandom(messDigestOTS);
+
+ // calulate keysize for private and public key and also the help
+ // array
+
+ mdsize = messDigestOTS.getDigestSize();
+ int mdsizeBit = mdsize << 3;
+ messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w);
+
+ checksumsize = getLog((messagesize << w) + 1);
+
+ keysize = messagesize
+ + (int)Math.ceil((double)checksumsize / (double)w);
+
+ /*
+ * mdsize = messDigestOTS.getDigestLength(); messagesize =
+ * ((mdsize<<3)+(w-1))/w;
+ *
+ * checksumsize = getlog((messagesize<<w)+1);
+ *
+ * keysize = messagesize + (checksumsize+w-1)/w;
+ */
+ // define the private key messagesize
+ privateKeyOTS = new byte[keysize][mdsize];
+
+ // gmssRandom.setSeed(seed0);
+ byte[] dummy = new byte[mdsize];
+ System.arraycopy(seed0, 0, dummy, 0, dummy.length);
+
+ // generate random bytes and
+ // assign them to the private key
+ for (int i = 0; i < keysize; i++)
+ {
+ privateKeyOTS[i] = gmssRandom.nextSeed(dummy);
+ }
+ }
+
+ /**
+ * @return The private OTS key
+ */
+ public byte[][] getPrivateKey()
+ {
+ return privateKeyOTS;
+ }
+
+ /**
+ * @return The public OTS key
+ */
+ public byte[] getPublicKey()
+ {
+ byte[] helppubKey = new byte[keysize * mdsize];
+
+ byte[] help = new byte[mdsize];
+ int two_power_t = 1 << w;
+
+ for (int i = 0; i < keysize; i++)
+ {
+ // hash w-1 time the private key and assign it to the public key
+ messDigestOTS.update(privateKeyOTS[i], 0, privateKeyOTS[i].length);
+ help = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(help, 0);
+ for (int j = 2; j < two_power_t; j++)
+ {
+ messDigestOTS.update(help, 0, help.length);
+ help = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(help, 0);
+ }
+ System.arraycopy(help, 0, helppubKey, mdsize * i, mdsize);
+ }
+
+ messDigestOTS.update(helppubKey, 0, helppubKey.length);
+ byte[] tmp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(tmp, 0);
+ return tmp;
+ }
+
+ /**
+ * @return The one-time signature of the message, generated with the private
+ * key
+ */
+ public byte[] getSignature(byte[] message)
+ {
+ byte[] sign = new byte[keysize * mdsize];
+ // byte [] message; // message m as input
+ byte[] hash = new byte[mdsize]; // hash of message m
+ int counter = 0;
+ int c = 0;
+ int test = 0;
+ // create hash of message m
+ messDigestOTS.update(message, 0, message.length);
+ hash = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hash, 0);
+
+ if (8 % w == 0)
+ {
+ int d = 8 / w;
+ int k = (1 << w) - 1;
+ byte[] hlp = new byte[mdsize];
+
+ // create signature
+ for (int i = 0; i < hash.length; i++)
+ {
+ for (int j = 0; j < d; j++)
+ {
+ test = hash[i] & k;
+ c += test;
+
+ System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize);
+
+ while (test > 0)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test--;
+ }
+ System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize);
+ hash[i] = (byte)(hash[i] >>> w);
+ counter++;
+ }
+ }
+
+ c = (messagesize << w) - c;
+ for (int i = 0; i < checksumsize; i += w)
+ {
+ test = c & k;
+
+ System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize);
+
+ while (test > 0)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test--;
+ }
+ System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize);
+ c >>>= w;
+ counter++;
+ }
+ }
+ else if (w < 8)
+ {
+ int d = mdsize / w;
+ int k = (1 << w) - 1;
+ byte[] hlp = new byte[mdsize];
+ long big8;
+ int ii = 0;
+ // create signature
+ // first d*w bytes of hash
+ for (int i = 0; i < d; i++)
+ {
+ big8 = 0;
+ for (int j = 0; j < w; j++)
+ {
+ big8 ^= (hash[ii] & 0xff) << (j << 3);
+ ii++;
+ }
+ for (int j = 0; j < 8; j++)
+ {
+ test = (int)(big8 & k);
+ c += test;
+
+ System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize);
+
+ while (test > 0)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test--;
+ }
+ System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize);
+ big8 >>>= w;
+ counter++;
+ }
+ }
+ // rest of hash
+ d = mdsize % w;
+ big8 = 0;
+ for (int j = 0; j < d; j++)
+ {
+ big8 ^= (hash[ii] & 0xff) << (j << 3);
+ ii++;
+ }
+ d <<= 3;
+ for (int j = 0; j < d; j += w)
+ {
+ test = (int)(big8 & k);
+ c += test;
+
+ System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize);
+
+ while (test > 0)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test--;
+ }
+ System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize);
+ big8 >>>= w;
+ counter++;
+ }
+
+ // check bytes
+ c = (messagesize << w) - c;
+ for (int i = 0; i < checksumsize; i += w)
+ {
+ test = c & k;
+
+ System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize);
+
+ while (test > 0)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test--;
+ }
+ System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize);
+ c >>>= w;
+ counter++;
+ }
+ }// end if(w<8)
+ else if (w < 57)
+ {
+ int d = (mdsize << 3) - w;
+ int k = (1 << w) - 1;
+ byte[] hlp = new byte[mdsize];
+ long big8, test8;
+ int r = 0;
+ int s, f, rest, ii;
+ // create signature
+ // first a*w bits of hash where a*w <= 8*mdsize < (a+1)*w
+ while (r <= d)
+ {
+ s = r >>> 3;
+ rest = r % 8;
+ r += w;
+ f = (r + 7) >>> 3;
+ big8 = 0;
+ ii = 0;
+ for (int j = s; j < f; j++)
+ {
+ big8 ^= (hash[j] & 0xff) << (ii << 3);
+ ii++;
+ }
+
+ big8 >>>= rest;
+ test8 = (big8 & k);
+ c += test8;
+
+ System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize);
+ while (test8 > 0)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test8--;
+ }
+ System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize);
+ counter++;
+
+ }
+ // rest of hash
+ s = r >>> 3;
+ if (s < mdsize)
+ {
+ rest = r % 8;
+ big8 = 0;
+ ii = 0;
+ for (int j = s; j < mdsize; j++)
+ {
+ big8 ^= (hash[j] & 0xff) << (ii << 3);
+ ii++;
+ }
+
+ big8 >>>= rest;
+ test8 = (big8 & k);
+ c += test8;
+
+ System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize);
+ while (test8 > 0)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test8--;
+ }
+ System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize);
+ counter++;
+ }
+ // check bytes
+ c = (messagesize << w) - c;
+ for (int i = 0; i < checksumsize; i += w)
+ {
+ test8 = (c & k);
+
+ System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize);
+
+ while (test8 > 0)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test8--;
+ }
+ System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize);
+ c >>>= w;
+ counter++;
+ }
+ }// end if(w<57)
+
+ return sign;
+ }
+
+ /**
+ * This method returns the least integer that is greater or equal to the
+ * logarithm to the base 2 of an integer <code>intValue</code>.
+ *
+ * @param intValue an integer
+ * @return The least integer greater or equal to the logarithm to the base 2
+ * of <code>intValue</code>
+ */
+ public int getLog(int intValue)
+ {
+ int log = 1;
+ int i = 2;
+ while (i < intValue)
+ {
+ i <<= 1;
+ log++;
+ }
+ return log;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/Conversions.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/Conversions.java
new file mode 100644
index 00000000..752d51cc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/Conversions.java
@@ -0,0 +1,236 @@
+package org.bouncycastle.pqc.crypto.mceliece;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.pqc.math.linearalgebra.BigIntUtils;
+import org.bouncycastle.pqc.math.linearalgebra.GF2Vector;
+import org.bouncycastle.pqc.math.linearalgebra.IntegerFunctions;
+
+
+/**
+ * Provides methods for CCA2-Secure Conversions of McEliece PKCS
+ */
+final class Conversions
+{
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ /**
+ * Default constructor (private).
+ */
+ private Conversions()
+ {
+ }
+
+ /**
+ * Encode a number between 0 and (n|t) (binomial coefficient) into a binary
+ * vector of length n with weight t. The number is given as a byte array.
+ * Only the first s bits are used, where s = floor[log(n|t)].
+ *
+ * @param n integer
+ * @param t integer
+ * @param m the message as a byte array
+ * @return the encoded message as {@link GF2Vector}
+ */
+ public static GF2Vector encode(final int n, final int t, final byte[] m)
+ {
+ if (n < t)
+ {
+ throw new IllegalArgumentException("n < t");
+ }
+
+ // compute the binomial c = (n|t)
+ BigInteger c = IntegerFunctions.binomial(n, t);
+ // get the number encoded in m
+ BigInteger i = new BigInteger(1, m);
+ // compare
+ if (i.compareTo(c) >= 0)
+ {
+ throw new IllegalArgumentException("Encoded number too large.");
+ }
+
+ GF2Vector result = new GF2Vector(n);
+
+ int nn = n;
+ int tt = t;
+ for (int j = 0; j < n; j++)
+ {
+ c = c.multiply(BigInteger.valueOf(nn - tt)).divide(
+ BigInteger.valueOf(nn));
+ nn--;
+ if (c.compareTo(i) <= 0)
+ {
+ result.setBit(j);
+ i = i.subtract(c);
+ tt--;
+ if (nn == tt)
+ {
+ c = ONE;
+ }
+ else
+ {
+ c = (c.multiply(BigInteger.valueOf(tt + 1)))
+ .divide(BigInteger.valueOf(nn - tt));
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Decode a binary vector of length n and weight t into a number between 0
+ * and (n|t) (binomial coefficient). The result is given as a byte array of
+ * length floor[(s+7)/8], where s = floor[log(n|t)].
+ *
+ * @param n integer
+ * @param t integer
+ * @param vec the binary vector
+ * @return the decoded vector as a byte array
+ */
+ public static byte[] decode(int n, int t, GF2Vector vec)
+ {
+ if ((vec.getLength() != n) || (vec.getHammingWeight() != t))
+ {
+ throw new IllegalArgumentException(
+ "vector has wrong length or hamming weight");
+ }
+ int[] vecArray = vec.getVecArray();
+
+ BigInteger bc = IntegerFunctions.binomial(n, t);
+ BigInteger d = ZERO;
+ int nn = n;
+ int tt = t;
+ for (int i = 0; i < n; i++)
+ {
+ bc = bc.multiply(BigInteger.valueOf(nn - tt)).divide(
+ BigInteger.valueOf(nn));
+ nn--;
+
+ int q = i >> 5;
+ int e = vecArray[q] & (1 << (i & 0x1f));
+ if (e != 0)
+ {
+ d = d.add(bc);
+ tt--;
+ if (nn == tt)
+ {
+ bc = ONE;
+ }
+ else
+ {
+ bc = bc.multiply(BigInteger.valueOf(tt + 1)).divide(
+ BigInteger.valueOf(nn - tt));
+ }
+
+ }
+ }
+
+ return BigIntUtils.toMinimalByteArray(d);
+ }
+
+ /**
+ * Compute a message representative of a message given as a vector of length
+ * <tt>n</tt> bit and of hamming weight <tt>t</tt>. The result is a
+ * byte array of length <tt>(s+7)/8</tt>, where
+ * <tt>s = floor[log(n|t)]</tt>.
+ *
+ * @param n integer
+ * @param t integer
+ * @param m the message vector as a byte array
+ * @return a message representative for <tt>m</tt>
+ */
+ public static byte[] signConversion(int n, int t, byte[] m)
+ {
+ if (n < t)
+ {
+ throw new IllegalArgumentException("n < t");
+ }
+
+ BigInteger bc = IntegerFunctions.binomial(n, t);
+ // finds s = floor[log(binomial(n,t))]
+ int s = bc.bitLength() - 1;
+ // s = sq*8 + sr;
+ int sq = s >> 3;
+ int sr = s & 7;
+ if (sr == 0)
+ {
+ sq--;
+ sr = 8;
+ }
+
+ // n = nq*8+nr;
+ int nq = n >> 3;
+ int nr = n & 7;
+ if (nr == 0)
+ {
+ nq--;
+ nr = 8;
+ }
+ // take s bit from m
+ byte[] data = new byte[nq + 1];
+ if (m.length < data.length)
+ {
+ System.arraycopy(m, 0, data, 0, m.length);
+ for (int i = m.length; i < data.length; i++)
+ {
+ data[i] = 0;
+ }
+ }
+ else
+ {
+ System.arraycopy(m, 0, data, 0, nq);
+ int h = (1 << nr) - 1;
+ data[nq] = (byte)(h & m[nq]);
+ }
+
+ BigInteger d = ZERO;
+ int nn = n;
+ int tt = t;
+ for (int i = 0; i < n; i++)
+ {
+ bc = (bc.multiply(new BigInteger(Integer.toString(nn - tt))))
+ .divide(new BigInteger(Integer.toString(nn)));
+ nn--;
+
+ int q = i >>> 3;
+ int r = i & 7;
+ r = 1 << r;
+ byte e = (byte)(r & data[q]);
+ if (e != 0)
+ {
+ d = d.add(bc);
+ tt--;
+ if (nn == tt)
+ {
+ bc = ONE;
+ }
+ else
+ {
+ bc = (bc
+ .multiply(new BigInteger(Integer.toString(tt + 1))))
+ .divide(new BigInteger(Integer.toString(nn - tt)));
+ }
+ }
+ }
+
+ byte[] result = new byte[sq + 1];
+ byte[] help = d.toByteArray();
+ if (help.length < result.length)
+ {
+ System.arraycopy(help, 0, result, 0, help.length);
+ for (int i = help.length; i < result.length; i++)
+ {
+ result[i] = 0;
+ }
+ }
+ else
+ {
+ System.arraycopy(help, 0, result, 0, sq);
+ result[sq] = (byte)(((1 << sr) - 1) & help[sq]);
+ }
+
+ return result;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2KeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2KeyGenerationParameters.java
new file mode 100644
index 00000000..dbd5a82a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2KeyGenerationParameters.java
@@ -0,0 +1,25 @@
+package org.bouncycastle.pqc.crypto.mceliece;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.KeyGenerationParameters;
+
+public class McElieceCCA2KeyGenerationParameters
+ extends KeyGenerationParameters
+{
+ private McElieceCCA2Parameters params;
+
+ public McElieceCCA2KeyGenerationParameters(
+ SecureRandom random,
+ McElieceCCA2Parameters params)
+ {
+ // XXX key size?
+ super(random, 128);
+ this.params = params;
+ }
+
+ public McElieceCCA2Parameters getParameters()
+ {
+ return params;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2KeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2KeyPairGenerator.java
new file mode 100644
index 00000000..198e5d29
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2KeyPairGenerator.java
@@ -0,0 +1,119 @@
+package org.bouncycastle.pqc.crypto.mceliece;
+
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.pqc.math.linearalgebra.GF2Matrix;
+import org.bouncycastle.pqc.math.linearalgebra.GF2mField;
+import org.bouncycastle.pqc.math.linearalgebra.GoppaCode;
+import org.bouncycastle.pqc.math.linearalgebra.GoppaCode.MaMaPe;
+import org.bouncycastle.pqc.math.linearalgebra.Permutation;
+import org.bouncycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
+import org.bouncycastle.pqc.math.linearalgebra.PolynomialRingGF2m;
+
+
+/**
+ * This class implements key pair generation of the McEliece Public Key
+ * Cryptosystem (McEliecePKC).
+ */
+public class McElieceCCA2KeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+
+
+ /**
+ * The OID of the algorithm.
+ */
+ public static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.2";
+
+ private McElieceCCA2KeyGenerationParameters mcElieceCCA2Params;
+
+ // the extension degree of the finite field GF(2^m)
+ private int m;
+
+ // the length of the code
+ private int n;
+
+ // the error correction capability
+ private int t;
+
+ // the field polynomial
+ private int fieldPoly;
+
+ // the source of randomness
+ private SecureRandom random;
+
+ // flag indicating whether the key pair generator has been initialized
+ private boolean initialized = false;
+
+ /**
+ * Default initialization of the key pair generator.
+ */
+ private void initializeDefault()
+ {
+ McElieceCCA2KeyGenerationParameters mcCCA2Params = new McElieceCCA2KeyGenerationParameters(new SecureRandom(), new McElieceCCA2Parameters());
+ init(mcCCA2Params);
+ }
+
+ // TODO
+ public void init(
+ KeyGenerationParameters param)
+ {
+ this.mcElieceCCA2Params = (McElieceCCA2KeyGenerationParameters)param;
+
+ // set source of randomness
+ this.random = new SecureRandom();
+
+ this.m = this.mcElieceCCA2Params.getParameters().getM();
+ this.n = this.mcElieceCCA2Params.getParameters().getN();
+ this.t = this.mcElieceCCA2Params.getParameters().getT();
+ this.fieldPoly = this.mcElieceCCA2Params.getParameters().getFieldPoly();
+ this.initialized = true;
+ }
+
+
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+
+ if (!initialized)
+ {
+ initializeDefault();
+ }
+
+ // finite field GF(2^m)
+ GF2mField field = new GF2mField(m, fieldPoly);
+
+ // irreducible Goppa polynomial
+ PolynomialGF2mSmallM gp = new PolynomialGF2mSmallM(field, t,
+ PolynomialGF2mSmallM.RANDOM_IRREDUCIBLE_POLYNOMIAL, random);
+ PolynomialRingGF2m ring = new PolynomialRingGF2m(field, gp);
+
+ // matrix for computing square roots in (GF(2^m))^t
+ PolynomialGF2mSmallM[] qInv = ring.getSquareRootMatrix();
+
+ // generate canonical check matrix
+ GF2Matrix h = GoppaCode.createCanonicalCheckMatrix(field, gp);
+
+ // compute short systematic form of check matrix
+ MaMaPe mmp = GoppaCode.computeSystematicForm(h, random);
+ GF2Matrix shortH = mmp.getSecondMatrix();
+ Permutation p = mmp.getPermutation();
+
+ // compute short systematic form of generator matrix
+ GF2Matrix shortG = (GF2Matrix)shortH.computeTranspose();
+
+ // obtain number of rows of G (= dimension of the code)
+ int k = shortG.getNumRows();
+
+ // generate keys
+ McElieceCCA2PublicKeyParameters pubKey = new McElieceCCA2PublicKeyParameters(OID, n, t, shortG, mcElieceCCA2Params.getParameters());
+ McElieceCCA2PrivateKeyParameters privKey = new McElieceCCA2PrivateKeyParameters(OID, n, k,
+ field, gp, p, h, qInv, mcElieceCCA2Params.getParameters());
+
+ // return key pair
+ return new AsymmetricCipherKeyPair(pubKey, privKey);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2KeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2KeyParameters.java
new file mode 100644
index 00000000..80114767
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2KeyParameters.java
@@ -0,0 +1,25 @@
+package org.bouncycastle.pqc.crypto.mceliece;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+
+
+public class McElieceCCA2KeyParameters
+ extends AsymmetricKeyParameter
+{
+ private McElieceCCA2Parameters params;
+
+ public McElieceCCA2KeyParameters(
+ boolean isPrivate,
+ McElieceCCA2Parameters params)
+ {
+ super(isPrivate);
+ this.params = params;
+ }
+
+
+ public McElieceCCA2Parameters getParameters()
+ {
+ return params;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2Parameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2Parameters.java
new file mode 100644
index 00000000..7f800106
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2Parameters.java
@@ -0,0 +1,51 @@
+package org.bouncycastle.pqc.crypto.mceliece;
+
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+
+/**
+ * This class provides a specification for the parameters of the CCA2-secure
+ * variants of the McEliece PKCS that are used with
+ * {@link McElieceFujisakiCipher}, {@link McElieceKobaraImaiCipher}, and
+ * {@link McEliecePointchevalCipher}.
+ *
+ * @see McElieceFujisakiCipher
+ * @see McElieceKobaraImaiCipher
+ * @see McEliecePointchevalCipher
+ */
+public class McElieceCCA2Parameters
+ extends McElieceParameters
+{
+
+
+ public Digest digest;
+
+
+ /**
+ * Construct the default parameters.
+ * The default message digest is SHA256.
+ */
+ public McElieceCCA2Parameters()
+ {
+ this.digest = new SHA256Digest();
+ }
+
+ public McElieceCCA2Parameters(int m, int t)
+ {
+ super(m, t);
+ this.digest = new SHA256Digest();
+ }
+
+ public McElieceCCA2Parameters(Digest digest)
+ {
+ this.digest = digest;
+ }
+
+ public Digest getDigest()
+ {
+ return this.digest;
+ }
+
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2Primitives.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2Primitives.java
new file mode 100644
index 00000000..726add15
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2Primitives.java
@@ -0,0 +1,86 @@
+package org.bouncycastle.pqc.crypto.mceliece;
+
+import org.bouncycastle.pqc.math.linearalgebra.GF2Matrix;
+import org.bouncycastle.pqc.math.linearalgebra.GF2Vector;
+import org.bouncycastle.pqc.math.linearalgebra.GF2mField;
+import org.bouncycastle.pqc.math.linearalgebra.GoppaCode;
+import org.bouncycastle.pqc.math.linearalgebra.Permutation;
+import org.bouncycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
+import org.bouncycastle.pqc.math.linearalgebra.Vector;
+
+/**
+ * Core operations for the CCA-secure variants of McEliece.
+ */
+public final class McElieceCCA2Primitives
+{
+
+ /**
+ * Default constructor (private).
+ */
+ private McElieceCCA2Primitives()
+ {
+ }
+
+ /**
+ * The McEliece encryption primitive.
+ *
+ * @param pubKey the public key
+ * @param m the message vector
+ * @param z the error vector
+ * @return <tt>m*G + z</tt>
+ */
+
+
+ public static GF2Vector encryptionPrimitive(McElieceCCA2PublicKeyParameters pubKey,
+ GF2Vector m, GF2Vector z)
+ {
+
+ GF2Matrix matrixG = pubKey.getMatrixG();
+ Vector mG = matrixG.leftMultiplyLeftCompactForm(m);
+ return (GF2Vector)mG.add(z);
+ }
+
+ /**
+ * The McEliece decryption primitive.
+ *
+ * @param privKey the private key
+ * @param c the ciphertext vector <tt>c = m*G + z</tt>
+ * @return the message vector <tt>m</tt> and the error vector <tt>z</tt>
+ */
+ public static GF2Vector[] decryptionPrimitive(
+ McElieceCCA2PrivateKeyParameters privKey, GF2Vector c)
+ {
+
+ // obtain values from private key
+ int k = privKey.getK();
+ Permutation p = privKey.getP();
+ GF2mField field = privKey.getField();
+ PolynomialGF2mSmallM gp = privKey.getGoppaPoly();
+ GF2Matrix h = privKey.getH();
+ PolynomialGF2mSmallM[] q = privKey.getQInv();
+
+ // compute inverse permutation P^-1
+ Permutation pInv = p.computeInverse();
+
+ // multiply c with permutation P^-1
+ GF2Vector cPInv = (GF2Vector)c.multiply(pInv);
+
+ // compute syndrome of cP^-1
+ GF2Vector syndVec = (GF2Vector)h.rightMultiply(cPInv);
+
+ // decode syndrome
+ GF2Vector errors = GoppaCode.syndromeDecode(syndVec, field, gp, q);
+ GF2Vector mG = (GF2Vector)cPInv.add(errors);
+
+ // multiply codeword and error vector with P
+ mG = (GF2Vector)mG.multiply(p);
+ errors = (GF2Vector)errors.multiply(p);
+
+ // extract plaintext vector (last k columns of mG)
+ GF2Vector m = mG.extractRightVector(k);
+
+ // return vectors
+ return new GF2Vector[]{m, errors};
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2PrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2PrivateKeyParameters.java
new file mode 100644
index 00000000..980ecdc9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2PrivateKeyParameters.java
@@ -0,0 +1,172 @@
+package org.bouncycastle.pqc.crypto.mceliece;
+
+
+import org.bouncycastle.pqc.math.linearalgebra.GF2Matrix;
+import org.bouncycastle.pqc.math.linearalgebra.GF2mField;
+import org.bouncycastle.pqc.math.linearalgebra.Permutation;
+import org.bouncycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
+
+/**
+ *
+ *
+ *
+ */
+public class McElieceCCA2PrivateKeyParameters
+ extends McElieceCCA2KeyParameters
+{
+
+ // the OID of the algorithm
+ private String oid;
+
+ // the length of the code
+ private int n;
+
+ // the dimension of the code
+ private int k;
+
+ // the finte field GF(2^m)
+ private GF2mField field;
+
+ // the irreducible Goppa polynomial
+ private PolynomialGF2mSmallM goppaPoly;
+
+ // the permutation
+ private Permutation p;
+
+ // the canonical check matrix
+ private GF2Matrix h;
+
+ // the matrix used to compute square roots in (GF(2^m))^t
+ private PolynomialGF2mSmallM[] qInv;
+
+ /**
+ * Constructor.
+ *
+ * @param n the length of the code
+ * @param k the dimension of the code
+ * @param field the finite field <tt>GF(2<sup>m</sup>)</tt>
+ * @param gp the irreducible Goppa polynomial
+ * @param p the permutation
+ * @param h the canonical check matrix
+ * @param qInv the matrix used to compute square roots in
+ * <tt>(GF(2^m))^t</tt>
+ * @param params McElieceCCA2Parameters
+ */
+ public McElieceCCA2PrivateKeyParameters(String oid, int n, int k, GF2mField field,
+ PolynomialGF2mSmallM gp, Permutation p, GF2Matrix h,
+ PolynomialGF2mSmallM[] qInv, McElieceCCA2Parameters params)
+ {
+ super(true, params);
+ this.oid = oid;
+ this.n = n;
+ this.k = k;
+ this.field = field;
+ this.goppaPoly = gp;
+ this.p = p;
+ this.h = h;
+ this.qInv = qInv;
+ }
+
+ /**
+ * Constructor used by the {@link McElieceKeyFactory}.
+ *
+ * @param n the length of the code
+ * @param k the dimension of the code
+ * @param encFieldPoly the encoded field polynomial defining the finite field
+ * <tt>GF(2<sup>m</sup>)</tt>
+ * @param encGoppaPoly the encoded irreducible Goppa polynomial
+ * @param encP the encoded permutation
+ * @param encH the encoded canonical check matrix
+ * @param encQInv the encoded matrix used to compute square roots in
+ * <tt>(GF(2^m))^t</tt>
+ * @param params McElieceCCA2Parameters
+ */
+ public McElieceCCA2PrivateKeyParameters(String oid, int n, int k, byte[] encFieldPoly,
+ byte[] encGoppaPoly, byte[] encP, byte[] encH, byte[][] encQInv, McElieceCCA2Parameters params)
+ {
+ super(true, params);
+ this.oid = oid;
+ this.n = n;
+ this.k = k;
+ field = new GF2mField(encFieldPoly);
+ goppaPoly = new PolynomialGF2mSmallM(field, encGoppaPoly);
+ p = new Permutation(encP);
+ h = new GF2Matrix(encH);
+ qInv = new PolynomialGF2mSmallM[encQInv.length];
+ for (int i = 0; i < encQInv.length; i++)
+ {
+ qInv[i] = new PolynomialGF2mSmallM(field, encQInv[i]);
+ }
+ }
+
+ /**
+ * @return the length of the code
+ */
+ public int getN()
+ {
+ return n;
+ }
+
+ /**
+ * @return the dimension of the code
+ */
+ public int getK()
+ {
+ return k;
+ }
+
+ /**
+ * @return the degree of the Goppa polynomial (error correcting capability)
+ */
+ public int getT()
+ {
+ return goppaPoly.getDegree();
+ }
+
+ /**
+ * @return the finite field
+ */
+ public GF2mField getField()
+ {
+ return field;
+ }
+
+ /**
+ * @return the irreducible Goppa polynomial
+ */
+ public PolynomialGF2mSmallM getGoppaPoly()
+ {
+ return goppaPoly;
+ }
+
+ /**
+ * @return the permutation P
+ */
+ public Permutation getP()
+ {
+ return p;
+ }
+
+ /**
+ * @return the canonical check matrix H
+ */
+ public GF2Matrix getH()
+ {
+ return h;
+ }
+
+ /**
+ * @return the matrix used to compute square roots in <tt>(GF(2^m))^t</tt>
+ */
+ public PolynomialGF2mSmallM[] getQInv()
+ {
+ return qInv;
+ }
+
+ public String getOIDString()
+ {
+ return oid;
+
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2PublicKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2PublicKeyParameters.java
new file mode 100644
index 00000000..e63377c3
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2PublicKeyParameters.java
@@ -0,0 +1,97 @@
+package org.bouncycastle.pqc.crypto.mceliece;
+
+import org.bouncycastle.pqc.math.linearalgebra.GF2Matrix;
+
+/**
+ *
+ *
+ *
+ */
+public class McElieceCCA2PublicKeyParameters
+ extends McElieceCCA2KeyParameters
+{
+
+ // the OID of the algorithm
+ private String oid;
+
+ // the length of the code
+ private int n;
+
+ // the error correction capability of the code
+ private int t;
+
+ // the generator matrix
+ private GF2Matrix matrixG;
+
+ /**
+ * Constructor.
+ *
+ * @param n length of the code
+ * @param t error correction capability
+ * @param matrix generator matrix
+ * @param params McElieceCCA2Parameters
+ */
+ public McElieceCCA2PublicKeyParameters(String oid, int n, int t, GF2Matrix matrix, McElieceCCA2Parameters params)
+ {
+ super(false, params);
+ this.oid = oid;
+ this.n = n;
+ this.t = t;
+ this.matrixG = new GF2Matrix(matrix);
+ }
+
+ /**
+ * Constructor (used by {@link McElieceKeyFactory}).
+ *
+ * @param n length of the code
+ * @param t error correction capability of the code
+ * @param encMatrix encoded generator matrix
+ * @param params McElieceCCA2Parameters
+ */
+ public McElieceCCA2PublicKeyParameters(String oid, int n, int t, byte[] encMatrix, McElieceCCA2Parameters params)
+ {
+ super(false, params);
+ this.oid = oid;
+ this.n = n;
+ this.t = t;
+ this.matrixG = new GF2Matrix(encMatrix);
+ }
+
+ /**
+ * @return the length of the code
+ */
+ public int getN()
+ {
+ return n;
+ }
+
+ /**
+ * @return the error correction capability of the code
+ */
+ public int getT()
+ {
+ return t;
+ }
+
+ /**
+ * @return the generator matrix
+ */
+ public GF2Matrix getMatrixG()
+ {
+ return matrixG;
+ }
+
+ /**
+ * @return the dimension of the code
+ */
+ public int getK()
+ {
+ return matrixG.getNumRows();
+ }
+
+ public String getOIDString()
+ {
+ return oid;
+
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceFujisakiCipher.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceFujisakiCipher.java
new file mode 100644
index 00000000..c414540f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceFujisakiCipher.java
@@ -0,0 +1,218 @@
+package org.bouncycastle.pqc.crypto.mceliece;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.prng.DigestRandomGenerator;
+import org.bouncycastle.pqc.crypto.MessageEncryptor;
+import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
+import org.bouncycastle.pqc.math.linearalgebra.GF2Vector;
+
+/**
+ * This class implements the Fujisaki/Okamoto conversion of the McEliecePKCS.
+ * Fujisaki and Okamoto propose hybrid encryption that merges a symmetric
+ * encryption scheme which is secure in the find-guess model with an asymmetric
+ * one-way encryption scheme which is sufficiently probabilistic to obtain a
+ * public key cryptosystem which is CCA2-secure. For details, see D. Engelbert,
+ * R. Overbeck, A. Schmidt, "A summary of the development of the McEliece
+ * Cryptosystem", technical report.
+ */
+public class McElieceFujisakiCipher
+ implements MessageEncryptor
+{
+
+
+ /**
+ * The OID of the algorithm.
+ */
+ public static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.2.1";
+
+ private static final String DEFAULT_PRNG_NAME = "SHA1PRNG";
+
+ private Digest messDigest;
+
+ private SecureRandom sr;
+
+ /**
+ * The McEliece main parameters
+ */
+ private int n, k, t;
+
+ McElieceCCA2KeyParameters key;
+
+
+ public void init(boolean forSigning,
+ CipherParameters param)
+ {
+
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.sr = rParam.getRandom();
+ this.key = (McElieceCCA2PublicKeyParameters)rParam.getParameters();
+ this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
+
+ }
+ else
+ {
+ this.sr = new SecureRandom();
+ this.key = (McElieceCCA2PublicKeyParameters)param;
+ this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
+ }
+ }
+ else
+ {
+ this.key = (McElieceCCA2PrivateKeyParameters)param;
+ this.initCipherDecrypt((McElieceCCA2PrivateKeyParameters)key);
+ }
+
+ }
+
+
+ public int getKeySize(McElieceCCA2KeyParameters key)
+ throws IllegalArgumentException
+ {
+
+ if (key instanceof McElieceCCA2PublicKeyParameters)
+ {
+ return ((McElieceCCA2PublicKeyParameters)key).getN();
+
+ }
+ if (key instanceof McElieceCCA2PrivateKeyParameters)
+ {
+ return ((McElieceCCA2PrivateKeyParameters)key).getN();
+ }
+ throw new IllegalArgumentException("unsupported type");
+
+ }
+
+
+ private void initCipherEncrypt(McElieceCCA2PublicKeyParameters pubKey)
+ {
+ this.sr = sr != null ? sr : new SecureRandom();
+ this.messDigest = pubKey.getParameters().getDigest();
+ n = pubKey.getN();
+ k = pubKey.getK();
+ t = pubKey.getT();
+ }
+
+
+ public void initCipherDecrypt(McElieceCCA2PrivateKeyParameters privKey)
+ {
+ this.messDigest = privKey.getParameters().getDigest();
+ n = privKey.getN();
+ t = privKey.getT();
+ }
+
+
+ public byte[] messageEncrypt(byte[] input)
+ throws Exception
+ {
+
+ // generate random vector r of length k bits
+ GF2Vector r = new GF2Vector(k, sr);
+
+ // convert r to byte array
+ byte[] rBytes = r.getEncoded();
+
+ // compute (r||input)
+ byte[] rm = ByteUtils.concatenate(rBytes, input);
+
+ // compute H(r||input)
+ messDigest.update(rm, 0, rm.length);
+ byte[] hrm = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hrm, 0);
+
+ // convert H(r||input) to error vector z
+ GF2Vector z = Conversions.encode(n, t, hrm);
+
+ // compute c1 = E(r, z)
+ byte[] c1 = McElieceCCA2Primitives.encryptionPrimitive((McElieceCCA2PublicKeyParameters)key, r, z)
+ .getEncoded();
+
+ // get PRNG object
+ DigestRandomGenerator sr0 = new DigestRandomGenerator(new SHA1Digest());
+
+ // seed PRNG with r'
+ sr0.addSeedMaterial(rBytes);
+
+ // generate random c2
+ byte[] c2 = new byte[input.length];
+ sr0.nextBytes(c2);
+
+ // XOR with input
+ for (int i = 0; i < input.length; i++)
+ {
+ c2[i] ^= input[i];
+ }
+
+ // return (c1||c2)
+ return ByteUtils.concatenate(c1, c2);
+ }
+
+ public byte[] messageDecrypt(byte[] input)
+ throws Exception
+ {
+
+ int c1Len = (n + 7) >> 3;
+ int c2Len = input.length - c1Len;
+
+ // split ciphertext (c1||c2)
+ byte[][] c1c2 = ByteUtils.split(input, c1Len);
+ byte[] c1 = c1c2[0];
+ byte[] c2 = c1c2[1];
+
+ // decrypt c1 ...
+ GF2Vector hrmVec = GF2Vector.OS2VP(n, c1);
+ GF2Vector[] decC1 = McElieceCCA2Primitives.decryptionPrimitive((McElieceCCA2PrivateKeyParameters)key,
+ hrmVec);
+ byte[] rBytes = decC1[0].getEncoded();
+ // ... and obtain error vector z
+ GF2Vector z = decC1[1];
+
+ // get PRNG object
+ DigestRandomGenerator sr0 = new DigestRandomGenerator(new SHA1Digest());
+
+ // seed PRNG with r'
+ sr0.addSeedMaterial(rBytes);
+
+ // generate random sequence
+ byte[] mBytes = new byte[c2Len];
+ sr0.nextBytes(mBytes);
+
+ // XOR with c2 to obtain m
+ for (int i = 0; i < c2Len; i++)
+ {
+ mBytes[i] ^= c2[i];
+ }
+
+ // compute H(r||m)
+ byte[] rmBytes = ByteUtils.concatenate(rBytes, mBytes);
+ byte[] hrm = new byte[messDigest.getDigestSize()];
+ messDigest.update(rmBytes, 0, rmBytes.length);
+ messDigest.doFinal(hrm, 0);
+
+
+ // compute Conv(H(r||m))
+ hrmVec = Conversions.encode(n, t, hrm);
+
+ // check that Conv(H(m||r)) = z
+ if (!hrmVec.equals(z))
+ {
+
+ throw new Exception("Bad Padding: invalid ciphertext");
+
+ }
+
+ // return plaintext m
+ return mBytes;
+ }
+
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceFujisakiDigestCipher.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceFujisakiDigestCipher.java
new file mode 100644
index 00000000..423e6ff8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceFujisakiDigestCipher.java
@@ -0,0 +1,128 @@
+package org.bouncycastle.pqc.crypto.mceliece;
+
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.pqc.crypto.MessageEncryptor;
+
+// TODO should implement some interface?
+public class McElieceFujisakiDigestCipher
+{
+
+ private final Digest messDigest;
+
+ private final MessageEncryptor mcElieceCCA2Cipher;
+
+ private boolean forEncrypting;
+
+
+ public McElieceFujisakiDigestCipher(MessageEncryptor mcElieceCCA2Cipher, Digest messDigest)
+ {
+ this.mcElieceCCA2Cipher = mcElieceCCA2Cipher;
+ this.messDigest = messDigest;
+ }
+
+
+ public void init(boolean forEncrypting,
+ CipherParameters param)
+ {
+
+ this.forEncrypting = forEncrypting;
+ AsymmetricKeyParameter k;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ k = (AsymmetricKeyParameter)((ParametersWithRandom)param).getParameters();
+ }
+ else
+ {
+ k = (AsymmetricKeyParameter)param;
+ }
+
+ if (forEncrypting && k.isPrivate())
+ {
+ throw new IllegalArgumentException("Encrypting Requires Public Key.");
+ }
+
+ if (!forEncrypting && !k.isPrivate())
+ {
+ throw new IllegalArgumentException("Decrypting Requires Private Key.");
+ }
+
+ reset();
+
+ mcElieceCCA2Cipher.init(forEncrypting, param);
+ }
+
+
+ public byte[] messageEncrypt()
+ {
+ if (!forEncrypting)
+ {
+ throw new IllegalStateException("McElieceFujisakiDigestCipher not initialised for encrypting.");
+ }
+
+ byte[] hash = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hash, 0);
+ byte[] enc = null;
+
+ try
+ {
+ enc = mcElieceCCA2Cipher.messageEncrypt(hash);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return enc;
+ }
+
+
+ public byte[] messageDecrypt(byte[] ciphertext)
+ {
+ byte[] output = null;
+ if (forEncrypting)
+ {
+ throw new IllegalStateException("McElieceFujisakiDigestCipher not initialised for decrypting.");
+ }
+
+
+ try
+ {
+ output = mcElieceCCA2Cipher.messageDecrypt(ciphertext);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return output;
+ }
+
+
+ public void update(byte b)
+ {
+ messDigest.update(b);
+
+ }
+
+ public void update(byte[] in, int off, int len)
+ {
+ messDigest.update(in, off, len);
+
+ }
+
+
+ public void reset()
+ {
+ messDigest.reset();
+
+ }
+
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKeyGenerationParameters.java
new file mode 100644
index 00000000..1b1fa658
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKeyGenerationParameters.java
@@ -0,0 +1,25 @@
+package org.bouncycastle.pqc.crypto.mceliece;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.KeyGenerationParameters;
+
+public class McElieceKeyGenerationParameters
+ extends KeyGenerationParameters
+{
+ private McElieceParameters params;
+
+ public McElieceKeyGenerationParameters(
+ SecureRandom random,
+ McElieceParameters params)
+ {
+ // XXX key size?
+ super(random, 256);
+ this.params = params;
+ }
+
+ public McElieceParameters getParameters()
+ {
+ return params;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKeyPairGenerator.java
new file mode 100644
index 00000000..6ad7fc2f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKeyPairGenerator.java
@@ -0,0 +1,151 @@
+package org.bouncycastle.pqc.crypto.mceliece;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.pqc.math.linearalgebra.GF2Matrix;
+import org.bouncycastle.pqc.math.linearalgebra.GF2mField;
+import org.bouncycastle.pqc.math.linearalgebra.GoppaCode;
+import org.bouncycastle.pqc.math.linearalgebra.GoppaCode.MaMaPe;
+import org.bouncycastle.pqc.math.linearalgebra.Permutation;
+import org.bouncycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
+import org.bouncycastle.pqc.math.linearalgebra.PolynomialRingGF2m;
+
+
+/**
+ * This class implements key pair generation of the McEliece Public Key
+ * Cryptosystem (McEliecePKC).
+ */
+public class McElieceKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+
+
+ public McElieceKeyPairGenerator()
+ {
+
+ }
+
+
+ /**
+ * The OID of the algorithm.
+ */
+ private static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.1";
+
+ private McElieceKeyGenerationParameters mcElieceParams;
+
+ // the extension degree of the finite field GF(2^m)
+ private int m;
+
+ // the length of the code
+ private int n;
+
+ // the error correction capability
+ private int t;
+
+ // the field polynomial
+ private int fieldPoly;
+
+ // the source of randomness
+ private SecureRandom random;
+
+ // flag indicating whether the key pair generator has been initialized
+ private boolean initialized = false;
+
+
+ /**
+ * Default initialization of the key pair generator.
+ */
+ private void initializeDefault()
+ {
+ McElieceKeyGenerationParameters mcParams = new McElieceKeyGenerationParameters(new SecureRandom(), new McElieceParameters());
+ initialize(mcParams);
+ }
+
+ private void initialize(
+ KeyGenerationParameters param)
+ {
+ this.mcElieceParams = (McElieceKeyGenerationParameters)param;
+
+ // set source of randomness
+ this.random = new SecureRandom();
+
+ this.m = this.mcElieceParams.getParameters().getM();
+ this.n = this.mcElieceParams.getParameters().getN();
+ this.t = this.mcElieceParams.getParameters().getT();
+ this.fieldPoly = this.mcElieceParams.getParameters().getFieldPoly();
+ this.initialized = true;
+ }
+
+
+ private AsymmetricCipherKeyPair genKeyPair()
+ {
+
+ if (!initialized)
+ {
+ initializeDefault();
+ }
+
+ // finite field GF(2^m)
+ GF2mField field = new GF2mField(m, fieldPoly);
+
+ // irreducible Goppa polynomial
+ PolynomialGF2mSmallM gp = new PolynomialGF2mSmallM(field, t,
+ PolynomialGF2mSmallM.RANDOM_IRREDUCIBLE_POLYNOMIAL, random);
+ PolynomialRingGF2m ring = new PolynomialRingGF2m(field, gp);
+
+ // matrix used to compute square roots in (GF(2^m))^t
+ PolynomialGF2mSmallM[] sqRootMatrix = ring.getSquareRootMatrix();
+
+ // generate canonical check matrix
+ GF2Matrix h = GoppaCode.createCanonicalCheckMatrix(field, gp);
+
+ // compute short systematic form of check matrix
+ MaMaPe mmp = GoppaCode.computeSystematicForm(h, random);
+ GF2Matrix shortH = mmp.getSecondMatrix();
+ Permutation p1 = mmp.getPermutation();
+
+ // compute short systematic form of generator matrix
+ GF2Matrix shortG = (GF2Matrix)shortH.computeTranspose();
+
+ // extend to full systematic form
+ GF2Matrix gPrime = shortG.extendLeftCompactForm();
+
+ // obtain number of rows of G (= dimension of the code)
+ int k = shortG.getNumRows();
+
+ // generate random invertible (k x k)-matrix S and its inverse S^-1
+ GF2Matrix[] matrixSandInverse = GF2Matrix
+ .createRandomRegularMatrixAndItsInverse(k, random);
+
+ // generate random permutation P2
+ Permutation p2 = new Permutation(n, random);
+
+ // compute public matrix G=S*G'*P2
+ GF2Matrix g = (GF2Matrix)matrixSandInverse[0].rightMultiply(gPrime);
+ g = (GF2Matrix)g.rightMultiply(p2);
+
+
+ // generate keys
+ McEliecePublicKeyParameters pubKey = new McEliecePublicKeyParameters(OID, n, t, g, mcElieceParams.getParameters());
+ McEliecePrivateKeyParameters privKey = new McEliecePrivateKeyParameters(OID, n, k,
+ field, gp, matrixSandInverse[1], p1, p2, h, sqRootMatrix, mcElieceParams.getParameters());
+
+ // return key pair
+ return new AsymmetricCipherKeyPair(pubKey, privKey);
+ }
+
+ public void init(KeyGenerationParameters param)
+ {
+ this.initialize(param);
+
+ }
+
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ return genKeyPair();
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKeyParameters.java
new file mode 100644
index 00000000..007e743e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKeyParameters.java
@@ -0,0 +1,25 @@
+package org.bouncycastle.pqc.crypto.mceliece;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+
+
+public class McElieceKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private McElieceParameters params;
+
+ public McElieceKeyParameters(
+ boolean isPrivate,
+ McElieceParameters params)
+ {
+ super(isPrivate);
+ this.params = params;
+ }
+
+
+ public McElieceParameters getParameters()
+ {
+ return params;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKobaraImaiCipher.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKobaraImaiCipher.java
new file mode 100644
index 00000000..fe3ebf94
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKobaraImaiCipher.java
@@ -0,0 +1,319 @@
+package org.bouncycastle.pqc.crypto.mceliece;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.prng.DigestRandomGenerator;
+import org.bouncycastle.pqc.crypto.MessageEncryptor;
+import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
+import org.bouncycastle.pqc.math.linearalgebra.GF2Vector;
+import org.bouncycastle.pqc.math.linearalgebra.IntegerFunctions;
+
+/**
+ * This class implements the Kobara/Imai conversion of the McEliecePKCS. This is
+ * a conversion of the McEliecePKCS which is CCA2-secure. For details, see D.
+ * Engelbert, R. Overbeck, A. Schmidt, "A summary of the development of the
+ * McEliece Cryptosystem", technical report.
+ */
+public class McElieceKobaraImaiCipher
+ implements MessageEncryptor
+{
+
+ /**
+ * The OID of the algorithm.
+ */
+ public static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.2.3";
+
+ private static final String DEFAULT_PRNG_NAME = "SHA1PRNG";
+
+ /**
+ * A predetermined public constant.
+ */
+ public static final byte[] PUBLIC_CONSTANT = "a predetermined public constant"
+ .getBytes();
+
+
+ private Digest messDigest;
+
+ private SecureRandom sr;
+
+ McElieceCCA2KeyParameters key;
+
+ /**
+ * The McEliece main parameters
+ */
+ private int n, k, t;
+
+
+ public void init(boolean forSigning,
+ CipherParameters param)
+ {
+
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.sr = rParam.getRandom();
+ this.key = (McElieceCCA2PublicKeyParameters)rParam.getParameters();
+ this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
+
+ }
+ else
+ {
+ this.sr = new SecureRandom();
+ this.key = (McElieceCCA2PublicKeyParameters)param;
+ this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
+ }
+ }
+ else
+ {
+ this.key = (McElieceCCA2PrivateKeyParameters)param;
+ this.initCipherDecrypt((McElieceCCA2PrivateKeyParameters)key);
+ }
+
+ }
+
+ /**
+ * Return the key size of the given key object.
+ *
+ * @param key the McElieceCCA2KeyParameters object
+ * @return the key size of the given key object
+ */
+ public int getKeySize(McElieceCCA2KeyParameters key)
+ {
+ if (key instanceof McElieceCCA2PublicKeyParameters)
+ {
+ return ((McElieceCCA2PublicKeyParameters)key).getN();
+
+ }
+ if (key instanceof McElieceCCA2PrivateKeyParameters)
+ {
+ return ((McElieceCCA2PrivateKeyParameters)key).getN();
+ }
+ throw new IllegalArgumentException("unsupported type");
+ }
+
+ private void initCipherEncrypt(McElieceCCA2PublicKeyParameters pubKey)
+ {
+ this.messDigest = pubKey.getParameters().getDigest();
+ n = pubKey.getN();
+ k = pubKey.getK();
+ t = pubKey.getT();
+
+ }
+
+ public void initCipherDecrypt(McElieceCCA2PrivateKeyParameters privKey)
+ {
+ this.messDigest = privKey.getParameters().getDigest();
+ n = privKey.getN();
+ k = privKey.getK();
+ t = privKey.getT();
+ }
+
+ public byte[] messageEncrypt(byte[] input)
+ throws Exception
+ {
+
+ int c2Len = messDigest.getDigestSize();
+ int c4Len = k >> 3;
+ int c5Len = (IntegerFunctions.binomial(n, t).bitLength() - 1) >> 3;
+
+
+ int mLen = c4Len + c5Len - c2Len - PUBLIC_CONSTANT.length;
+ if (input.length > mLen)
+ {
+ mLen = input.length;
+ }
+
+ int c1Len = mLen + PUBLIC_CONSTANT.length;
+ int c6Len = c1Len + c2Len - c4Len - c5Len;
+
+ // compute (m||const)
+ byte[] mConst = new byte[c1Len];
+ System.arraycopy(input, 0, mConst, 0, input.length);
+ System.arraycopy(PUBLIC_CONSTANT, 0, mConst, mLen,
+ PUBLIC_CONSTANT.length);
+
+ // generate random r of length c2Len bytes
+ byte[] r = new byte[c2Len];
+ sr.nextBytes(r);
+
+ // get PRNG object
+ // get PRNG object
+ DigestRandomGenerator sr0 = new DigestRandomGenerator(new SHA1Digest());
+
+ // seed PRNG with r'
+ sr0.addSeedMaterial(r);
+
+ // generate random sequence ...
+ byte[] c1 = new byte[c1Len];
+ sr0.nextBytes(c1);
+
+ // ... and XOR with (m||const) to obtain c1
+ for (int i = c1Len - 1; i >= 0; i--)
+ {
+ c1[i] ^= mConst[i];
+ }
+
+ // compute H(c1) ...
+ byte[] c2 = new byte[messDigest.getDigestSize()];
+ messDigest.update(c1, 0, c1.length);
+ messDigest.doFinal(c2, 0);
+
+ // ... and XOR with r
+ for (int i = c2Len - 1; i >= 0; i--)
+ {
+ c2[i] ^= r[i];
+ }
+
+ // compute (c2||c1)
+ byte[] c2c1 = ByteUtils.concatenate(c2, c1);
+
+ // split (c2||c1) into (c6||c5||c4), where c4Len is k/8 bytes, c5Len is
+ // floor[log(n|t)]/8 bytes, and c6Len is c1Len+c2Len-c4Len-c5Len (may be
+ // 0).
+ byte[] c6 = new byte[0];
+ if (c6Len > 0)
+ {
+ c6 = new byte[c6Len];
+ System.arraycopy(c2c1, 0, c6, 0, c6Len);
+ }
+
+ byte[] c5 = new byte[c5Len];
+ System.arraycopy(c2c1, c6Len, c5, 0, c5Len);
+
+ byte[] c4 = new byte[c4Len];
+ System.arraycopy(c2c1, c6Len + c5Len, c4, 0, c4Len);
+
+ // convert c4 to vector over GF(2)
+ GF2Vector c4Vec = GF2Vector.OS2VP(k, c4);
+
+ // convert c5 to error vector z
+ GF2Vector z = Conversions.encode(n, t, c5);
+
+ // compute encC4 = E(c4, z)
+ byte[] encC4 = McElieceCCA2Primitives.encryptionPrimitive((McElieceCCA2PublicKeyParameters)key,
+ c4Vec, z).getEncoded();
+
+ // if c6Len > 0
+ if (c6Len > 0)
+ {
+ // return (c6||encC4)
+ return ByteUtils.concatenate(c6, encC4);
+ }
+ // else, return encC4
+ return encC4;
+ }
+
+
+ public byte[] messageDecrypt(byte[] input)
+ throws Exception
+ {
+
+ int nDiv8 = n >> 3;
+
+ if (input.length < nDiv8)
+ {
+ throw new Exception("Bad Padding: Ciphertext too short.");
+ }
+
+ int c2Len = messDigest.getDigestSize();
+ int c4Len = k >> 3;
+ int c6Len = input.length - nDiv8;
+
+ // split cipher text (c6||encC4), where c6 may be empty
+ byte[] c6, encC4;
+ if (c6Len > 0)
+ {
+ byte[][] c6EncC4 = ByteUtils.split(input, c6Len);
+ c6 = c6EncC4[0];
+ encC4 = c6EncC4[1];
+ }
+ else
+ {
+ c6 = new byte[0];
+ encC4 = input;
+ }
+
+ // convert encC4 into vector over GF(2)
+ GF2Vector encC4Vec = GF2Vector.OS2VP(n, encC4);
+
+ // decrypt encC4Vec to obtain c4 and error vector z
+ GF2Vector[] c4z = McElieceCCA2Primitives.decryptionPrimitive((McElieceCCA2PrivateKeyParameters)key,
+ encC4Vec);
+ byte[] c4 = c4z[0].getEncoded();
+ GF2Vector z = c4z[1];
+
+ // if length of c4 is greater than c4Len (because of padding) ...
+ if (c4.length > c4Len)
+ {
+ // ... truncate the padding bytes
+ c4 = ByteUtils.subArray(c4, 0, c4Len);
+ }
+
+ // compute c5 = Conv^-1(z)
+ byte[] c5 = Conversions.decode(n, t, z);
+
+ // compute (c6||c5||c4)
+ byte[] c6c5c4 = ByteUtils.concatenate(c6, c5);
+ c6c5c4 = ByteUtils.concatenate(c6c5c4, c4);
+
+ // split (c6||c5||c4) into (c2||c1), where c2Len = mdLen and c1Len =
+ // input.length-c2Len bytes.
+ int c1Len = c6c5c4.length - c2Len;
+ byte[][] c2c1 = ByteUtils.split(c6c5c4, c2Len);
+ byte[] c2 = c2c1[0];
+ byte[] c1 = c2c1[1];
+
+ // compute H(c1) ...
+ byte[] rPrime = new byte[messDigest.getDigestSize()];
+ messDigest.update(c1, 0, c1.length);
+ messDigest.doFinal(rPrime, 0);
+
+ // ... and XOR with c2 to obtain r'
+ for (int i = c2Len - 1; i >= 0; i--)
+ {
+ rPrime[i] ^= c2[i];
+ }
+
+ // get PRNG object
+ DigestRandomGenerator sr0 = new DigestRandomGenerator(new SHA1Digest());
+
+ // seed PRNG with r'
+ sr0.addSeedMaterial(rPrime);
+
+ // generate random sequence R(r') ...
+ byte[] mConstPrime = new byte[c1Len];
+ sr0.nextBytes(mConstPrime);
+
+ // ... and XOR with c1 to obtain (m||const')
+ for (int i = c1Len - 1; i >= 0; i--)
+ {
+ mConstPrime[i] ^= c1[i];
+ }
+
+ if (mConstPrime.length < c1Len)
+ {
+ throw new Exception("Bad Padding: invalid ciphertext");
+ }
+
+ byte[][] temp = ByteUtils.split(mConstPrime, c1Len
+ - PUBLIC_CONSTANT.length);
+ byte[] mr = temp[0];
+ byte[] constPrime = temp[1];
+
+ if (!ByteUtils.equals(constPrime, PUBLIC_CONSTANT))
+ {
+ throw new Exception("Bad Padding: invalid ciphertext");
+ }
+
+ return mr;
+ }
+
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKobaraImaiDigestCipher.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKobaraImaiDigestCipher.java
new file mode 100644
index 00000000..365f387c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKobaraImaiDigestCipher.java
@@ -0,0 +1,128 @@
+package org.bouncycastle.pqc.crypto.mceliece;
+
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.pqc.crypto.MessageEncryptor;
+
+// TODO should implement some interface?
+public class McElieceKobaraImaiDigestCipher
+{
+
+ private final Digest messDigest;
+
+ private final MessageEncryptor mcElieceCCA2Cipher;
+
+ private boolean forEncrypting;
+
+
+ public McElieceKobaraImaiDigestCipher(MessageEncryptor mcElieceCCA2Cipher, Digest messDigest)
+ {
+ this.mcElieceCCA2Cipher = mcElieceCCA2Cipher;
+ this.messDigest = messDigest;
+ }
+
+
+ public void init(boolean forEncrypting,
+ CipherParameters param)
+ {
+
+ this.forEncrypting = forEncrypting;
+ AsymmetricKeyParameter k;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ k = (AsymmetricKeyParameter)((ParametersWithRandom)param).getParameters();
+ }
+ else
+ {
+ k = (AsymmetricKeyParameter)param;
+ }
+
+ if (forEncrypting && k.isPrivate())
+ {
+ throw new IllegalArgumentException("Encrypting Requires Public Key.");
+ }
+
+ if (!forEncrypting && !k.isPrivate())
+ {
+ throw new IllegalArgumentException("Decrypting Requires Private Key.");
+ }
+
+ reset();
+
+ mcElieceCCA2Cipher.init(forEncrypting, param);
+ }
+
+
+ public byte[] messageEncrypt()
+ {
+ if (!forEncrypting)
+ {
+ throw new IllegalStateException("McElieceKobaraImaiDigestCipher not initialised for encrypting.");
+ }
+
+ byte[] hash = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hash, 0);
+ byte[] enc = null;
+
+ try
+ {
+ enc = mcElieceCCA2Cipher.messageEncrypt(hash);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return enc;
+ }
+
+
+ public byte[] messageDecrypt(byte[] ciphertext)
+ {
+ byte[] output = null;
+ if (forEncrypting)
+ {
+ throw new IllegalStateException("McElieceKobaraImaiDigestCipher not initialised for decrypting.");
+ }
+
+
+ try
+ {
+ output = mcElieceCCA2Cipher.messageDecrypt(ciphertext);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return output;
+ }
+
+
+ public void update(byte b)
+ {
+ messDigest.update(b);
+
+ }
+
+ public void update(byte[] in, int off, int len)
+ {
+ messDigest.update(in, off, len);
+
+ }
+
+
+ public void reset()
+ {
+ messDigest.reset();
+
+ }
+
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePKCSCipher.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePKCSCipher.java
new file mode 100644
index 00000000..7a6be1be
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePKCSCipher.java
@@ -0,0 +1,224 @@
+package org.bouncycastle.pqc.crypto.mceliece;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.pqc.crypto.MessageEncryptor;
+import org.bouncycastle.pqc.math.linearalgebra.GF2Matrix;
+import org.bouncycastle.pqc.math.linearalgebra.GF2Vector;
+import org.bouncycastle.pqc.math.linearalgebra.GF2mField;
+import org.bouncycastle.pqc.math.linearalgebra.GoppaCode;
+import org.bouncycastle.pqc.math.linearalgebra.Permutation;
+import org.bouncycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
+import org.bouncycastle.pqc.math.linearalgebra.Vector;
+
+/**
+ * This class implements the McEliece Public Key cryptosystem (McEliecePKCS). It
+ * was first described in R.J. McEliece, "A public key cryptosystem based on
+ * algebraic coding theory", DSN progress report, 42-44:114-116, 1978. The
+ * McEliecePKCS is the first cryptosystem which is based on error correcting
+ * codes. The trapdoor for the McEliece cryptosystem using Goppa codes is the
+ * knowledge of the Goppa polynomial used to generate the code.
+ */
+public class McEliecePKCSCipher
+ implements MessageEncryptor
+{
+
+ /**
+ * The OID of the algorithm.
+ */
+ public static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.1";
+
+
+ // the source of randomness
+ private SecureRandom sr;
+
+ // the McEliece main parameters
+ private int n, k, t;
+
+ // The maximum number of bytes the cipher can decrypt
+ public int maxPlainTextSize;
+
+ // The maximum number of bytes the cipher can encrypt
+ public int cipherTextSize;
+
+ McElieceKeyParameters key;
+
+
+ public void init(boolean forSigning,
+ CipherParameters param)
+ {
+
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.sr = rParam.getRandom();
+ this.key = (McEliecePublicKeyParameters)rParam.getParameters();
+ this.initCipherEncrypt((McEliecePublicKeyParameters)key);
+
+ }
+ else
+ {
+ this.sr = new SecureRandom();
+ this.key = (McEliecePublicKeyParameters)param;
+ this.initCipherEncrypt((McEliecePublicKeyParameters)key);
+ }
+ }
+ else
+ {
+ this.key = (McEliecePrivateKeyParameters)param;
+ this.initCipherDecrypt((McEliecePrivateKeyParameters)key);
+ }
+
+ }
+
+
+ /**
+ * Return the key size of the given key object.
+ *
+ * @param key the McElieceKeyParameters object
+ * @return the keysize of the given key object
+ */
+
+ public int getKeySize(McElieceKeyParameters key)
+ {
+
+ if (key instanceof McEliecePublicKeyParameters)
+ {
+ return ((McEliecePublicKeyParameters)key).getN();
+
+ }
+ if (key instanceof McEliecePrivateKeyParameters)
+ {
+ return ((McEliecePrivateKeyParameters)key).getN();
+ }
+ throw new IllegalArgumentException("unsupported type");
+
+ }
+
+
+ public void initCipherEncrypt(McEliecePublicKeyParameters pubKey)
+ {
+ this.sr = sr != null ? sr : new SecureRandom();
+ n = pubKey.getN();
+ k = pubKey.getK();
+ t = pubKey.getT();
+ cipherTextSize = n >> 3;
+ maxPlainTextSize = (k >> 3);
+ }
+
+
+ public void initCipherDecrypt(McEliecePrivateKeyParameters privKey)
+ {
+ n = privKey.getN();
+ k = privKey.getK();
+
+ maxPlainTextSize = (k >> 3);
+ cipherTextSize = n >> 3;
+ }
+
+ /**
+ * Encrypt a plain text.
+ *
+ * @param input the plain text
+ * @return the cipher text
+ */
+ public byte[] messageEncrypt(byte[] input)
+ {
+ GF2Vector m = computeMessageRepresentative(input);
+ GF2Vector z = new GF2Vector(n, t, sr);
+
+ GF2Matrix g = ((McEliecePublicKeyParameters)key).getG();
+ Vector mG = g.leftMultiply(m);
+ GF2Vector mGZ = (GF2Vector)mG.add(z);
+
+ return mGZ.getEncoded();
+ }
+
+ private GF2Vector computeMessageRepresentative(byte[] input)
+ {
+ byte[] data = new byte[maxPlainTextSize + ((k & 0x07) != 0 ? 1 : 0)];
+ System.arraycopy(input, 0, data, 0, input.length);
+ data[input.length] = 0x01;
+ return GF2Vector.OS2VP(k, data);
+ }
+
+ /**
+ * Decrypt a cipher text.
+ *
+ * @param input the cipher text
+ * @return the plain text
+ * @throws Exception if the cipher text is invalid.
+ */
+ public byte[] messageDecrypt(byte[] input)
+ throws Exception
+ {
+ GF2Vector vec = GF2Vector.OS2VP(n, input);
+ McEliecePrivateKeyParameters privKey = (McEliecePrivateKeyParameters)key;
+ GF2mField field = privKey.getField();
+ PolynomialGF2mSmallM gp = privKey.getGoppaPoly();
+ GF2Matrix sInv = privKey.getSInv();
+ Permutation p1 = privKey.getP1();
+ Permutation p2 = privKey.getP2();
+ GF2Matrix h = privKey.getH();
+ PolynomialGF2mSmallM[] qInv = privKey.getQInv();
+
+ // compute permutation P = P1 * P2
+ Permutation p = p1.rightMultiply(p2);
+
+ // compute P^-1
+ Permutation pInv = p.computeInverse();
+
+ // compute c P^-1
+ GF2Vector cPInv = (GF2Vector)vec.multiply(pInv);
+
+ // compute syndrome of c P^-1
+ GF2Vector syndrome = (GF2Vector)h.rightMultiply(cPInv);
+
+ // decode syndrome
+ GF2Vector z = GoppaCode.syndromeDecode(syndrome, field, gp, qInv);
+ GF2Vector mSG = (GF2Vector)cPInv.add(z);
+
+ // multiply codeword with P1 and error vector with P
+ mSG = (GF2Vector)mSG.multiply(p1);
+ z = (GF2Vector)z.multiply(p);
+
+ // extract mS (last k columns of mSG)
+ GF2Vector mS = mSG.extractRightVector(k);
+
+ // compute plaintext vector
+ GF2Vector mVec = (GF2Vector)sInv.leftMultiply(mS);
+
+ // compute and return plaintext
+ return computeMessage(mVec);
+ }
+
+ private byte[] computeMessage(GF2Vector mr)
+ throws Exception
+ {
+ byte[] mrBytes = mr.getEncoded();
+ // find first non-zero byte
+ int index;
+ for (index = mrBytes.length - 1; index >= 0 && mrBytes[index] == 0; index--)
+ {
+ ;
+ }
+
+ // check if padding byte is valid
+ if (mrBytes[index] != 0x01)
+ {
+ throw new Exception("Bad Padding: invalid ciphertext");
+ }
+
+ // extract and return message
+ byte[] mBytes = new byte[index];
+ System.arraycopy(mrBytes, 0, mBytes, 0, index);
+ return mBytes;
+ }
+
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePKCSDigestCipher.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePKCSDigestCipher.java
new file mode 100644
index 00000000..d8e6ba25
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePKCSDigestCipher.java
@@ -0,0 +1,128 @@
+package org.bouncycastle.pqc.crypto.mceliece;
+
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.pqc.crypto.MessageEncryptor;
+
+// TODO should implement some interface?
+public class McEliecePKCSDigestCipher
+{
+
+ private final Digest messDigest;
+
+ private final MessageEncryptor mcElieceCipher;
+
+ private boolean forEncrypting;
+
+
+ public McEliecePKCSDigestCipher(MessageEncryptor mcElieceCipher, Digest messDigest)
+ {
+ this.mcElieceCipher = mcElieceCipher;
+ this.messDigest = messDigest;
+ }
+
+
+ public void init(boolean forEncrypting,
+ CipherParameters param)
+ {
+
+ this.forEncrypting = forEncrypting;
+ AsymmetricKeyParameter k;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ k = (AsymmetricKeyParameter)((ParametersWithRandom)param).getParameters();
+ }
+ else
+ {
+ k = (AsymmetricKeyParameter)param;
+ }
+
+ if (forEncrypting && k.isPrivate())
+ {
+ throw new IllegalArgumentException("Encrypting Requires Public Key.");
+ }
+
+ if (!forEncrypting && !k.isPrivate())
+ {
+ throw new IllegalArgumentException("Decrypting Requires Private Key.");
+ }
+
+ reset();
+
+ mcElieceCipher.init(forEncrypting, param);
+ }
+
+
+ public byte[] messageEncrypt()
+ {
+ if (!forEncrypting)
+ {
+ throw new IllegalStateException("McEliecePKCSDigestCipher not initialised for encrypting.");
+ }
+
+ byte[] hash = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hash, 0);
+ byte[] enc = null;
+
+ try
+ {
+ enc = mcElieceCipher.messageEncrypt(hash);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return enc;
+ }
+
+
+ public byte[] messageDecrypt(byte[] ciphertext)
+ {
+ byte[] output = null;
+ if (forEncrypting)
+ {
+ throw new IllegalStateException("McEliecePKCSDigestCipher not initialised for decrypting.");
+ }
+
+
+ try
+ {
+ output = mcElieceCipher.messageDecrypt(ciphertext);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return output;
+ }
+
+
+ public void update(byte b)
+ {
+ messDigest.update(b);
+
+ }
+
+ public void update(byte[] in, int off, int len)
+ {
+ messDigest.update(in, off, len);
+
+ }
+
+
+ public void reset()
+ {
+ messDigest.reset();
+
+ }
+
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceParameters.java
new file mode 100644
index 00000000..e90c7846
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceParameters.java
@@ -0,0 +1,181 @@
+package org.bouncycastle.pqc.crypto.mceliece;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.pqc.math.linearalgebra.PolynomialRingGF2;
+
+public class McElieceParameters
+ implements CipherParameters
+{
+
+ /**
+ * The default extension degree
+ */
+ public static final int DEFAULT_M = 11;
+
+ /**
+ * The default error correcting capability.
+ */
+ public static final int DEFAULT_T = 50;
+
+ /**
+ * extension degree of the finite field GF(2^m)
+ */
+ private int m;
+
+ /**
+ * error correction capability of the code
+ */
+ private int t;
+
+ /**
+ * length of the code
+ */
+ private int n;
+
+ /**
+ * the field polynomial
+ */
+ private int fieldPoly;
+
+ /**
+ * Constructor. Set the default parameters: extension degree.
+ */
+ public McElieceParameters()
+ {
+ this(DEFAULT_M, DEFAULT_T);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param keysize the length of a Goppa code
+ * @throws IllegalArgumentException if <tt>keysize &lt; 1</tt>.
+ */
+ public McElieceParameters(int keysize)
+ throws IllegalArgumentException
+ {
+ if (keysize < 1)
+ {
+ throw new IllegalArgumentException("key size must be positive");
+ }
+ m = 0;
+ n = 1;
+ while (n < keysize)
+ {
+ n <<= 1;
+ m++;
+ }
+ t = n >>> 1;
+ t /= m;
+ fieldPoly = PolynomialRingGF2.getIrreduciblePolynomial(m);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param m degree of the finite field GF(2^m)
+ * @param t error correction capability of the code
+ * @throws IllegalArgumentException if <tt>m &lt; 1</tt> or <tt>m &gt; 32</tt> or
+ * <tt>t &lt; 0</tt> or <tt>t &gt; n</tt>.
+ */
+ public McElieceParameters(int m, int t)
+ throws IllegalArgumentException
+ {
+ if (m < 1)
+ {
+ throw new IllegalArgumentException("m must be positive");
+ }
+ if (m > 32)
+ {
+ throw new IllegalArgumentException("m is too large");
+ }
+ this.m = m;
+ n = 1 << m;
+ if (t < 0)
+ {
+ throw new IllegalArgumentException("t must be positive");
+ }
+ if (t > n)
+ {
+ throw new IllegalArgumentException("t must be less than n = 2^m");
+ }
+ this.t = t;
+ fieldPoly = PolynomialRingGF2.getIrreduciblePolynomial(m);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param m degree of the finite field GF(2^m)
+ * @param t error correction capability of the code
+ * @param poly the field polynomial
+ * @throws IllegalArgumentException if <tt>m &lt; 1</tt> or <tt>m &gt; 32</tt> or
+ * <tt>t &lt; 0</tt> or <tt>t &gt; n</tt> or
+ * <tt>poly</tt> is not an irreducible field polynomial.
+ */
+ public McElieceParameters(int m, int t, int poly)
+ throws IllegalArgumentException
+ {
+ this.m = m;
+ if (m < 1)
+ {
+ throw new IllegalArgumentException("m must be positive");
+ }
+ if (m > 32)
+ {
+ throw new IllegalArgumentException(" m is too large");
+ }
+ this.n = 1 << m;
+ this.t = t;
+ if (t < 0)
+ {
+ throw new IllegalArgumentException("t must be positive");
+ }
+ if (t > n)
+ {
+ throw new IllegalArgumentException("t must be less than n = 2^m");
+ }
+ if ((PolynomialRingGF2.degree(poly) == m)
+ && (PolynomialRingGF2.isIrreducible(poly)))
+ {
+ this.fieldPoly = poly;
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "polynomial is not a field polynomial for GF(2^m)");
+ }
+ }
+
+ /**
+ * @return the extension degree of the finite field GF(2^m)
+ */
+ public int getM()
+ {
+ return m;
+ }
+
+ /**
+ * @return the length of the code
+ */
+ public int getN()
+ {
+ return n;
+ }
+
+ /**
+ * @return the error correction capability of the code
+ */
+ public int getT()
+ {
+ return t;
+ }
+
+ /**
+ * @return the field polynomial
+ */
+ public int getFieldPoly()
+ {
+ return fieldPoly;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePointchevalCipher.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePointchevalCipher.java
new file mode 100644
index 00000000..854d79e0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePointchevalCipher.java
@@ -0,0 +1,241 @@
+package org.bouncycastle.pqc.crypto.mceliece;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.prng.DigestRandomGenerator;
+import org.bouncycastle.pqc.crypto.MessageEncryptor;
+import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
+import org.bouncycastle.pqc.math.linearalgebra.GF2Vector;
+
+/**
+ * This class implements the Pointcheval conversion of the McEliecePKCS.
+ * Pointcheval presents a generic technique to make a CCA2-secure cryptosystem
+ * from any partially trapdoor one-way function in the random oracle model. For
+ * details, see D. Engelbert, R. Overbeck, A. Schmidt, "A summary of the
+ * development of the McEliece Cryptosystem", technical report.
+ */
+public class McEliecePointchevalCipher
+ implements MessageEncryptor
+{
+
+
+ /**
+ * The OID of the algorithm.
+ */
+ public static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.2.2";
+
+ private Digest messDigest;
+
+ private SecureRandom sr;
+
+ /**
+ * The McEliece main parameters
+ */
+ private int n, k, t;
+
+ McElieceCCA2KeyParameters key;
+
+ public void init(boolean forSigning,
+ CipherParameters param)
+ {
+
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.sr = rParam.getRandom();
+ this.key = (McElieceCCA2PublicKeyParameters)rParam.getParameters();
+ this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
+
+ }
+ else
+ {
+ this.sr = new SecureRandom();
+ this.key = (McElieceCCA2PublicKeyParameters)param;
+ this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
+ }
+ }
+ else
+ {
+ this.key = (McElieceCCA2PrivateKeyParameters)param;
+ this.initCipherDecrypt((McElieceCCA2PrivateKeyParameters)key);
+ }
+
+ }
+
+ /**
+ * Return the key size of the given key object.
+ *
+ * @param key the McElieceCCA2KeyParameters object
+ * @return the key size of the given key object
+ * @throws IllegalArgumentException if the key is invalid
+ */
+ public int getKeySize(McElieceCCA2KeyParameters key)
+ throws IllegalArgumentException
+ {
+
+ if (key instanceof McElieceCCA2PublicKeyParameters)
+ {
+ return ((McElieceCCA2PublicKeyParameters)key).getN();
+
+ }
+ if (key instanceof McElieceCCA2PrivateKeyParameters)
+ {
+ return ((McElieceCCA2PrivateKeyParameters)key).getN();
+ }
+ throw new IllegalArgumentException("unsupported type");
+
+ }
+
+
+ protected int decryptOutputSize(int inLen)
+ {
+ return 0;
+ }
+
+ protected int encryptOutputSize(int inLen)
+ {
+ return 0;
+ }
+
+
+ public void initCipherEncrypt(McElieceCCA2PublicKeyParameters pubKey)
+ {
+ this.sr = sr != null ? sr : new SecureRandom();
+ this.messDigest = pubKey.getParameters().getDigest();
+ n = pubKey.getN();
+ k = pubKey.getK();
+ t = pubKey.getT();
+ }
+
+ public void initCipherDecrypt(McElieceCCA2PrivateKeyParameters privKey)
+ {
+ this.messDigest = privKey.getParameters().getDigest();
+ n = privKey.getN();
+ k = privKey.getK();
+ t = privKey.getT();
+ }
+
+ public byte[] messageEncrypt(byte[] input)
+ throws Exception
+ {
+
+ int kDiv8 = k >> 3;
+
+ // generate random r of length k div 8 bytes
+ byte[] r = new byte[kDiv8];
+ sr.nextBytes(r);
+
+ // generate random vector r' of length k bits
+ GF2Vector rPrime = new GF2Vector(k, sr);
+
+ // convert r' to byte array
+ byte[] rPrimeBytes = rPrime.getEncoded();
+
+ // compute (input||r)
+ byte[] mr = ByteUtils.concatenate(input, r);
+
+ // compute H(input||r)
+ messDigest.update(mr, 0, mr.length);
+ byte[] hmr = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hmr, 0);
+
+
+ // convert H(input||r) to error vector z
+ GF2Vector z = Conversions.encode(n, t, hmr);
+
+ // compute c1 = E(rPrime, z)
+ byte[] c1 = McElieceCCA2Primitives.encryptionPrimitive((McElieceCCA2PublicKeyParameters)key, rPrime,
+ z).getEncoded();
+
+ // get PRNG object
+ DigestRandomGenerator sr0 = new DigestRandomGenerator(new SHA1Digest());
+
+ // seed PRNG with r'
+ sr0.addSeedMaterial(rPrimeBytes);
+
+ // generate random c2
+ byte[] c2 = new byte[input.length + kDiv8];
+ sr0.nextBytes(c2);
+
+ // XOR with input
+ for (int i = 0; i < input.length; i++)
+ {
+ c2[i] ^= input[i];
+ }
+ // XOR with r
+ for (int i = 0; i < kDiv8; i++)
+ {
+ c2[input.length + i] ^= r[i];
+ }
+
+ // return (c1||c2)
+ return ByteUtils.concatenate(c1, c2);
+ }
+
+ public byte[] messageDecrypt(byte[] input)
+ throws Exception
+ {
+
+ int c1Len = (n + 7) >> 3;
+ int c2Len = input.length - c1Len;
+
+ // split cipher text (c1||c2)
+ byte[][] c1c2 = ByteUtils.split(input, c1Len);
+ byte[] c1 = c1c2[0];
+ byte[] c2 = c1c2[1];
+
+ // decrypt c1 ...
+ GF2Vector c1Vec = GF2Vector.OS2VP(n, c1);
+ GF2Vector[] c1Dec = McElieceCCA2Primitives.decryptionPrimitive((McElieceCCA2PrivateKeyParameters)key,
+ c1Vec);
+ byte[] rPrimeBytes = c1Dec[0].getEncoded();
+ // ... and obtain error vector z
+ GF2Vector z = c1Dec[1];
+
+ // get PRNG object
+ DigestRandomGenerator sr0 = new DigestRandomGenerator(new SHA1Digest());
+
+ // seed PRNG with r'
+ sr0.addSeedMaterial(rPrimeBytes);
+
+ // generate random sequence
+ byte[] mrBytes = new byte[c2Len];
+ sr0.nextBytes(mrBytes);
+
+ // XOR with c2 to obtain (m||r)
+ for (int i = 0; i < c2Len; i++)
+ {
+ mrBytes[i] ^= c2[i];
+ }
+
+ // compute H(m||r)
+ messDigest.update(mrBytes, 0, mrBytes.length);
+ byte[] hmr = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hmr, 0);
+
+ // compute Conv(H(m||r))
+ c1Vec = Conversions.encode(n, t, hmr);
+
+ // check that Conv(H(m||r)) = z
+ if (!c1Vec.equals(z))
+ {
+ throw new Exception("Bad Padding: Invalid ciphertext.");
+ }
+
+ // split (m||r) to obtain m
+ int kDiv8 = k >> 3;
+ byte[][] mr = ByteUtils.split(mrBytes, c2Len - kDiv8);
+
+ // return plain text m
+ return mr[0];
+ }
+
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePointchevalDigestCipher.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePointchevalDigestCipher.java
new file mode 100644
index 00000000..8a1ed62a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePointchevalDigestCipher.java
@@ -0,0 +1,128 @@
+package org.bouncycastle.pqc.crypto.mceliece;
+
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.pqc.crypto.MessageEncryptor;
+
+// TODO should implement some interface?
+public class McEliecePointchevalDigestCipher
+{
+
+ private final Digest messDigest;
+
+ private final MessageEncryptor mcElieceCCA2Cipher;
+
+ private boolean forEncrypting;
+
+
+ public McEliecePointchevalDigestCipher(MessageEncryptor mcElieceCCA2Cipher, Digest messDigest)
+ {
+ this.mcElieceCCA2Cipher = mcElieceCCA2Cipher;
+ this.messDigest = messDigest;
+ }
+
+
+ public void init(boolean forEncrypting,
+ CipherParameters param)
+ {
+
+ this.forEncrypting = forEncrypting;
+ AsymmetricKeyParameter k;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ k = (AsymmetricKeyParameter)((ParametersWithRandom)param).getParameters();
+ }
+ else
+ {
+ k = (AsymmetricKeyParameter)param;
+ }
+
+ if (forEncrypting && k.isPrivate())
+ {
+ throw new IllegalArgumentException("Encrypting Requires Public Key.");
+ }
+
+ if (!forEncrypting && !k.isPrivate())
+ {
+ throw new IllegalArgumentException("Decrypting Requires Private Key.");
+ }
+
+ reset();
+
+ mcElieceCCA2Cipher.init(forEncrypting, param);
+ }
+
+
+ public byte[] messageEncrypt()
+ {
+ if (!forEncrypting)
+ {
+ throw new IllegalStateException("McEliecePointchevalDigestCipher not initialised for encrypting.");
+ }
+
+ byte[] hash = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hash, 0);
+ byte[] enc = null;
+
+ try
+ {
+ enc = mcElieceCCA2Cipher.messageEncrypt(hash);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return enc;
+ }
+
+
+ public byte[] messageDecrypt(byte[] ciphertext)
+ {
+ byte[] output = null;
+ if (forEncrypting)
+ {
+ throw new IllegalStateException("McEliecePointchevalDigestCipher not initialised for decrypting.");
+ }
+
+
+ try
+ {
+ output = mcElieceCCA2Cipher.messageDecrypt(ciphertext);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return output;
+ }
+
+
+ public void update(byte b)
+ {
+ messDigest.update(b);
+
+ }
+
+ public void update(byte[] in, int off, int len)
+ {
+ messDigest.update(in, off, len);
+
+ }
+
+
+ public void reset()
+ {
+ messDigest.reset();
+
+ }
+
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePrivateKeyParameters.java
new file mode 100644
index 00000000..762c2a25
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePrivateKeyParameters.java
@@ -0,0 +1,197 @@
+package org.bouncycastle.pqc.crypto.mceliece;
+
+import org.bouncycastle.pqc.math.linearalgebra.GF2Matrix;
+import org.bouncycastle.pqc.math.linearalgebra.GF2mField;
+import org.bouncycastle.pqc.math.linearalgebra.Permutation;
+import org.bouncycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
+
+
+public class McEliecePrivateKeyParameters
+ extends McElieceKeyParameters
+{
+
+ // the OID of the algorithm
+ private String oid;
+
+ // the length of the code
+ private int n;
+
+ // the dimension of the code, where <tt>k &gt;= n - mt</tt>
+ private int k;
+
+ // the underlying finite field
+ private GF2mField field;
+
+ // the irreducible Goppa polynomial
+ private PolynomialGF2mSmallM goppaPoly;
+
+ // a k x k random binary non-singular matrix
+ private GF2Matrix sInv;
+
+ // the permutation used to generate the systematic check matrix
+ private Permutation p1;
+
+ // the permutation used to compute the public generator matrix
+ private Permutation p2;
+
+ // the canonical check matrix of the code
+ private GF2Matrix h;
+
+ // the matrix used to compute square roots in <tt>(GF(2^m))^t</tt>
+ private PolynomialGF2mSmallM[] qInv;
+
+ /**
+ * Constructor.
+ *
+ * @param oid
+ * @param n the length of the code
+ * @param k the dimension of the code
+ * @param field the field polynomial defining the finite field
+ * <tt>GF(2<sup>m</sup>)</tt>
+ * @param goppaPoly the irreducible Goppa polynomial
+ * @param sInv the matrix <tt>S<sup>-1</sup></tt>
+ * @param p1 the permutation used to generate the systematic check
+ * matrix
+ * @param p2 the permutation used to compute the public generator
+ * matrix
+ * @param h the canonical check matrix
+ * @param qInv the matrix used to compute square roots in
+ * <tt>(GF(2<sup>m</sup>))<sup>t</sup></tt>
+ * @param params McElieceParameters
+ */
+ public McEliecePrivateKeyParameters(String oid, int n, int k, GF2mField field,
+ PolynomialGF2mSmallM goppaPoly, GF2Matrix sInv, Permutation p1,
+ Permutation p2, GF2Matrix h, PolynomialGF2mSmallM[] qInv, McElieceParameters params)
+ {
+ super(true, params);
+ this.oid = oid;
+ this.k = k;
+ this.n = n;
+ this.field = field;
+ this.goppaPoly = goppaPoly;
+ this.sInv = sInv;
+ this.p1 = p1;
+ this.p2 = p2;
+ this.h = h;
+ this.qInv = qInv;
+ }
+
+ /**
+ * Constructor (used by the {@link McElieceKeyFactory}).
+ *
+ * @param oid
+ * @param n the length of the code
+ * @param k the dimension of the code
+ * @param encField the encoded field polynomial defining the finite field
+ * <tt>GF(2<sup>m</sup>)</tt>
+ * @param encGoppaPoly the encoded irreducible Goppa polynomial
+ * @param encSInv the encoded matrix <tt>S<sup>-1</sup></tt>
+ * @param encP1 the encoded permutation used to generate the systematic
+ * check matrix
+ * @param encP2 the encoded permutation used to compute the public
+ * generator matrix
+ * @param encH the encoded canonical check matrix
+ * @param encQInv the encoded matrix used to compute square roots in
+ * <tt>(GF(2<sup>m</sup>))<sup>t</sup></tt>
+ * @param params McElieceParameters
+ */
+ public McEliecePrivateKeyParameters(String oid, int n, int k, byte[] encField,
+ byte[] encGoppaPoly, byte[] encSInv, byte[] encP1, byte[] encP2,
+ byte[] encH, byte[][] encQInv, McElieceParameters params)
+ {
+ super(true, params);
+ this.oid = oid;
+ this.n = n;
+ this.k = k;
+ field = new GF2mField(encField);
+ goppaPoly = new PolynomialGF2mSmallM(field, encGoppaPoly);
+ sInv = new GF2Matrix(encSInv);
+ p1 = new Permutation(encP1);
+ p2 = new Permutation(encP2);
+ h = new GF2Matrix(encH);
+ qInv = new PolynomialGF2mSmallM[encQInv.length];
+ for (int i = 0; i < encQInv.length; i++)
+ {
+ qInv[i] = new PolynomialGF2mSmallM(field, encQInv[i]);
+ }
+ }
+
+ /**
+ * @return the length of the code
+ */
+ public int getN()
+ {
+ return n;
+ }
+
+ /**
+ * @return the dimension of the code
+ */
+ public int getK()
+ {
+ return k;
+ }
+
+ /**
+ * @return the finite field <tt>GF(2<sup>m</sup>)</tt>
+ */
+ public GF2mField getField()
+ {
+ return field;
+ }
+
+ /**
+ * @return the irreducible Goppa polynomial
+ */
+ public PolynomialGF2mSmallM getGoppaPoly()
+ {
+ return goppaPoly;
+ }
+
+ /**
+ * @return the k x k random binary non-singular matrix S^-1
+ */
+ public GF2Matrix getSInv()
+ {
+ return sInv;
+ }
+
+ /**
+ * @return the permutation used to generate the systematic check matrix
+ */
+ public Permutation getP1()
+ {
+ return p1;
+ }
+
+ /**
+ * @return the permutation used to compute the public generator matrix
+ */
+ public Permutation getP2()
+ {
+ return p2;
+ }
+
+ /**
+ * @return the canonical check matrix H
+ */
+ public GF2Matrix getH()
+ {
+ return h;
+ }
+
+ /**
+ * @return the matrix used to compute square roots in
+ * <tt>(GF(2<sup>m</sup>))<sup>t</sup></tt>
+ */
+ public PolynomialGF2mSmallM[] getQInv()
+ {
+ return qInv;
+ }
+
+ public String getOIDString()
+ {
+ return oid;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePublicKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePublicKeyParameters.java
new file mode 100644
index 00000000..6059e2e8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePublicKeyParameters.java
@@ -0,0 +1,96 @@
+package org.bouncycastle.pqc.crypto.mceliece;
+
+import org.bouncycastle.pqc.math.linearalgebra.GF2Matrix;
+
+
+public class McEliecePublicKeyParameters
+ extends McElieceKeyParameters
+{
+
+ // the OID of the algorithm
+ private String oid;
+
+ // the length of the code
+ private int n;
+
+ // the error correction capability of the code
+ private int t;
+
+ // the generator matrix
+ private GF2Matrix g;
+
+ /**
+ * Constructor (used by {@link McElieceKeyFactory}).
+ *
+ * @param oid
+ * @param n the length of the code
+ * @param t the error correction capability of the code
+ * @param g the generator matrix
+ * @param params McElieceParameters
+ */
+ public McEliecePublicKeyParameters(String oid, int n, int t, GF2Matrix g, McElieceParameters params)
+ {
+ super(false, params);
+ this.oid = oid;
+ this.n = n;
+ this.t = t;
+ this.g = new GF2Matrix(g);
+ }
+
+ /**
+ * Constructor (used by {@link McElieceKeyFactory}).
+ *
+ * @param oid
+ * @param n the length of the code
+ * @param t the error correction capability of the code
+ * @param encG the encoded generator matrix
+ * @param params McElieceParameters
+ */
+ public McEliecePublicKeyParameters(String oid, int t, int n, byte[] encG, McElieceParameters params)
+ {
+ super(false, params);
+ this.oid = oid;
+ this.n = n;
+ this.t = t;
+ this.g = new GF2Matrix(encG);
+ }
+
+ /**
+ * @return the length of the code
+ */
+ public int getN()
+ {
+ return n;
+ }
+
+ /**
+ * @return the error correction capability of the code
+ */
+ public int getT()
+ {
+ return t;
+ }
+
+ /**
+ * @return the generator matrix
+ */
+ public GF2Matrix getG()
+ {
+ return g;
+ }
+
+ public String getOIDString()
+ {
+ return oid;
+
+ }
+
+ /**
+ * @return the dimension of the code
+ */
+ public int getK()
+ {
+ return g.getNumRows();
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/IndexGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/IndexGenerator.java
new file mode 100644
index 00000000..82974b30
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/IndexGenerator.java
@@ -0,0 +1,239 @@
+package org.bouncycastle.pqc.crypto.ntru;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * An implementation of the Index Generation Function in IEEE P1363.1.
+ */
+public class IndexGenerator
+{
+ private byte[] seed;
+ private int N;
+ private int c;
+ private int minCallsR;
+ private int totLen;
+ private int remLen;
+ private BitString buf;
+ private int counter;
+ private boolean initialized;
+ private Digest hashAlg;
+ private int hLen;
+
+ /**
+ * Constructs a new index generator.
+ *
+ * @param seed a seed of arbitrary length to initialize the index generator with
+ * @param params NtruEncrypt parameters
+ */
+ IndexGenerator(byte[] seed, NTRUEncryptionParameters params)
+ {
+ this.seed = seed;
+ N = params.N;
+ c = params.c;
+ minCallsR = params.minCallsR;
+
+ totLen = 0;
+ remLen = 0;
+ counter = 0;
+ hashAlg = params.hashAlg;
+
+ hLen = hashAlg.getDigestSize(); // hash length
+ initialized = false;
+ }
+
+ /**
+ * Returns a number <code>i</code> such that <code>0 &lt;= i &lt; N</code>.
+ *
+ * @return
+ */
+ int nextIndex()
+ {
+ if (!initialized)
+ {
+ buf = new BitString();
+ byte[] hash = new byte[hashAlg.getDigestSize()];
+ while (counter < minCallsR)
+ {
+ appendHash(buf, hash);
+ counter++;
+ }
+ totLen = minCallsR * 8 * hLen;
+ remLen = totLen;
+ initialized = true;
+ }
+
+ while (true)
+ {
+ totLen += c;
+ BitString M = buf.getTrailing(remLen);
+ if (remLen < c)
+ {
+ int tmpLen = c - remLen;
+ int cThreshold = counter + (tmpLen + hLen - 1) / hLen;
+ byte[] hash = new byte[hashAlg.getDigestSize()];
+ while (counter < cThreshold)
+ {
+ appendHash(M, hash);
+ counter++;
+ if (tmpLen > 8 * hLen)
+ {
+ tmpLen -= 8 * hLen;
+ }
+ }
+ remLen = 8 * hLen - tmpLen;
+ buf = new BitString();
+ buf.appendBits(hash);
+ }
+ else
+ {
+ remLen -= c;
+ }
+
+ int i = M.getLeadingAsInt(c); // assume c<32
+ if (i < (1 << c) - ((1 << c) % N))
+ {
+ return i % N;
+ }
+ }
+ }
+
+ private void appendHash(BitString m, byte[] hash)
+ {
+ hashAlg.update(seed, 0, seed.length);
+
+ putInt(hashAlg, counter);
+
+ hashAlg.doFinal(hash, 0);
+
+ m.appendBits(hash);
+ }
+
+ private void putInt(Digest hashAlg, int counter)
+ {
+ hashAlg.update((byte)(counter >> 24));
+ hashAlg.update((byte)(counter >> 16));
+ hashAlg.update((byte)(counter >> 8));
+ hashAlg.update((byte)counter);
+ }
+
+ /**
+ * Represents a string of bits and supports appending, reading the head, and reading the tail.
+ */
+ public static class BitString
+ {
+ byte[] bytes = new byte[4];
+ int numBytes; // includes the last byte even if only some of its bits are used
+ int lastByteBits; // lastByteBits <= 8
+
+ /**
+ * Appends all bits in a byte array to the end of the bit string.
+ *
+ * @param bytes a byte array
+ */
+ void appendBits(byte[] bytes)
+ {
+ for (int i = 0; i != bytes.length; i++)
+ {
+ appendBits(bytes[i]);
+ }
+ }
+
+ /**
+ * Appends all bits in a byte to the end of the bit string.
+ *
+ * @param b a byte
+ */
+ public void appendBits(byte b)
+ {
+ if (numBytes == bytes.length)
+ {
+ bytes = copyOf(bytes, 2 * bytes.length);
+ }
+
+ if (numBytes == 0)
+ {
+ numBytes = 1;
+ bytes[0] = b;
+ lastByteBits = 8;
+ }
+ else if (lastByteBits == 8)
+ {
+ bytes[numBytes++] = b;
+ }
+ else
+ {
+ int s = 8 - lastByteBits;
+ bytes[numBytes - 1] |= (b & 0xFF) << lastByteBits;
+ bytes[numBytes++] = (byte)((b & 0xFF) >> s);
+ }
+ }
+
+ /**
+ * Returns the last <code>numBits</code> bits from the end of the bit string.
+ *
+ * @param numBits number of bits
+ * @return a new <code>BitString</code> of length <code>numBits</code>
+ */
+ public BitString getTrailing(int numBits)
+ {
+ BitString newStr = new BitString();
+ newStr.numBytes = (numBits + 7) / 8;
+ newStr.bytes = new byte[newStr.numBytes];
+ for (int i = 0; i < newStr.numBytes; i++)
+ {
+ newStr.bytes[i] = bytes[i];
+ }
+
+ newStr.lastByteBits = numBits % 8;
+ if (newStr.lastByteBits == 0)
+ {
+ newStr.lastByteBits = 8;
+ }
+ else
+ {
+ int s = 32 - newStr.lastByteBits;
+ newStr.bytes[newStr.numBytes - 1] = (byte)(newStr.bytes[newStr.numBytes - 1] << s >>> s);
+ }
+
+ return newStr;
+ }
+
+ /**
+ * Returns up to 32 bits from the beginning of the bit string.
+ *
+ * @param numBits number of bits
+ * @return an <code>int</code> whose lower <code>numBits</code> bits are the beginning of the bit string
+ */
+ public int getLeadingAsInt(int numBits)
+ {
+ int startBit = (numBytes - 1) * 8 + lastByteBits - numBits;
+ int startByte = startBit / 8;
+
+ int startBitInStartByte = startBit % 8;
+ int sum = (bytes[startByte] & 0xFF) >>> startBitInStartByte;
+ int shift = 8 - startBitInStartByte;
+ for (int i = startByte + 1; i < numBytes; i++)
+ {
+ sum |= (bytes[i] & 0xFF) << shift;
+ shift += 8;
+ }
+
+ return sum;
+ }
+
+ public byte[] getBytes()
+ {
+ return Arrays.clone(bytes);
+ }
+ }
+
+ private static byte[] copyOf(byte[] src, int len)
+ {
+ byte[] tmp = new byte[len];
+
+ System.arraycopy(src, 0, tmp, 0, len < src.length ? len : src.length);
+
+ return tmp;
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionKeyGenerationParameters.java
new file mode 100644
index 00000000..d5caa352
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionKeyGenerationParameters.java
@@ -0,0 +1,463 @@
+package org.bouncycastle.pqc.crypto.ntru;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+import java.util.Arrays;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+
+/**
+ * A set of parameters for NtruEncrypt. Several predefined parameter sets are available and new ones can be created as well.
+ */
+public class NTRUEncryptionKeyGenerationParameters
+ extends KeyGenerationParameters
+ implements Cloneable
+{
+ /**
+ * A conservative (in terms of security) parameter set that gives 256 bits of security and is optimized for key size.
+ */
+ public static final NTRUEncryptionKeyGenerationParameters EES1087EP2 = new NTRUEncryptionKeyGenerationParameters(1087, 2048, 120, 120, 256, 13, 25, 14, true, new byte[]{0, 6, 3}, true, false, new SHA512Digest());
+
+ /**
+ * A conservative (in terms of security) parameter set that gives 256 bits of security and is a tradeoff between key size and encryption/decryption speed.
+ */
+ public static final NTRUEncryptionKeyGenerationParameters EES1171EP1 = new NTRUEncryptionKeyGenerationParameters(1171, 2048, 106, 106, 256, 13, 20, 15, true, new byte[]{0, 6, 4}, true, false, new SHA512Digest());
+
+ /**
+ * A conservative (in terms of security) parameter set that gives 256 bits of security and is optimized for encryption/decryption speed.
+ */
+ public static final NTRUEncryptionKeyGenerationParameters EES1499EP1 = new NTRUEncryptionKeyGenerationParameters(1499, 2048, 79, 79, 256, 13, 17, 19, true, new byte[]{0, 6, 5}, true, false, new SHA512Digest());
+
+ /**
+ * A parameter set that gives 128 bits of security and uses simple ternary polynomials.
+ */
+ public static final NTRUEncryptionKeyGenerationParameters APR2011_439 = new NTRUEncryptionKeyGenerationParameters(439, 2048, 146, 130, 128, 9, 32, 9, true, new byte[]{0, 7, 101}, true, false, new SHA256Digest());
+
+ /**
+ * Like <code>APR2011_439</code>, this parameter set gives 128 bits of security but uses product-form polynomials and <code>f=1+pF</code>.
+ */
+ public static final NTRUEncryptionKeyGenerationParameters APR2011_439_FAST = new NTRUEncryptionKeyGenerationParameters(439, 2048, 9, 8, 5, 130, 128, 9, 32, 9, true, new byte[]{0, 7, 101}, true, true, new SHA256Digest());
+
+ /**
+ * A parameter set that gives 256 bits of security and uses simple ternary polynomials.
+ */
+ public static final NTRUEncryptionKeyGenerationParameters APR2011_743 = new NTRUEncryptionKeyGenerationParameters(743, 2048, 248, 220, 256, 10, 27, 14, true, new byte[]{0, 7, 105}, false, false, new SHA512Digest());
+
+ /**
+ * Like <code>APR2011_743</code>, this parameter set gives 256 bits of security but uses product-form polynomials and <code>f=1+pF</code>.
+ */
+ public static final NTRUEncryptionKeyGenerationParameters APR2011_743_FAST = new NTRUEncryptionKeyGenerationParameters(743, 2048, 11, 11, 15, 220, 256, 10, 27, 14, true, new byte[]{0, 7, 105}, false, true, new SHA512Digest());
+
+ public int N, q, df, df1, df2, df3;
+ public int dr;
+ public int dr1;
+ public int dr2;
+ public int dr3;
+ public int dg;
+ int llen;
+ public int maxMsgLenBytes;
+ public int db;
+ public int bufferLenBits;
+ int bufferLenTrits;
+ public int dm0;
+ public int pkLen;
+ public int c;
+ public int minCallsR;
+ public int minCallsMask;
+ public boolean hashSeed;
+ public byte[] oid;
+ public boolean sparse;
+ public boolean fastFp;
+ public int polyType;
+ public Digest hashAlg;
+
+ /**
+ * Constructs a parameter set that uses ternary private keys (i.e. </code>polyType=SIMPLE</code>).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param df number of ones in the private polynomial <code>f</code>
+ * @param dm0 minimum acceptable number of -1's, 0's, and 1's in the polynomial <code>m'</code> in the last encryption step
+ * @param db number of random bits to prepend to the message
+ * @param c a parameter for the Index Generation Function ({@link org.bouncycastle.pqc.crypto.ntru.IndexGenerator})
+ * @param minCallsR minimum number of hash calls for the IGF to make
+ * @param minCallsMask minimum number of calls to generate the masking polynomial
+ * @param hashSeed whether to hash the seed in the MGF first (true) or use the seed directly (false)
+ * @param oid three bytes that uniquely identify the parameter set
+ * @param sparse whether to treat ternary polynomials as sparsely populated ({@link org.bouncycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial} vs {@link org.bouncycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial})
+ * @param fastFp whether <code>f=1+p*F</code> for a ternary <code>F</code> (true) or <code>f</code> is ternary (false)
+ * @param hashAlg a valid identifier for a <code>java.security.MessageDigest</code> instance such as <code>SHA-256</code>. The <code>MessageDigest</code> must support the <code>getDigestLength()</code> method.
+ */
+ public NTRUEncryptionKeyGenerationParameters(int N, int q, int df, int dm0, int db, int c, int minCallsR, int minCallsMask, boolean hashSeed, byte[] oid, boolean sparse, boolean fastFp, Digest hashAlg)
+ {
+ super(new SecureRandom(), db);
+ this.N = N;
+ this.q = q;
+ this.df = df;
+ this.db = db;
+ this.dm0 = dm0;
+ this.c = c;
+ this.minCallsR = minCallsR;
+ this.minCallsMask = minCallsMask;
+ this.hashSeed = hashSeed;
+ this.oid = oid;
+ this.sparse = sparse;
+ this.fastFp = fastFp;
+ this.polyType = NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE;
+ this.hashAlg = hashAlg;
+ init();
+ }
+
+ /**
+ * Constructs a parameter set that uses product-form private keys (i.e. </code>polyType=PRODUCT</code>).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param df1 number of ones in the private polynomial <code>f1</code>
+ * @param df2 number of ones in the private polynomial <code>f2</code>
+ * @param df3 number of ones in the private polynomial <code>f3</code>
+ * @param dm0 minimum acceptable number of -1's, 0's, and 1's in the polynomial <code>m'</code> in the last encryption step
+ * @param db number of random bits to prepend to the message
+ * @param c a parameter for the Index Generation Function ({@link org.bouncycastle.pqc.crypto.ntru.IndexGenerator})
+ * @param minCallsR minimum number of hash calls for the IGF to make
+ * @param minCallsMask minimum number of calls to generate the masking polynomial
+ * @param hashSeed whether to hash the seed in the MGF first (true) or use the seed directly (false)
+ * @param oid three bytes that uniquely identify the parameter set
+ * @param sparse whether to treat ternary polynomials as sparsely populated ({@link org.bouncycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial} vs {@link org.bouncycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial})
+ * @param fastFp whether <code>f=1+p*F</code> for a ternary <code>F</code> (true) or <code>f</code> is ternary (false)
+ * @param hashAlg a valid identifier for a <code>java.security.MessageDigest</code> instance such as <code>SHA-256</code>
+ */
+ public NTRUEncryptionKeyGenerationParameters(int N, int q, int df1, int df2, int df3, int dm0, int db, int c, int minCallsR, int minCallsMask, boolean hashSeed, byte[] oid, boolean sparse, boolean fastFp, Digest hashAlg)
+ {
+ super(new SecureRandom(), db);
+
+ this.N = N;
+ this.q = q;
+ this.df1 = df1;
+ this.df2 = df2;
+ this.df3 = df3;
+ this.db = db;
+ this.dm0 = dm0;
+ this.c = c;
+ this.minCallsR = minCallsR;
+ this.minCallsMask = minCallsMask;
+ this.hashSeed = hashSeed;
+ this.oid = oid;
+ this.sparse = sparse;
+ this.fastFp = fastFp;
+ this.polyType = NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT;
+ this.hashAlg = hashAlg;
+ init();
+ }
+
+ private void init()
+ {
+ dr = df;
+ dr1 = df1;
+ dr2 = df2;
+ dr3 = df3;
+ dg = N / 3;
+ llen = 1; // ceil(log2(maxMsgLenBytes))
+ maxMsgLenBytes = N * 3 / 2 / 8 - llen - db / 8 - 1;
+ bufferLenBits = (N * 3 / 2 + 7) / 8 * 8 + 1;
+ bufferLenTrits = N - 1;
+ pkLen = db;
+ }
+
+ /**
+ * Reads a parameter set from an input stream.
+ *
+ * @param is an input stream
+ * @throws java.io.IOException
+ */
+ public NTRUEncryptionKeyGenerationParameters(InputStream is)
+ throws IOException
+ {
+ super(new SecureRandom(), -1);
+ DataInputStream dis = new DataInputStream(is);
+ N = dis.readInt();
+ q = dis.readInt();
+ df = dis.readInt();
+ df1 = dis.readInt();
+ df2 = dis.readInt();
+ df3 = dis.readInt();
+ db = dis.readInt();
+ dm0 = dis.readInt();
+ c = dis.readInt();
+ minCallsR = dis.readInt();
+ minCallsMask = dis.readInt();
+ hashSeed = dis.readBoolean();
+ oid = new byte[3];
+ dis.read(oid);
+ sparse = dis.readBoolean();
+ fastFp = dis.readBoolean();
+ polyType = dis.read();
+
+ String alg = dis.readUTF();
+
+ if ("SHA-512".equals(alg))
+ {
+ hashAlg = new SHA512Digest();
+ }
+ else if ("SHA-256".equals(alg))
+ {
+ hashAlg = new SHA256Digest();
+ }
+
+ init();
+ }
+
+ public NTRUEncryptionParameters getEncryptionParameters()
+ {
+ if (polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE)
+ {
+ return new NTRUEncryptionParameters(N, q, df, dm0, db, c, minCallsR, minCallsMask, hashSeed, oid, sparse, fastFp, hashAlg);
+ }
+ else
+ {
+ return new NTRUEncryptionParameters(N, q, df1, df2, df3, dm0, db, c, minCallsR, minCallsMask, hashSeed, oid, sparse, fastFp, hashAlg);
+ }
+ }
+
+ public NTRUEncryptionKeyGenerationParameters clone()
+ {
+ if (polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE)
+ {
+ return new NTRUEncryptionKeyGenerationParameters(N, q, df, dm0, db, c, minCallsR, minCallsMask, hashSeed, oid, sparse, fastFp, hashAlg);
+ }
+ else
+ {
+ return new NTRUEncryptionKeyGenerationParameters(N, q, df1, df2, df3, dm0, db, c, minCallsR, minCallsMask, hashSeed, oid, sparse, fastFp, hashAlg);
+ }
+ }
+
+ /**
+ * Returns the maximum length a plaintext message can be with this parameter set.
+ *
+ * @return the maximum length in bytes
+ */
+ public int getMaxMessageLength()
+ {
+ return maxMsgLenBytes;
+ }
+
+ /**
+ * Writes the parameter set to an output stream
+ *
+ * @param os an output stream
+ * @throws java.io.IOException
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ DataOutputStream dos = new DataOutputStream(os);
+ dos.writeInt(N);
+ dos.writeInt(q);
+ dos.writeInt(df);
+ dos.writeInt(df1);
+ dos.writeInt(df2);
+ dos.writeInt(df3);
+ dos.writeInt(db);
+ dos.writeInt(dm0);
+ dos.writeInt(c);
+ dos.writeInt(minCallsR);
+ dos.writeInt(minCallsMask);
+ dos.writeBoolean(hashSeed);
+ dos.write(oid);
+ dos.writeBoolean(sparse);
+ dos.writeBoolean(fastFp);
+ dos.write(polyType);
+ dos.writeUTF(hashAlg.getAlgorithmName());
+ }
+
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + N;
+ result = prime * result + bufferLenBits;
+ result = prime * result + bufferLenTrits;
+ result = prime * result + c;
+ result = prime * result + db;
+ result = prime * result + df;
+ result = prime * result + df1;
+ result = prime * result + df2;
+ result = prime * result + df3;
+ result = prime * result + dg;
+ result = prime * result + dm0;
+ result = prime * result + dr;
+ result = prime * result + dr1;
+ result = prime * result + dr2;
+ result = prime * result + dr3;
+ result = prime * result + (fastFp ? 1231 : 1237);
+ result = prime * result + ((hashAlg == null) ? 0 : hashAlg.getAlgorithmName().hashCode());
+ result = prime * result + (hashSeed ? 1231 : 1237);
+ result = prime * result + llen;
+ result = prime * result + maxMsgLenBytes;
+ result = prime * result + minCallsMask;
+ result = prime * result + minCallsR;
+ result = prime * result + Arrays.hashCode(oid);
+ result = prime * result + pkLen;
+ result = prime * result + polyType;
+ result = prime * result + q;
+ result = prime * result + (sparse ? 1231 : 1237);
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ NTRUEncryptionKeyGenerationParameters other = (NTRUEncryptionKeyGenerationParameters)obj;
+ if (N != other.N)
+ {
+ return false;
+ }
+ if (bufferLenBits != other.bufferLenBits)
+ {
+ return false;
+ }
+ if (bufferLenTrits != other.bufferLenTrits)
+ {
+ return false;
+ }
+ if (c != other.c)
+ {
+ return false;
+ }
+ if (db != other.db)
+ {
+ return false;
+ }
+ if (df != other.df)
+ {
+ return false;
+ }
+ if (df1 != other.df1)
+ {
+ return false;
+ }
+ if (df2 != other.df2)
+ {
+ return false;
+ }
+ if (df3 != other.df3)
+ {
+ return false;
+ }
+ if (dg != other.dg)
+ {
+ return false;
+ }
+ if (dm0 != other.dm0)
+ {
+ return false;
+ }
+ if (dr != other.dr)
+ {
+ return false;
+ }
+ if (dr1 != other.dr1)
+ {
+ return false;
+ }
+ if (dr2 != other.dr2)
+ {
+ return false;
+ }
+ if (dr3 != other.dr3)
+ {
+ return false;
+ }
+ if (fastFp != other.fastFp)
+ {
+ return false;
+ }
+ if (hashAlg == null)
+ {
+ if (other.hashAlg != null)
+ {
+ return false;
+ }
+ }
+ else if (!hashAlg.getAlgorithmName().equals(other.hashAlg.getAlgorithmName()))
+ {
+ return false;
+ }
+ if (hashSeed != other.hashSeed)
+ {
+ return false;
+ }
+ if (llen != other.llen)
+ {
+ return false;
+ }
+ if (maxMsgLenBytes != other.maxMsgLenBytes)
+ {
+ return false;
+ }
+ if (minCallsMask != other.minCallsMask)
+ {
+ return false;
+ }
+ if (minCallsR != other.minCallsR)
+ {
+ return false;
+ }
+ if (!Arrays.equals(oid, other.oid))
+ {
+ return false;
+ }
+ if (pkLen != other.pkLen)
+ {
+ return false;
+ }
+ if (polyType != other.polyType)
+ {
+ return false;
+ }
+ if (q != other.q)
+ {
+ return false;
+ }
+ if (sparse != other.sparse)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public String toString()
+ {
+ StringBuilder output = new StringBuilder("EncryptionParameters(N=" + N + " q=" + q);
+ if (polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE)
+ {
+ output.append(" polyType=SIMPLE df=" + df);
+ }
+ else
+ {
+ output.append(" polyType=PRODUCT df1=" + df1 + " df2=" + df2 + " df3=" + df3);
+ }
+ output.append(" dm0=" + dm0 + " db=" + db + " c=" + c + " minCallsR=" + minCallsR + " minCallsMask=" + minCallsMask +
+ " hashSeed=" + hashSeed + " hashAlg=" + hashAlg + " oid=" + Arrays.toString(oid) + " sparse=" + sparse + ")");
+ return output.toString();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionKeyPairGenerator.java
new file mode 100644
index 00000000..7a648c8f
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionKeyPairGenerator.java
@@ -0,0 +1,113 @@
+package org.bouncycastle.pqc.crypto.ntru;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.Polynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.ProductFormPolynomial;
+import org.bouncycastle.pqc.math.ntru.util.Util;
+
+/**
+ * Generates key pairs.<br/>
+ * The parameter p is hardcoded to 3.
+ */
+public class NTRUEncryptionKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+ private NTRUEncryptionKeyGenerationParameters params;
+
+ /**
+ * Constructs a new instance with a set of encryption parameters.
+ *
+ * @param param encryption parameters
+ */
+ public void init(KeyGenerationParameters param)
+ {
+ this.params = (NTRUEncryptionKeyGenerationParameters)param;
+ }
+
+ /**
+ * Generates a new encryption key pair.
+ *
+ * @return a key pair
+ */
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ int N = params.N;
+ int q = params.q;
+ int df = params.df;
+ int df1 = params.df1;
+ int df2 = params.df2;
+ int df3 = params.df3;
+ int dg = params.dg;
+ boolean fastFp = params.fastFp;
+ boolean sparse = params.sparse;
+
+ Polynomial t;
+ IntegerPolynomial fq;
+ IntegerPolynomial fp = null;
+
+ // choose a random f that is invertible mod 3 and q
+ while (true)
+ {
+ IntegerPolynomial f;
+
+ // choose random t, calculate f and fp
+ if (fastFp)
+ {
+ // if fastFp=true, f is always invertible mod 3
+ t = params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE ? Util.generateRandomTernary(N, df, df, sparse, params.getRandom()) : ProductFormPolynomial.generateRandom(N, df1, df2, df3, df3, params.getRandom());
+ f = t.toIntegerPolynomial();
+ f.mult(3);
+ f.coeffs[0] += 1;
+ }
+ else
+ {
+ t = params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE ? Util.generateRandomTernary(N, df, df - 1, sparse, params.getRandom()) : ProductFormPolynomial.generateRandom(N, df1, df2, df3, df3 - 1, params.getRandom());
+ f = t.toIntegerPolynomial();
+ fp = f.invertF3();
+ if (fp == null)
+ {
+ continue;
+ }
+ }
+
+ fq = f.invertFq(q);
+ if (fq == null)
+ {
+ continue;
+ }
+ break;
+ }
+
+ // if fastFp=true, fp=1
+ if (fastFp)
+ {
+ fp = new IntegerPolynomial(N);
+ fp.coeffs[0] = 1;
+ }
+
+ // choose a random g that is invertible mod q
+ DenseTernaryPolynomial g;
+ while (true)
+ {
+ g = DenseTernaryPolynomial.generateRandom(N, dg, dg - 1, params.getRandom());
+ if (g.invertFq(q) != null)
+ {
+ break;
+ }
+ }
+
+ IntegerPolynomial h = g.mult(fq, q);
+ h.mult3(q);
+ h.ensurePositive(q);
+ g.clear();
+ fq.clear();
+
+ NTRUEncryptionPrivateKeyParameters priv = new NTRUEncryptionPrivateKeyParameters(h, t, fp, params.getEncryptionParameters());
+ NTRUEncryptionPublicKeyParameters pub = new NTRUEncryptionPublicKeyParameters(h, params.getEncryptionParameters());
+ return new AsymmetricCipherKeyPair(pub, priv);
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionKeyParameters.java
new file mode 100644
index 00000000..27a7987c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionKeyParameters.java
@@ -0,0 +1,20 @@
+package org.bouncycastle.pqc.crypto.ntru;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+
+public class NTRUEncryptionKeyParameters
+ extends AsymmetricKeyParameter
+{
+ final protected NTRUEncryptionParameters params;
+
+ public NTRUEncryptionKeyParameters(boolean privateKey, NTRUEncryptionParameters params)
+ {
+ super(privateKey);
+ this.params = params;
+ }
+
+ public NTRUEncryptionParameters getParameters()
+ {
+ return params;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionParameters.java
new file mode 100644
index 00000000..eeb38391
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionParameters.java
@@ -0,0 +1,410 @@
+package org.bouncycastle.pqc.crypto.ntru;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+
+/**
+ * A set of parameters for NtruEncrypt. Several predefined parameter sets are available and new ones can be created as well.
+ */
+public class NTRUEncryptionParameters
+ implements Cloneable
+{
+
+ public int N, q, df, df1, df2, df3;
+ public int dr;
+ public int dr1;
+ public int dr2;
+ public int dr3;
+ public int dg;
+ int llen;
+ public int maxMsgLenBytes;
+ public int db;
+ public int bufferLenBits;
+ int bufferLenTrits;
+ public int dm0;
+ public int pkLen;
+ public int c;
+ public int minCallsR;
+ public int minCallsMask;
+ public boolean hashSeed;
+ public byte[] oid;
+ public boolean sparse;
+ public boolean fastFp;
+ public int polyType;
+ public Digest hashAlg;
+
+ /**
+ * Constructs a parameter set that uses ternary private keys (i.e. </code>polyType=SIMPLE</code>).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param df number of ones in the private polynomial <code>f</code>
+ * @param dm0 minimum acceptable number of -1's, 0's, and 1's in the polynomial <code>m'</code> in the last encryption step
+ * @param db number of random bits to prepend to the message
+ * @param c a parameter for the Index Generation Function ({@link org.bouncycastle.pqc.crypto.ntru.IndexGenerator})
+ * @param minCallsR minimum number of hash calls for the IGF to make
+ * @param minCallsMask minimum number of calls to generate the masking polynomial
+ * @param hashSeed whether to hash the seed in the MGF first (true) or use the seed directly (false)
+ * @param oid three bytes that uniquely identify the parameter set
+ * @param sparse whether to treat ternary polynomials as sparsely populated ({@link org.bouncycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial} vs {@link org.bouncycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial})
+ * @param fastFp whether <code>f=1+p*F</code> for a ternary <code>F</code> (true) or <code>f</code> is ternary (false)
+ * @param hashAlg a valid identifier for a <code>java.security.MessageDigest</code> instance such as <code>SHA-256</code>. The <code>MessageDigest</code> must support the <code>getDigestLength()</code> method.
+ */
+ public NTRUEncryptionParameters(int N, int q, int df, int dm0, int db, int c, int minCallsR, int minCallsMask, boolean hashSeed, byte[] oid, boolean sparse, boolean fastFp, Digest hashAlg)
+ {
+ this.N = N;
+ this.q = q;
+ this.df = df;
+ this.db = db;
+ this.dm0 = dm0;
+ this.c = c;
+ this.minCallsR = minCallsR;
+ this.minCallsMask = minCallsMask;
+ this.hashSeed = hashSeed;
+ this.oid = oid;
+ this.sparse = sparse;
+ this.fastFp = fastFp;
+ this.polyType = NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE;
+ this.hashAlg = hashAlg;
+ init();
+ }
+
+ /**
+ * Constructs a parameter set that uses product-form private keys (i.e. </code>polyType=PRODUCT</code>).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param df1 number of ones in the private polynomial <code>f1</code>
+ * @param df2 number of ones in the private polynomial <code>f2</code>
+ * @param df3 number of ones in the private polynomial <code>f3</code>
+ * @param dm0 minimum acceptable number of -1's, 0's, and 1's in the polynomial <code>m'</code> in the last encryption step
+ * @param db number of random bits to prepend to the message
+ * @param c a parameter for the Index Generation Function ({@link org.bouncycastle.pqc.crypto.ntru.IndexGenerator})
+ * @param minCallsR minimum number of hash calls for the IGF to make
+ * @param minCallsMask minimum number of calls to generate the masking polynomial
+ * @param hashSeed whether to hash the seed in the MGF first (true) or use the seed directly (false)
+ * @param oid three bytes that uniquely identify the parameter set
+ * @param sparse whether to treat ternary polynomials as sparsely populated ({@link org.bouncycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial} vs {@link org.bouncycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial})
+ * @param fastFp whether <code>f=1+p*F</code> for a ternary <code>F</code> (true) or <code>f</code> is ternary (false)
+ * @param hashAlg a valid identifier for a <code>java.security.MessageDigest</code> instance such as <code>SHA-256</code>
+ */
+ public NTRUEncryptionParameters(int N, int q, int df1, int df2, int df3, int dm0, int db, int c, int minCallsR, int minCallsMask, boolean hashSeed, byte[] oid, boolean sparse, boolean fastFp, Digest hashAlg)
+ {
+ this.N = N;
+ this.q = q;
+ this.df1 = df1;
+ this.df2 = df2;
+ this.df3 = df3;
+ this.db = db;
+ this.dm0 = dm0;
+ this.c = c;
+ this.minCallsR = minCallsR;
+ this.minCallsMask = minCallsMask;
+ this.hashSeed = hashSeed;
+ this.oid = oid;
+ this.sparse = sparse;
+ this.fastFp = fastFp;
+ this.polyType = NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT;
+ this.hashAlg = hashAlg;
+ init();
+ }
+
+ private void init()
+ {
+ dr = df;
+ dr1 = df1;
+ dr2 = df2;
+ dr3 = df3;
+ dg = N / 3;
+ llen = 1; // ceil(log2(maxMsgLenBytes))
+ maxMsgLenBytes = N * 3 / 2 / 8 - llen - db / 8 - 1;
+ bufferLenBits = (N * 3 / 2 + 7) / 8 * 8 + 1;
+ bufferLenTrits = N - 1;
+ pkLen = db;
+ }
+
+ /**
+ * Reads a parameter set from an input stream.
+ *
+ * @param is an input stream
+ * @throws IOException
+ */
+ public NTRUEncryptionParameters(InputStream is)
+ throws IOException
+ {
+ DataInputStream dis = new DataInputStream(is);
+ N = dis.readInt();
+ q = dis.readInt();
+ df = dis.readInt();
+ df1 = dis.readInt();
+ df2 = dis.readInt();
+ df3 = dis.readInt();
+ db = dis.readInt();
+ dm0 = dis.readInt();
+ c = dis.readInt();
+ minCallsR = dis.readInt();
+ minCallsMask = dis.readInt();
+ hashSeed = dis.readBoolean();
+ oid = new byte[3];
+ dis.read(oid);
+ sparse = dis.readBoolean();
+ fastFp = dis.readBoolean();
+ polyType = dis.read();
+
+ String alg = dis.readUTF();
+
+ if ("SHA-512".equals(alg))
+ {
+ hashAlg = new SHA512Digest();
+ }
+ else if ("SHA-256".equals(alg))
+ {
+ hashAlg = new SHA256Digest();
+ }
+
+ init();
+ }
+
+ public NTRUEncryptionParameters clone()
+ {
+ if (polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE)
+ {
+ return new NTRUEncryptionParameters(N, q, df, dm0, db, c, minCallsR, minCallsMask, hashSeed, oid, sparse, fastFp, hashAlg);
+ }
+ else
+ {
+ return new NTRUEncryptionParameters(N, q, df1, df2, df3, dm0, db, c, minCallsR, minCallsMask, hashSeed, oid, sparse, fastFp, hashAlg);
+ }
+ }
+
+ /**
+ * Returns the maximum length a plaintext message can be with this parameter set.
+ *
+ * @return the maximum length in bytes
+ */
+ public int getMaxMessageLength()
+ {
+ return maxMsgLenBytes;
+ }
+
+ /**
+ * Writes the parameter set to an output stream
+ *
+ * @param os an output stream
+ * @throws IOException
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ DataOutputStream dos = new DataOutputStream(os);
+ dos.writeInt(N);
+ dos.writeInt(q);
+ dos.writeInt(df);
+ dos.writeInt(df1);
+ dos.writeInt(df2);
+ dos.writeInt(df3);
+ dos.writeInt(db);
+ dos.writeInt(dm0);
+ dos.writeInt(c);
+ dos.writeInt(minCallsR);
+ dos.writeInt(minCallsMask);
+ dos.writeBoolean(hashSeed);
+ dos.write(oid);
+ dos.writeBoolean(sparse);
+ dos.writeBoolean(fastFp);
+ dos.write(polyType);
+ dos.writeUTF(hashAlg.getAlgorithmName());
+ }
+
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + N;
+ result = prime * result + bufferLenBits;
+ result = prime * result + bufferLenTrits;
+ result = prime * result + c;
+ result = prime * result + db;
+ result = prime * result + df;
+ result = prime * result + df1;
+ result = prime * result + df2;
+ result = prime * result + df3;
+ result = prime * result + dg;
+ result = prime * result + dm0;
+ result = prime * result + dr;
+ result = prime * result + dr1;
+ result = prime * result + dr2;
+ result = prime * result + dr3;
+ result = prime * result + (fastFp ? 1231 : 1237);
+ result = prime * result + ((hashAlg == null) ? 0 : hashAlg.getAlgorithmName().hashCode());
+ result = prime * result + (hashSeed ? 1231 : 1237);
+ result = prime * result + llen;
+ result = prime * result + maxMsgLenBytes;
+ result = prime * result + minCallsMask;
+ result = prime * result + minCallsR;
+ result = prime * result + Arrays.hashCode(oid);
+ result = prime * result + pkLen;
+ result = prime * result + polyType;
+ result = prime * result + q;
+ result = prime * result + (sparse ? 1231 : 1237);
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ NTRUEncryptionParameters other = (NTRUEncryptionParameters)obj;
+ if (N != other.N)
+ {
+ return false;
+ }
+ if (bufferLenBits != other.bufferLenBits)
+ {
+ return false;
+ }
+ if (bufferLenTrits != other.bufferLenTrits)
+ {
+ return false;
+ }
+ if (c != other.c)
+ {
+ return false;
+ }
+ if (db != other.db)
+ {
+ return false;
+ }
+ if (df != other.df)
+ {
+ return false;
+ }
+ if (df1 != other.df1)
+ {
+ return false;
+ }
+ if (df2 != other.df2)
+ {
+ return false;
+ }
+ if (df3 != other.df3)
+ {
+ return false;
+ }
+ if (dg != other.dg)
+ {
+ return false;
+ }
+ if (dm0 != other.dm0)
+ {
+ return false;
+ }
+ if (dr != other.dr)
+ {
+ return false;
+ }
+ if (dr1 != other.dr1)
+ {
+ return false;
+ }
+ if (dr2 != other.dr2)
+ {
+ return false;
+ }
+ if (dr3 != other.dr3)
+ {
+ return false;
+ }
+ if (fastFp != other.fastFp)
+ {
+ return false;
+ }
+ if (hashAlg == null)
+ {
+ if (other.hashAlg != null)
+ {
+ return false;
+ }
+ }
+ else if (!hashAlg.getAlgorithmName().equals(other.hashAlg.getAlgorithmName()))
+ {
+ return false;
+ }
+ if (hashSeed != other.hashSeed)
+ {
+ return false;
+ }
+ if (llen != other.llen)
+ {
+ return false;
+ }
+ if (maxMsgLenBytes != other.maxMsgLenBytes)
+ {
+ return false;
+ }
+ if (minCallsMask != other.minCallsMask)
+ {
+ return false;
+ }
+ if (minCallsR != other.minCallsR)
+ {
+ return false;
+ }
+ if (!Arrays.equals(oid, other.oid))
+ {
+ return false;
+ }
+ if (pkLen != other.pkLen)
+ {
+ return false;
+ }
+ if (polyType != other.polyType)
+ {
+ return false;
+ }
+ if (q != other.q)
+ {
+ return false;
+ }
+ if (sparse != other.sparse)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public String toString()
+ {
+ StringBuilder output = new StringBuilder("EncryptionParameters(N=" + N + " q=" + q);
+ if (polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE)
+ {
+ output.append(" polyType=SIMPLE df=" + df);
+ }
+ else
+ {
+ output.append(" polyType=PRODUCT df1=" + df1 + " df2=" + df2 + " df3=" + df3);
+ }
+ output.append(" dm0=" + dm0 + " db=" + db + " c=" + c + " minCallsR=" + minCallsR + " minCallsMask=" + minCallsMask +
+ " hashSeed=" + hashSeed + " hashAlg=" + hashAlg + " oid=" + Arrays.toString(oid) + " sparse=" + sparse + ")");
+ return output.toString();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionPrivateKeyParameters.java
new file mode 100644
index 00000000..d1ee858e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionPrivateKeyParameters.java
@@ -0,0 +1,199 @@
+package org.bouncycastle.pqc.crypto.ntru;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.bouncycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.Polynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.ProductFormPolynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial;
+
+/**
+ * A NtruEncrypt private key is essentially a polynomial named <code>f</code>
+ * which takes different forms depending on whether product-form polynomials are used,
+ * and on <code>fastP</code><br/>
+ * The inverse of <code>f</code> modulo <code>p</code> is precomputed on initialization.
+ */
+public class NTRUEncryptionPrivateKeyParameters
+ extends NTRUEncryptionKeyParameters
+{
+ public Polynomial t;
+ public IntegerPolynomial fp;
+ public IntegerPolynomial h;
+
+ /**
+ * Constructs a new private key from a polynomial
+ *
+ * @param h the public polynomial for the key.
+ * @param t the polynomial which determines the key: if <code>fastFp=true</code>, <code>f=1+3t</code>; otherwise, <code>f=t</code>
+ * @param fp the inverse of <code>f</code>
+ * @param params the NtruEncrypt parameters to use
+ */
+ public NTRUEncryptionPrivateKeyParameters(IntegerPolynomial h, Polynomial t, IntegerPolynomial fp, NTRUEncryptionParameters params)
+ {
+ super(true, params);
+
+ this.h = h;
+ this.t = t;
+ this.fp = fp;
+ }
+
+ /**
+ * Converts a byte array to a polynomial <code>f</code> and constructs a new private key
+ *
+ * @param b an encoded polynomial
+ * @param params the NtruEncrypt parameters to use
+ * @see #getEncoded()
+ */
+ public NTRUEncryptionPrivateKeyParameters(byte[] b, NTRUEncryptionParameters params)
+ throws IOException
+ {
+ this(new ByteArrayInputStream(b), params);
+ }
+
+ /**
+ * Reads a polynomial <code>f</code> from an input stream and constructs a new private key
+ *
+ * @param is an input stream
+ * @param params the NtruEncrypt parameters to use
+ * @see #writeTo(OutputStream)
+ */
+ public NTRUEncryptionPrivateKeyParameters(InputStream is, NTRUEncryptionParameters params)
+ throws IOException
+ {
+ super(true, params);
+
+ if (params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT)
+ {
+ int N = params.N;
+ int df1 = params.df1;
+ int df2 = params.df2;
+ int df3Ones = params.df3;
+ int df3NegOnes = params.fastFp ? params.df3 : params.df3 - 1;
+ h = IntegerPolynomial.fromBinary(is, params.N, params.q);
+ t = ProductFormPolynomial.fromBinary(is, N, df1, df2, df3Ones, df3NegOnes);
+ }
+ else
+ {
+ h = IntegerPolynomial.fromBinary(is, params.N, params.q);
+ IntegerPolynomial fInt = IntegerPolynomial.fromBinary3Tight(is, params.N);
+ t = params.sparse ? new SparseTernaryPolynomial(fInt) : new DenseTernaryPolynomial(fInt);
+ }
+
+ init();
+ }
+
+ /**
+ * Initializes <code>fp</code> from t.
+ */
+ private void init()
+ {
+ if (params.fastFp)
+ {
+ fp = new IntegerPolynomial(params.N);
+ fp.coeffs[0] = 1;
+ }
+ else
+ {
+ fp = t.toIntegerPolynomial().invertF3();
+ }
+ }
+
+ /**
+ * Converts the key to a byte array
+ *
+ * @return the encoded key
+ * @see #NTRUEncryptionPrivateKeyParameters(byte[], NTRUEncryptionParameters)
+ */
+ public byte[] getEncoded()
+ {
+ byte[] hBytes = h.toBinary(params.q);
+ byte[] tBytes;
+
+ if (t instanceof ProductFormPolynomial)
+ {
+ tBytes = ((ProductFormPolynomial)t).toBinary();
+ }
+ else
+ {
+ tBytes = t.toIntegerPolynomial().toBinary3Tight();
+ }
+
+ byte[] res = new byte[hBytes.length + tBytes.length];
+
+ System.arraycopy(hBytes, 0, res, 0, hBytes.length);
+ System.arraycopy(tBytes, 0, res, hBytes.length, tBytes.length);
+
+ return res;
+ }
+
+ /**
+ * Writes the key to an output stream
+ *
+ * @param os an output stream
+ * @throws IOException
+ * @see #NTRUEncryptionPrivateKeyParameters(InputStream, NTRUEncryptionParameters)
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ os.write(getEncoded());
+ }
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((params == null) ? 0 : params.hashCode());
+ result = prime * result + ((t == null) ? 0 : t.hashCode());
+ result = prime * result + ((h == null) ? 0 : h.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (!(obj instanceof NTRUEncryptionPrivateKeyParameters))
+ {
+ return false;
+ }
+ NTRUEncryptionPrivateKeyParameters other = (NTRUEncryptionPrivateKeyParameters)obj;
+ if (params == null)
+ {
+ if (other.params != null)
+ {
+ return false;
+ }
+ }
+ else if (!params.equals(other.params))
+ {
+ return false;
+ }
+ if (t == null)
+ {
+ if (other.t != null)
+ {
+ return false;
+ }
+ }
+ else if (!t.equals(other.t))
+ {
+ return false;
+ }
+ if (!h.equals(other.h))
+ {
+ return false;
+ }
+ return true;
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionPublicKeyParameters.java
new file mode 100644
index 00000000..0aa03573
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionPublicKeyParameters.java
@@ -0,0 +1,131 @@
+package org.bouncycastle.pqc.crypto.ntru;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.bouncycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+
+/**
+ * A NtruEncrypt public key is essentially a polynomial named <code>h</code>.
+ */
+public class NTRUEncryptionPublicKeyParameters
+ extends NTRUEncryptionKeyParameters
+{
+ public IntegerPolynomial h;
+
+ /**
+ * Constructs a new public key from a polynomial
+ *
+ * @param h the polynomial <code>h</code> which determines the key
+ * @param params the NtruEncrypt parameters to use
+ */
+ public NTRUEncryptionPublicKeyParameters(IntegerPolynomial h, NTRUEncryptionParameters params)
+ {
+ super(false, params);
+
+ this.h = h;
+ }
+
+ /**
+ * Converts a byte array to a polynomial <code>h</code> and constructs a new public key
+ *
+ * @param b an encoded polynomial
+ * @param params the NtruEncrypt parameters to use
+ * @see #getEncoded()
+ */
+ public NTRUEncryptionPublicKeyParameters(byte[] b, NTRUEncryptionParameters params)
+ {
+ super(false, params);
+
+ h = IntegerPolynomial.fromBinary(b, params.N, params.q);
+ }
+
+ /**
+ * Reads a polynomial <code>h</code> from an input stream and constructs a new public key
+ *
+ * @param is an input stream
+ * @param params the NtruEncrypt parameters to use
+ * @see #writeTo(OutputStream)
+ */
+ public NTRUEncryptionPublicKeyParameters(InputStream is, NTRUEncryptionParameters params)
+ throws IOException
+ {
+ super(false, params);
+
+ h = IntegerPolynomial.fromBinary(is, params.N, params.q);
+ }
+
+ /**
+ * Converts the key to a byte array
+ *
+ * @return the encoded key
+ * @see #NTRUEncryptionPublicKeyParameters(byte[], NTRUEncryptionParameters)
+ */
+ public byte[] getEncoded()
+ {
+ return h.toBinary(params.q);
+ }
+
+ /**
+ * Writes the key to an output stream
+ *
+ * @param os an output stream
+ * @throws IOException
+ * @see #NTRUEncryptionPublicKeyParameters(InputStream, NTRUEncryptionParameters)
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ os.write(getEncoded());
+ }
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((h == null) ? 0 : h.hashCode());
+ result = prime * result + ((params == null) ? 0 : params.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (!(obj instanceof NTRUEncryptionPublicKeyParameters))
+ {
+ return false;
+ }
+ NTRUEncryptionPublicKeyParameters other = (NTRUEncryptionPublicKeyParameters)obj;
+ if (h == null)
+ {
+ if (other.h != null)
+ {
+ return false;
+ }
+ }
+ else if (!h.equals(other.h))
+ {
+ return false;
+ }
+ if (params == null)
+ {
+ if (other.params != null)
+ {
+ return false;
+ }
+ }
+ else if (!params.equals(other.params))
+ {
+ return false;
+ }
+ return true;
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEngine.java
new file mode 100644
index 00000000..1fb6a1de
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEngine.java
@@ -0,0 +1,495 @@
+package org.bouncycastle.pqc.crypto.ntru;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.Polynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.ProductFormPolynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.TernaryPolynomial;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Encrypts, decrypts data and generates key pairs.<br/>
+ * The parameter p is hardcoded to 3.
+ */
+public class NTRUEngine
+ implements AsymmetricBlockCipher
+{
+ private boolean forEncryption;
+ private NTRUEncryptionParameters params;
+ private NTRUEncryptionPublicKeyParameters pubKey;
+ private NTRUEncryptionPrivateKeyParameters privKey;
+ private SecureRandom random;
+
+ /**
+ * Constructs a new instance with a set of encryption parameters.
+ *
+ */
+ public NTRUEngine()
+ {
+ }
+
+ public void init(boolean forEncryption, CipherParameters parameters)
+ {
+ this.forEncryption = forEncryption;
+ if (forEncryption)
+ {
+ if (parameters instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom p = (ParametersWithRandom)parameters;
+
+ this.random = p.getRandom();
+ this.pubKey = (NTRUEncryptionPublicKeyParameters)p.getParameters();
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ this.pubKey = (NTRUEncryptionPublicKeyParameters)parameters;
+ }
+
+ this.params = pubKey.getParameters();
+ }
+ else
+ {
+ this.privKey = (NTRUEncryptionPrivateKeyParameters)parameters;
+ this.params = privKey.getParameters();
+ }
+ }
+
+ public int getInputBlockSize()
+ {
+ return params.maxMsgLenBytes;
+ }
+
+ public int getOutputBlockSize()
+ {
+ return ((params.N * log2(params.q)) + 7) / 8;
+ }
+
+ public byte[] processBlock(byte[] in, int inOff, int len)
+ throws InvalidCipherTextException
+ {
+ byte[] tmp = new byte[len];
+
+ System.arraycopy(in, inOff, tmp, 0, len);
+
+ if (forEncryption)
+ {
+ return encrypt(tmp, pubKey);
+ }
+ else
+ {
+ return decrypt(tmp, privKey);
+ }
+ }
+
+ /**
+ * Encrypts a message.<br/>
+ * See P1363.1 section 9.2.2.
+ *
+ * @param m The message to encrypt
+ * @param pubKey the public key to encrypt the message with
+ * @return the encrypted message
+ */
+ private byte[] encrypt(byte[] m, NTRUEncryptionPublicKeyParameters pubKey)
+ {
+ IntegerPolynomial pub = pubKey.h;
+ int N = params.N;
+ int q = params.q;
+
+ int maxLenBytes = params.maxMsgLenBytes;
+ int db = params.db;
+ int bufferLenBits = params.bufferLenBits;
+ int dm0 = params.dm0;
+ int pkLen = params.pkLen;
+ int minCallsMask = params.minCallsMask;
+ boolean hashSeed = params.hashSeed;
+ byte[] oid = params.oid;
+
+ int l = m.length;
+ if (maxLenBytes > 255)
+ {
+ throw new IllegalArgumentException("llen values bigger than 1 are not supported");
+ }
+ if (l > maxLenBytes)
+ {
+ throw new DataLengthException("Message too long: " + l + ">" + maxLenBytes);
+ }
+
+ while (true)
+ {
+ // M = b|octL|m|p0
+ byte[] b = new byte[db / 8];
+ random.nextBytes(b);
+ byte[] p0 = new byte[maxLenBytes + 1 - l];
+ byte[] M = new byte[bufferLenBits / 8];
+
+ System.arraycopy(b, 0, M, 0, b.length);
+ M[b.length] = (byte)l;
+ System.arraycopy(m, 0, M, b.length + 1, m.length);
+ System.arraycopy(p0, 0, M, b.length + 1 + m.length, p0.length);
+
+ IntegerPolynomial mTrin = IntegerPolynomial.fromBinary3Sves(M, N);
+
+ // sData = OID|m|b|hTrunc
+ byte[] bh = pub.toBinary(q);
+ byte[] hTrunc = copyOf(bh, pkLen / 8);
+ byte[] sData = buildSData(oid, m, l, b, hTrunc);
+
+ Polynomial r = generateBlindingPoly(sData, M);
+ IntegerPolynomial R = r.mult(pub, q);
+ IntegerPolynomial R4 = (IntegerPolynomial)R.clone();
+ R4.modPositive(4);
+ byte[] oR4 = R4.toBinary(4);
+ IntegerPolynomial mask = MGF(oR4, N, minCallsMask, hashSeed);
+ mTrin.add(mask);
+ mTrin.mod3();
+
+ if (mTrin.count(-1) < dm0)
+ {
+ continue;
+ }
+ if (mTrin.count(0) < dm0)
+ {
+ continue;
+ }
+ if (mTrin.count(1) < dm0)
+ {
+ continue;
+ }
+
+ R.add(mTrin, q);
+ R.ensurePositive(q);
+ return R.toBinary(q);
+ }
+ }
+
+ private byte[] buildSData(byte[] oid, byte[] m, int l, byte[] b, byte[] hTrunc)
+ {
+ byte[] sData = new byte[oid.length + l + b.length + hTrunc.length];
+
+ System.arraycopy(oid, 0, sData, 0, oid.length);
+ System.arraycopy(m, 0, sData, oid.length, m.length);
+ System.arraycopy(b, 0, sData, oid.length + m.length, b.length);
+ System.arraycopy(hTrunc, 0, sData, oid.length + m.length + b.length, hTrunc.length);
+ return sData;
+ }
+
+ protected IntegerPolynomial encrypt(IntegerPolynomial m, TernaryPolynomial r, IntegerPolynomial pubKey)
+ {
+ IntegerPolynomial e = r.mult(pubKey, params.q);
+ e.add(m, params.q);
+ e.ensurePositive(params.q);
+ return e;
+ }
+
+ /**
+ * Deterministically generates a blinding polynomial from a seed and a message representative.
+ *
+ * @param seed
+ * @param M message representative
+ * @return a blinding polynomial
+ */
+ private Polynomial generateBlindingPoly(byte[] seed, byte[] M)
+ {
+ IndexGenerator ig = new IndexGenerator(seed, params);
+
+ if (params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT)
+ {
+ SparseTernaryPolynomial r1 = new SparseTernaryPolynomial(generateBlindingCoeffs(ig, params.dr1));
+ SparseTernaryPolynomial r2 = new SparseTernaryPolynomial(generateBlindingCoeffs(ig, params.dr2));
+ SparseTernaryPolynomial r3 = new SparseTernaryPolynomial(generateBlindingCoeffs(ig, params.dr3));
+ return new ProductFormPolynomial(r1, r2, r3);
+ }
+ else
+ {
+ int dr = params.dr;
+ boolean sparse = params.sparse;
+ int[] r = generateBlindingCoeffs(ig, dr);
+ if (sparse)
+ {
+ return new SparseTernaryPolynomial(r);
+ }
+ else
+ {
+ return new DenseTernaryPolynomial(r);
+ }
+ }
+ }
+
+ /**
+ * Generates an <code>int</code> array containing <code>dr</code> elements equal to <code>1</code>
+ * and <code>dr</code> elements equal to <code>-1</code> using an index generator.
+ *
+ * @param ig an index generator
+ * @param dr number of ones / negative ones
+ * @return an array containing numbers between <code>-1</code> and <code>1</code>
+ */
+ private int[] generateBlindingCoeffs(IndexGenerator ig, int dr)
+ {
+ int N = params.N;
+
+ int[] r = new int[N];
+ for (int coeff = -1; coeff <= 1; coeff += 2)
+ {
+ int t = 0;
+ while (t < dr)
+ {
+ int i = ig.nextIndex();
+ if (r[i] == 0)
+ {
+ r[i] = coeff;
+ t++;
+ }
+ }
+ }
+
+ return r;
+ }
+
+ /**
+ * An implementation of MGF-TP-1 from P1363.1 section 8.4.1.1.
+ *
+ * @param seed
+ * @param N
+ * @param minCallsR
+ * @param hashSeed whether to hash the seed
+ * @return
+ */
+ private IntegerPolynomial MGF(byte[] seed, int N, int minCallsR, boolean hashSeed)
+ {
+ Digest hashAlg = params.hashAlg;
+ int hashLen = hashAlg.getDigestSize();
+ byte[] buf = new byte[minCallsR * hashLen];
+ byte[] Z = hashSeed ? calcHash(hashAlg, seed) : seed;
+ int counter = 0;
+ while (counter < minCallsR)
+ {
+ hashAlg.update(Z, 0, Z.length);
+ putInt(hashAlg, counter);
+
+ byte[] hash = calcHash(hashAlg);
+ System.arraycopy(hash, 0, buf, counter * hashLen, hashLen);
+ counter++;
+ }
+
+ IntegerPolynomial i = new IntegerPolynomial(N);
+ while (true)
+ {
+ int cur = 0;
+ for (int index = 0; index != buf.length; index++)
+ {
+ int O = (int)buf[index] & 0xFF;
+ if (O >= 243) // 243 = 3^5
+ {
+ continue;
+ }
+
+ for (int terIdx = 0; terIdx < 4; terIdx++)
+ {
+ int rem3 = O % 3;
+ i.coeffs[cur] = rem3 - 1;
+ cur++;
+ if (cur == N)
+ {
+ return i;
+ }
+ O = (O - rem3) / 3;
+ }
+
+ i.coeffs[cur] = O - 1;
+ cur++;
+ if (cur == N)
+ {
+ return i;
+ }
+ }
+
+ if (cur >= N)
+ {
+ return i;
+ }
+
+ hashAlg.update(Z, 0, Z.length);
+ putInt(hashAlg, counter);
+
+ byte[] hash = calcHash(hashAlg);
+
+ buf = hash;
+
+ counter++;
+ }
+ }
+
+ private void putInt(Digest hashAlg, int counter)
+ {
+ hashAlg.update((byte)(counter >> 24));
+ hashAlg.update((byte)(counter >> 16));
+ hashAlg.update((byte)(counter >> 8));
+ hashAlg.update((byte)counter);
+ }
+
+ private byte[] calcHash(Digest hashAlg)
+ {
+ byte[] tmp = new byte[hashAlg.getDigestSize()];
+
+ hashAlg.doFinal(tmp, 0);
+
+ return tmp;
+ }
+
+ private byte[] calcHash(Digest hashAlg, byte[] input)
+ {
+ byte[] tmp = new byte[hashAlg.getDigestSize()];
+
+ hashAlg.update(input, 0, input.length);
+ hashAlg.doFinal(tmp, 0);
+
+ return tmp;
+ }
+ /**
+ * Decrypts a message.<br/>
+ * See P1363.1 section 9.2.3.
+ *
+ * @param data The message to decrypt
+ * @param privKey the corresponding private key
+ * @return the decrypted message
+ * @throws InvalidCipherTextException if the encrypted data is invalid, or <code>maxLenBytes</code> is greater than 255
+ */
+ private byte[] decrypt(byte[] data, NTRUEncryptionPrivateKeyParameters privKey)
+ throws InvalidCipherTextException
+ {
+ Polynomial priv_t = privKey.t;
+ IntegerPolynomial priv_fp = privKey.fp;
+ IntegerPolynomial pub = privKey.h;
+ int N = params.N;
+ int q = params.q;
+ int db = params.db;
+ int maxMsgLenBytes = params.maxMsgLenBytes;
+ int dm0 = params.dm0;
+ int pkLen = params.pkLen;
+ int minCallsMask = params.minCallsMask;
+ boolean hashSeed = params.hashSeed;
+ byte[] oid = params.oid;
+
+ if (maxMsgLenBytes > 255)
+ {
+ throw new DataLengthException("maxMsgLenBytes values bigger than 255 are not supported");
+ }
+
+ int bLen = db / 8;
+
+ IntegerPolynomial e = IntegerPolynomial.fromBinary(data, N, q);
+ IntegerPolynomial ci = decrypt(e, priv_t, priv_fp);
+
+ if (ci.count(-1) < dm0)
+ {
+ throw new InvalidCipherTextException("Less than dm0 coefficients equal -1");
+ }
+ if (ci.count(0) < dm0)
+ {
+ throw new InvalidCipherTextException("Less than dm0 coefficients equal 0");
+ }
+ if (ci.count(1) < dm0)
+ {
+ throw new InvalidCipherTextException("Less than dm0 coefficients equal 1");
+ }
+
+ IntegerPolynomial cR = (IntegerPolynomial)e.clone();
+ cR.sub(ci);
+ cR.modPositive(q);
+ IntegerPolynomial cR4 = (IntegerPolynomial)cR.clone();
+ cR4.modPositive(4);
+ byte[] coR4 = cR4.toBinary(4);
+ IntegerPolynomial mask = MGF(coR4, N, minCallsMask, hashSeed);
+ IntegerPolynomial cMTrin = ci;
+ cMTrin.sub(mask);
+ cMTrin.mod3();
+ byte[] cM = cMTrin.toBinary3Sves();
+
+ byte[] cb = new byte[bLen];
+ System.arraycopy(cM, 0, cb, 0, bLen);
+ int cl = cM[bLen] & 0xFF; // llen=1, so read one byte
+ if (cl > maxMsgLenBytes)
+ {
+ throw new InvalidCipherTextException("Message too long: " + cl + ">" + maxMsgLenBytes);
+ }
+ byte[] cm = new byte[cl];
+ System.arraycopy(cM, bLen + 1, cm, 0, cl);
+ byte[] p0 = new byte[cM.length - (bLen + 1 + cl)];
+ System.arraycopy(cM, bLen + 1 + cl, p0, 0, p0.length);
+ if (!Arrays.areEqual(p0, new byte[p0.length]))
+ {
+ throw new InvalidCipherTextException("The message is not followed by zeroes");
+ }
+
+ // sData = OID|m|b|hTrunc
+ byte[] bh = pub.toBinary(q);
+ byte[] hTrunc = copyOf(bh, pkLen / 8);
+ byte[] sData = buildSData(oid, cm, cl, cb, hTrunc);
+
+ Polynomial cr = generateBlindingPoly(sData, cm);
+ IntegerPolynomial cRPrime = cr.mult(pub);
+ cRPrime.modPositive(q);
+ if (!cRPrime.equals(cR))
+ {
+ throw new InvalidCipherTextException("Invalid message encoding");
+ }
+
+ return cm;
+ }
+
+ /**
+ * @param e
+ * @param priv_t a polynomial such that if <code>fastFp=true</code>, <code>f=1+3*priv_t</code>; otherwise, <code>f=priv_t</code>
+ * @param priv_fp
+ * @return
+ */
+ protected IntegerPolynomial decrypt(IntegerPolynomial e, Polynomial priv_t, IntegerPolynomial priv_fp)
+ {
+ IntegerPolynomial a;
+ if (params.fastFp)
+ {
+ a = priv_t.mult(e, params.q);
+ a.mult(3);
+ a.add(e);
+ }
+ else
+ {
+ a = priv_t.mult(e, params.q);
+ }
+ a.center0(params.q);
+ a.mod3();
+
+ IntegerPolynomial c = params.fastFp ? a : new DenseTernaryPolynomial(a).mult(priv_fp, 3);
+ c.center0(3);
+ return c;
+ }
+
+ private byte[] copyOf(byte[] src, int len)
+ {
+ byte[] tmp = new byte[len];
+
+ System.arraycopy(src, 0, tmp, 0, len < src.length ? len : src.length);
+
+ return tmp;
+ }
+
+ private int log2(int value)
+ {
+ if (value == 2048)
+ {
+ return 11;
+ }
+
+ throw new IllegalStateException("log2 not fully implemented");
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUParameters.java
new file mode 100644
index 00000000..158c0386
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUParameters.java
@@ -0,0 +1,7 @@
+package org.bouncycastle.pqc.crypto.ntru;
+
+public class NTRUParameters
+{
+ public static final int TERNARY_POLYNOMIAL_TYPE_SIMPLE = 0;
+ public static final int TERNARY_POLYNOMIAL_TYPE_PRODUCT = 1;
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigner.java
new file mode 100644
index 00000000..0b8a0788
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigner.java
@@ -0,0 +1,259 @@
+package org.bouncycastle.pqc.crypto.ntru;
+
+import java.nio.ByteBuffer;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.Polynomial;
+
+/**
+ * Signs, verifies data and generates key pairs.
+ */
+public class NTRUSigner
+{
+ private NTRUSigningParameters params;
+ private Digest hashAlg;
+ private NTRUSigningPrivateKeyParameters signingKeyPair;
+ private NTRUSigningPublicKeyParameters verificationKey;
+
+ /**
+ * Constructs a new instance with a set of signature parameters.
+ *
+ * @param params signature parameters
+ */
+ public NTRUSigner(NTRUSigningParameters params)
+ {
+ this.params = params;
+ }
+
+ /**
+ * Resets the engine for signing a message.
+ *
+ * @param forSigning
+ * @param params
+ */
+ public void init(boolean forSigning, CipherParameters params)
+ {
+ if (forSigning)
+ {
+ this.signingKeyPair = (NTRUSigningPrivateKeyParameters)params;
+ }
+ else
+ {
+ this.verificationKey = (NTRUSigningPublicKeyParameters)params;
+ }
+ hashAlg = this.params.hashAlg;
+ hashAlg.reset();
+ }
+
+ /**
+ * Adds data to sign or verify.
+ *
+ * @param b data
+ */
+ public void update(byte b)
+ {
+ if (hashAlg == null)
+ {
+ throw new IllegalStateException("Call initSign or initVerify first!");
+ }
+
+ hashAlg.update(b);
+ }
+
+ /**
+ * Adds data to sign or verify.
+ *
+ * @param m data
+ * @param off offset
+ * @param length number of bytes
+ */
+ public void update(byte[] m, int off, int length)
+ {
+ if (hashAlg == null)
+ {
+ throw new IllegalStateException("Call initSign or initVerify first!");
+ }
+
+ hashAlg.update(m, off, length);
+ }
+
+ /**
+ * Adds data to sign and computes a signature over this data and any data previously added via {@link #update(byte[], int, int)}.
+ *
+ * @return a signature
+ * @throws IllegalStateException if <code>initSign</code> was not called
+ */
+ public byte[] generateSignature()
+ {
+ if (hashAlg == null || signingKeyPair == null)
+ {
+ throw new IllegalStateException("Call initSign first!");
+ }
+
+ byte[] msgHash = new byte[hashAlg.getDigestSize()];
+
+ hashAlg.doFinal(msgHash, 0);
+ return signHash(msgHash, signingKeyPair);
+ }
+
+ private byte[] signHash(byte[] msgHash, NTRUSigningPrivateKeyParameters kp)
+ {
+ int r = 0;
+ IntegerPolynomial s;
+ IntegerPolynomial i;
+
+ NTRUSigningPublicKeyParameters kPub = kp.getPublicKey();
+ do
+ {
+ r++;
+ if (r > params.signFailTolerance)
+ {
+ throw new IllegalStateException("Signing failed: too many retries (max=" + params.signFailTolerance + ")");
+ }
+ i = createMsgRep(msgHash, r);
+ s = sign(i, kp);
+ }
+ while (!verify(i, s, kPub.h));
+
+ byte[] rawSig = s.toBinary(params.q);
+ ByteBuffer sbuf = ByteBuffer.allocate(rawSig.length + 4);
+ sbuf.put(rawSig);
+ sbuf.putInt(r);
+ return sbuf.array();
+ }
+
+ private IntegerPolynomial sign(IntegerPolynomial i, NTRUSigningPrivateKeyParameters kp)
+ {
+ int N = params.N;
+ int q = params.q;
+ int perturbationBases = params.B;
+
+ NTRUSigningPrivateKeyParameters kPriv = kp;
+ NTRUSigningPublicKeyParameters kPub = kp.getPublicKey();
+
+ IntegerPolynomial s = new IntegerPolynomial(N);
+ int iLoop = perturbationBases;
+ while (iLoop >= 1)
+ {
+ Polynomial f = kPriv.getBasis(iLoop).f;
+ Polynomial fPrime = kPriv.getBasis(iLoop).fPrime;
+
+ IntegerPolynomial y = f.mult(i);
+ y.div(q);
+ y = fPrime.mult(y);
+
+ IntegerPolynomial x = fPrime.mult(i);
+ x.div(q);
+ x = f.mult(x);
+
+ IntegerPolynomial si = y;
+ si.sub(x);
+ s.add(si);
+
+ IntegerPolynomial hi = (IntegerPolynomial)kPriv.getBasis(iLoop).h.clone();
+ if (iLoop > 1)
+ {
+ hi.sub(kPriv.getBasis(iLoop - 1).h);
+ }
+ else
+ {
+ hi.sub(kPub.h);
+ }
+ i = si.mult(hi, q);
+
+ iLoop--;
+ }
+
+ Polynomial f = kPriv.getBasis(0).f;
+ Polynomial fPrime = kPriv.getBasis(0).fPrime;
+
+ IntegerPolynomial y = f.mult(i);
+ y.div(q);
+ y = fPrime.mult(y);
+
+ IntegerPolynomial x = fPrime.mult(i);
+ x.div(q);
+ x = f.mult(x);
+
+ y.sub(x);
+ s.add(y);
+ s.modPositive(q);
+ return s;
+ }
+
+ /**
+ * Verifies a signature for any data previously added via {@link #update(byte[], int, int)}.
+ *
+ * @param sig a signature
+ * @return whether the signature is valid
+ * @throws IllegalStateException if <code>initVerify</code> was not called
+ */
+ public boolean verifySignature(byte[] sig)
+ {
+ if (hashAlg == null || verificationKey == null)
+ {
+ throw new IllegalStateException("Call initVerify first!");
+ }
+
+ byte[] msgHash = new byte[hashAlg.getDigestSize()];
+
+ hashAlg.doFinal(msgHash, 0);
+
+ return verifyHash(msgHash, sig, verificationKey);
+ }
+
+ private boolean verifyHash(byte[] msgHash, byte[] sig, NTRUSigningPublicKeyParameters pub)
+ {
+ ByteBuffer sbuf = ByteBuffer.wrap(sig);
+ byte[] rawSig = new byte[sig.length - 4];
+ sbuf.get(rawSig);
+ IntegerPolynomial s = IntegerPolynomial.fromBinary(rawSig, params.N, params.q);
+ int r = sbuf.getInt();
+ return verify(createMsgRep(msgHash, r), s, pub.h);
+ }
+
+ private boolean verify(IntegerPolynomial i, IntegerPolynomial s, IntegerPolynomial h)
+ {
+ int q = params.q;
+ double normBoundSq = params.normBoundSq;
+ double betaSq = params.betaSq;
+
+ IntegerPolynomial t = h.mult(s, q);
+ t.sub(i);
+ long centeredNormSq = (long)(s.centeredNormSq(q) + betaSq * t.centeredNormSq(q));
+ return centeredNormSq <= normBoundSq;
+ }
+
+ protected IntegerPolynomial createMsgRep(byte[] msgHash, int r)
+ {
+ int N = params.N;
+ int q = params.q;
+
+ int c = 31 - Integer.numberOfLeadingZeros(q);
+ int B = (c + 7) / 8;
+ IntegerPolynomial i = new IntegerPolynomial(N);
+
+ ByteBuffer cbuf = ByteBuffer.allocate(msgHash.length + 4);
+ cbuf.put(msgHash);
+ cbuf.putInt(r);
+ NTRUSignerPrng prng = new NTRUSignerPrng(cbuf.array(), params.hashAlg);
+
+ for (int t = 0; t < N; t++)
+ {
+ byte[] o = prng.nextBytes(B);
+ int hi = o[o.length - 1];
+ hi >>= 8 * B - c;
+ hi <<= 8 * B - c;
+ o[o.length - 1] = (byte)hi;
+
+ ByteBuffer obuf = ByteBuffer.allocate(4);
+ obuf.put(o);
+ obuf.rewind();
+ // reverse byte order so it matches the endianness of java ints
+ i.coeffs[t] = Integer.reverseBytes(obuf.getInt());
+ }
+ return i;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSignerPrng.java b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSignerPrng.java
new file mode 100644
index 00000000..77ed63a2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSignerPrng.java
@@ -0,0 +1,64 @@
+package org.bouncycastle.pqc.crypto.ntru;
+
+import java.nio.ByteBuffer;
+
+import org.bouncycastle.crypto.Digest;
+
+/**
+ * An implementation of the deterministic pseudo-random generator in EESS section 3.7.3.1
+ */
+public class NTRUSignerPrng
+{
+ private int counter;
+ private byte[] seed;
+ private Digest hashAlg;
+
+ /**
+ * Constructs a new PRNG and seeds it with a byte array.
+ *
+ * @param seed a seed
+ * @param hashAlg the hash algorithm to use
+ */
+ NTRUSignerPrng(byte[] seed, Digest hashAlg)
+ {
+ counter = 0;
+ this.seed = seed;
+ this.hashAlg = hashAlg;
+ }
+
+ /**
+ * Returns <code>n</code> random bytes
+ *
+ * @param n number of bytes to return
+ * @return the next <code>n</code> random bytes
+ */
+ byte[] nextBytes(int n)
+ {
+ ByteBuffer buf = ByteBuffer.allocate(n);
+
+ while (buf.hasRemaining())
+ {
+ ByteBuffer cbuf = ByteBuffer.allocate(seed.length + 4);
+ cbuf.put(seed);
+ cbuf.putInt(counter);
+ byte[] array = cbuf.array();
+ byte[] hash = new byte[hashAlg.getDigestSize()];
+
+ hashAlg.update(array, 0, array.length);
+
+ hashAlg.doFinal(hash, 0);
+
+ if (buf.remaining() < hash.length)
+ {
+ buf.put(hash, 0, buf.remaining());
+ }
+ else
+ {
+ buf.put(hash);
+ }
+ counter++;
+ }
+
+ return buf.array();
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningKeyGenerationParameters.java
new file mode 100644
index 00000000..1398e2b6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningKeyGenerationParameters.java
@@ -0,0 +1,407 @@
+package org.bouncycastle.pqc.crypto.ntru;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+import java.text.DecimalFormat;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+
+/**
+ * A set of parameters for NtruSign. Several predefined parameter sets are available and new ones can be created as well.
+ */
+public class NTRUSigningKeyGenerationParameters
+ extends KeyGenerationParameters
+ implements Cloneable
+{
+ public static final int BASIS_TYPE_STANDARD = 0;
+ public static final int BASIS_TYPE_TRANSPOSE = 1;
+
+ public static final int KEY_GEN_ALG_RESULTANT = 0;
+ public static final int KEY_GEN_ALG_FLOAT = 1;
+
+ /**
+ * Gives 128 bits of security
+ */
+ public static final NTRUSigningKeyGenerationParameters APR2011_439 = new NTRUSigningKeyGenerationParameters(439, 2048, 146, 1, BASIS_TYPE_TRANSPOSE, 0.165, 400, 280, false, true, KEY_GEN_ALG_RESULTANT, new SHA256Digest());
+
+ /**
+ * Like <code>APR2011_439</code>, this parameter set gives 128 bits of security but uses product-form polynomials
+ */
+ public static final NTRUSigningKeyGenerationParameters APR2011_439_PROD = new NTRUSigningKeyGenerationParameters(439, 2048, 9, 8, 5, 1, BASIS_TYPE_TRANSPOSE, 0.165, 400, 280, false, true, KEY_GEN_ALG_RESULTANT, new SHA256Digest());
+
+ /**
+ * Gives 256 bits of security
+ */
+ public static final NTRUSigningKeyGenerationParameters APR2011_743 = new NTRUSigningKeyGenerationParameters(743, 2048, 248, 1, BASIS_TYPE_TRANSPOSE, 0.127, 405, 360, true, false, KEY_GEN_ALG_RESULTANT, new SHA512Digest());
+
+ /**
+ * Like <code>APR2011_439</code>, this parameter set gives 256 bits of security but uses product-form polynomials
+ */
+ public static final NTRUSigningKeyGenerationParameters APR2011_743_PROD = new NTRUSigningKeyGenerationParameters(743, 2048, 11, 11, 15, 1, BASIS_TYPE_TRANSPOSE, 0.127, 405, 360, true, false, KEY_GEN_ALG_RESULTANT, new SHA512Digest());
+
+ /**
+ * Generates key pairs quickly. Use for testing only.
+ */
+ public static final NTRUSigningKeyGenerationParameters TEST157 = new NTRUSigningKeyGenerationParameters(157, 256, 29, 1, BASIS_TYPE_TRANSPOSE, 0.38, 200, 80, false, false, KEY_GEN_ALG_RESULTANT, new SHA256Digest());
+ /**
+ * Generates key pairs quickly. Use for testing only.
+ */
+ public static final NTRUSigningKeyGenerationParameters TEST157_PROD = new NTRUSigningKeyGenerationParameters(157, 256, 5, 5, 8, 1, BASIS_TYPE_TRANSPOSE, 0.38, 200, 80, false, false, KEY_GEN_ALG_RESULTANT, new SHA256Digest());
+
+
+ public int N;
+ public int q;
+ public int d, d1, d2, d3, B;
+ double beta;
+ public double betaSq;
+ double normBound;
+ public double normBoundSq;
+ public int signFailTolerance = 100;
+ double keyNormBound;
+ public double keyNormBoundSq;
+ public boolean primeCheck; // true if N and 2N+1 are prime
+ public int basisType;
+ int bitsF = 6; // max #bits needed to encode one coefficient of the polynomial F
+ public boolean sparse; // whether to treat ternary polynomials as sparsely populated
+ public int keyGenAlg;
+ public Digest hashAlg;
+ public int polyType;
+
+ /**
+ * Constructs a parameter set that uses ternary private keys (i.e. </code>polyType=SIMPLE</code>).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param d number of -1's in the private polynomials <code>f</code> and <code>g</code>
+ * @param B number of perturbations
+ * @param basisType whether to use the standard or transpose lattice
+ * @param beta balancing factor for the transpose lattice
+ * @param normBound maximum norm for valid signatures
+ * @param keyNormBound maximum norm for the ploynomials <code>F</code> and <code>G</code>
+ * @param primeCheck whether <code>2N+1</code> is prime
+ * @param sparse whether to treat ternary polynomials as sparsely populated ({@link org.bouncycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial} vs {@link org.bouncycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial})
+ * @param keyGenAlg <code>RESULTANT</code> produces better bases, <code>FLOAT</code> is slightly faster. <code>RESULTANT</code> follows the EESS standard while <code>FLOAT</code> is described in Hoffstein et al: An Introduction to Mathematical Cryptography.
+ * @param hashAlg a valid identifier for a <code>java.security.MessageDigest</code> instance such as <code>SHA-256</code>. The <code>MessageDigest</code> must support the <code>getDigestLength()</code> method.
+ */
+ public NTRUSigningKeyGenerationParameters(int N, int q, int d, int B, int basisType, double beta, double normBound, double keyNormBound, boolean primeCheck, boolean sparse, int keyGenAlg, Digest hashAlg)
+ {
+ super(new SecureRandom(), N);
+ this.N = N;
+ this.q = q;
+ this.d = d;
+ this.B = B;
+ this.basisType = basisType;
+ this.beta = beta;
+ this.normBound = normBound;
+ this.keyNormBound = keyNormBound;
+ this.primeCheck = primeCheck;
+ this.sparse = sparse;
+ this.keyGenAlg = keyGenAlg;
+ this.hashAlg = hashAlg;
+ polyType = NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE;
+ init();
+ }
+
+ /**
+ * Constructs a parameter set that uses product-form private keys (i.e. </code>polyType=PRODUCT</code>).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param d1 number of -1's in the private polynomials <code>f</code> and <code>g</code>
+ * @param d2 number of -1's in the private polynomials <code>f</code> and <code>g</code>
+ * @param d3 number of -1's in the private polynomials <code>f</code> and <code>g</code>
+ * @param B number of perturbations
+ * @param basisType whether to use the standard or transpose lattice
+ * @param beta balancing factor for the transpose lattice
+ * @param normBound maximum norm for valid signatures
+ * @param keyNormBound maximum norm for the ploynomials <code>F</code> and <code>G</code>
+ * @param primeCheck whether <code>2N+1</code> is prime
+ * @param sparse whether to treat ternary polynomials as sparsely populated ({@link org.bouncycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial} vs {@link org.bouncycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial})
+ * @param keyGenAlg <code>RESULTANT</code> produces better bases, <code>FLOAT</code> is slightly faster. <code>RESULTANT</code> follows the EESS standard while <code>FLOAT</code> is described in Hoffstein et al: An Introduction to Mathematical Cryptography.
+ * @param hashAlg a valid identifier for a <code>java.security.MessageDigest</code> instance such as <code>SHA-256</code>. The <code>MessageDigest</code> must support the <code>getDigestLength()</code> method.
+ */
+ public NTRUSigningKeyGenerationParameters(int N, int q, int d1, int d2, int d3, int B, int basisType, double beta, double normBound, double keyNormBound, boolean primeCheck, boolean sparse, int keyGenAlg, Digest hashAlg)
+ {
+ super(new SecureRandom(), N);
+ this.N = N;
+ this.q = q;
+ this.d1 = d1;
+ this.d2 = d2;
+ this.d3 = d3;
+ this.B = B;
+ this.basisType = basisType;
+ this.beta = beta;
+ this.normBound = normBound;
+ this.keyNormBound = keyNormBound;
+ this.primeCheck = primeCheck;
+ this.sparse = sparse;
+ this.keyGenAlg = keyGenAlg;
+ this.hashAlg = hashAlg;
+ polyType = NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT;
+ init();
+ }
+
+ private void init()
+ {
+ betaSq = beta * beta;
+ normBoundSq = normBound * normBound;
+ keyNormBoundSq = keyNormBound * keyNormBound;
+ }
+
+ /**
+ * Reads a parameter set from an input stream.
+ *
+ * @param is an input stream
+ * @throws java.io.IOException
+ */
+ public NTRUSigningKeyGenerationParameters(InputStream is)
+ throws IOException
+ {
+ super(new SecureRandom(), 0); // TODO:
+ DataInputStream dis = new DataInputStream(is);
+ N = dis.readInt();
+ q = dis.readInt();
+ d = dis.readInt();
+ d1 = dis.readInt();
+ d2 = dis.readInt();
+ d3 = dis.readInt();
+ B = dis.readInt();
+ basisType = dis.readInt();
+ beta = dis.readDouble();
+ normBound = dis.readDouble();
+ keyNormBound = dis.readDouble();
+ signFailTolerance = dis.readInt();
+ primeCheck = dis.readBoolean();
+ sparse = dis.readBoolean();
+ bitsF = dis.readInt();
+ keyGenAlg = dis.read();
+ String alg = dis.readUTF();
+ if ("SHA-512".equals(alg))
+ {
+ hashAlg = new SHA512Digest();
+ }
+ else if ("SHA-256".equals(alg))
+ {
+ hashAlg = new SHA256Digest();
+ }
+ polyType = dis.read();
+ init();
+ }
+
+ /**
+ * Writes the parameter set to an output stream
+ *
+ * @param os an output stream
+ * @throws java.io.IOException
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ DataOutputStream dos = new DataOutputStream(os);
+ dos.writeInt(N);
+ dos.writeInt(q);
+ dos.writeInt(d);
+ dos.writeInt(d1);
+ dos.writeInt(d2);
+ dos.writeInt(d3);
+ dos.writeInt(B);
+ dos.writeInt(basisType);
+ dos.writeDouble(beta);
+ dos.writeDouble(normBound);
+ dos.writeDouble(keyNormBound);
+ dos.writeInt(signFailTolerance);
+ dos.writeBoolean(primeCheck);
+ dos.writeBoolean(sparse);
+ dos.writeInt(bitsF);
+ dos.write(keyGenAlg);
+ dos.writeUTF(hashAlg.getAlgorithmName());
+ dos.write(polyType);
+ }
+
+ public NTRUSigningParameters getSigningParameters()
+ {
+ return new NTRUSigningParameters(N, q, d, B, beta, normBound, hashAlg);
+ }
+
+ public NTRUSigningKeyGenerationParameters clone()
+ {
+ if (polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE)
+ {
+ return new NTRUSigningKeyGenerationParameters(N, q, d, B, basisType, beta, normBound, keyNormBound, primeCheck, sparse, keyGenAlg, hashAlg);
+ }
+ else
+ {
+ return new NTRUSigningKeyGenerationParameters(N, q, d1, d2, d3, B, basisType, beta, normBound, keyNormBound, primeCheck, sparse, keyGenAlg, hashAlg);
+ }
+ }
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + B;
+ result = prime * result + N;
+ result = prime * result + basisType;
+ long temp;
+ temp = Double.doubleToLongBits(beta);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(betaSq);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ result = prime * result + bitsF;
+ result = prime * result + d;
+ result = prime * result + d1;
+ result = prime * result + d2;
+ result = prime * result + d3;
+ result = prime * result + ((hashAlg == null) ? 0 : hashAlg.getAlgorithmName().hashCode());
+ result = prime * result + keyGenAlg;
+ temp = Double.doubleToLongBits(keyNormBound);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(keyNormBoundSq);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(normBound);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(normBoundSq);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ result = prime * result + polyType;
+ result = prime * result + (primeCheck ? 1231 : 1237);
+ result = prime * result + q;
+ result = prime * result + signFailTolerance;
+ result = prime * result + (sparse ? 1231 : 1237);
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (!(obj instanceof NTRUSigningKeyGenerationParameters))
+ {
+ return false;
+ }
+ NTRUSigningKeyGenerationParameters other = (NTRUSigningKeyGenerationParameters)obj;
+ if (B != other.B)
+ {
+ return false;
+ }
+ if (N != other.N)
+ {
+ return false;
+ }
+ if (basisType != other.basisType)
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(beta) != Double.doubleToLongBits(other.beta))
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(betaSq) != Double.doubleToLongBits(other.betaSq))
+ {
+ return false;
+ }
+ if (bitsF != other.bitsF)
+ {
+ return false;
+ }
+ if (d != other.d)
+ {
+ return false;
+ }
+ if (d1 != other.d1)
+ {
+ return false;
+ }
+ if (d2 != other.d2)
+ {
+ return false;
+ }
+ if (d3 != other.d3)
+ {
+ return false;
+ }
+ if (hashAlg == null)
+ {
+ if (other.hashAlg != null)
+ {
+ return false;
+ }
+ }
+ else if (!hashAlg.getAlgorithmName().equals(other.hashAlg.getAlgorithmName()))
+ {
+ return false;
+ }
+ if (keyGenAlg != other.keyGenAlg)
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(keyNormBound) != Double.doubleToLongBits(other.keyNormBound))
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(keyNormBoundSq) != Double.doubleToLongBits(other.keyNormBoundSq))
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(normBound) != Double.doubleToLongBits(other.normBound))
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(normBoundSq) != Double.doubleToLongBits(other.normBoundSq))
+ {
+ return false;
+ }
+ if (polyType != other.polyType)
+ {
+ return false;
+ }
+ if (primeCheck != other.primeCheck)
+ {
+ return false;
+ }
+ if (q != other.q)
+ {
+ return false;
+ }
+ if (signFailTolerance != other.signFailTolerance)
+ {
+ return false;
+ }
+ if (sparse != other.sparse)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public String toString()
+ {
+ DecimalFormat format = new DecimalFormat("0.00");
+
+ StringBuilder output = new StringBuilder("SignatureParameters(N=" + N + " q=" + q);
+ if (polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE)
+ {
+ output.append(" polyType=SIMPLE d=" + d);
+ }
+ else
+ {
+ output.append(" polyType=PRODUCT d1=" + d1 + " d2=" + d2 + " d3=" + d3);
+ }
+ output.append(" B=" + B + " basisType=" + basisType + " beta=" + format.format(beta) +
+ " normBound=" + format.format(normBound) + " keyNormBound=" + format.format(keyNormBound) +
+ " prime=" + primeCheck + " sparse=" + sparse + " keyGenAlg=" + keyGenAlg + " hashAlg=" + hashAlg + ")");
+ return output.toString();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningKeyPairGenerator.java
new file mode 100644
index 00000000..1471509a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningKeyPairGenerator.java
@@ -0,0 +1,357 @@
+package org.bouncycastle.pqc.crypto.ntru;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.pqc.math.ntru.euclid.BigIntEuclidean;
+import org.bouncycastle.pqc.math.ntru.polynomial.BigDecimalPolynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.BigIntPolynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.Polynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.ProductFormPolynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.Resultant;
+
+import static java.math.BigInteger.ONE;
+import static java.math.BigInteger.ZERO;
+
+public class NTRUSigningKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+ private NTRUSigningKeyGenerationParameters params;
+
+ public void init(KeyGenerationParameters param)
+ {
+ this.params = (NTRUSigningKeyGenerationParameters)param;
+ }
+
+ /**
+ * Generates a new signature key pair. Starts <code>B+1</code> threads.
+ *
+ * @return a key pair
+ */
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ NTRUSigningPublicKeyParameters pub = null;
+ ExecutorService executor = Executors.newCachedThreadPool();
+ List<Future<NTRUSigningPrivateKeyParameters.Basis>> bases = new ArrayList<Future<NTRUSigningPrivateKeyParameters.Basis>>();
+ for (int k = params.B; k >= 0; k--)
+ {
+ bases.add(executor.submit(new BasisGenerationTask()));
+ }
+ executor.shutdown();
+
+ List<NTRUSigningPrivateKeyParameters.Basis> basises = new ArrayList<NTRUSigningPrivateKeyParameters.Basis>();
+
+ for (int k = params.B; k >= 0; k--)
+ {
+ Future<NTRUSigningPrivateKeyParameters.Basis> basis = bases.get(k);
+ try
+ {
+ basises.add(basis.get());
+ if (k == params.B)
+ {
+ pub = new NTRUSigningPublicKeyParameters(basis.get().h, params.getSigningParameters());
+ }
+ }
+ catch (Exception e)
+ {
+ throw new IllegalStateException(e);
+ }
+ }
+ NTRUSigningPrivateKeyParameters priv = new NTRUSigningPrivateKeyParameters(basises, pub);
+ AsymmetricCipherKeyPair kp = new AsymmetricCipherKeyPair(pub, priv);
+ return kp;
+ }
+
+ /**
+ * Generates a new signature key pair. Runs in a single thread.
+ *
+ * @return a key pair
+ */
+ public AsymmetricCipherKeyPair generateKeyPairSingleThread()
+ {
+ List<NTRUSigningPrivateKeyParameters.Basis> basises = new ArrayList<NTRUSigningPrivateKeyParameters.Basis>();
+ NTRUSigningPublicKeyParameters pub = null;
+ for (int k = params.B; k >= 0; k--)
+ {
+ NTRUSigningPrivateKeyParameters.Basis basis = generateBoundedBasis();
+ basises.add(basis);
+ if (k == 0)
+ {
+ pub = new NTRUSigningPublicKeyParameters(basis.h, params.getSigningParameters());
+ }
+ }
+ NTRUSigningPrivateKeyParameters priv = new NTRUSigningPrivateKeyParameters(basises, pub);
+ return new AsymmetricCipherKeyPair(pub, priv);
+ }
+
+
+ /**
+ * Implementation of the optional steps 20 through 26 in EESS1v2.pdf, section 3.5.1.1.
+ * This doesn't seem to have much of an effect and sometimes actually increases the
+ * norm of F, but on average it slightly reduces the norm.<br/>
+ * This method changes <code>F</code> and <code>g</code> but leaves <code>f</code> and
+ * <code>g</code> unchanged.
+ *
+ * @param f
+ * @param g
+ * @param F
+ * @param G
+ * @param N
+ */
+ private void minimizeFG(IntegerPolynomial f, IntegerPolynomial g, IntegerPolynomial F, IntegerPolynomial G, int N)
+ {
+ int E = 0;
+ for (int j = 0; j < N; j++)
+ {
+ E += 2 * N * (f.coeffs[j] * f.coeffs[j] + g.coeffs[j] * g.coeffs[j]);
+ }
+
+ // [f(1)+g(1)]^2 = 4
+ E -= 4;
+
+ IntegerPolynomial u = (IntegerPolynomial)f.clone();
+ IntegerPolynomial v = (IntegerPolynomial)g.clone();
+ int j = 0;
+ int k = 0;
+ int maxAdjustment = N;
+ while (k < maxAdjustment && j < N)
+ {
+ int D = 0;
+ int i = 0;
+ while (i < N)
+ {
+ int D1 = F.coeffs[i] * f.coeffs[i];
+ int D2 = G.coeffs[i] * g.coeffs[i];
+ int D3 = 4 * N * (D1 + D2);
+ D += D3;
+ i++;
+ }
+ // f(1)+g(1) = 2
+ int D1 = 4 * (F.sumCoeffs() + G.sumCoeffs());
+ D -= D1;
+
+ if (D > E)
+ {
+ F.sub(u);
+ G.sub(v);
+ k++;
+ j = 0;
+ }
+ else if (D < -E)
+ {
+ F.add(u);
+ G.add(v);
+ k++;
+ j = 0;
+ }
+ j++;
+ u.rotate1();
+ v.rotate1();
+ }
+ }
+
+ /**
+ * Creates a NTRUSigner basis consisting of polynomials <code>f, g, F, G, h</code>.<br/>
+ * If <code>KeyGenAlg=FLOAT</code>, the basis may not be valid and this method must be rerun if that is the case.<br/>
+ *
+ * @see #generateBoundedBasis()
+ */
+ private FGBasis generateBasis()
+ {
+ int N = params.N;
+ int q = params.q;
+ int d = params.d;
+ int d1 = params.d1;
+ int d2 = params.d2;
+ int d3 = params.d3;
+ int basisType = params.basisType;
+
+ Polynomial f;
+ IntegerPolynomial fInt;
+ Polynomial g;
+ IntegerPolynomial gInt;
+ IntegerPolynomial fq;
+ Resultant rf;
+ Resultant rg;
+ BigIntEuclidean r;
+
+ int _2n1 = 2 * N + 1;
+ boolean primeCheck = params.primeCheck;
+
+ do
+ {
+ do
+ {
+ f = params.polyType== NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE ? DenseTernaryPolynomial.generateRandom(N, d + 1, d, new SecureRandom()) : ProductFormPolynomial.generateRandom(N, d1, d2, d3 + 1, d3, new SecureRandom());
+ fInt = f.toIntegerPolynomial();
+ }
+ while (primeCheck && fInt.resultant(_2n1).res.equals(ZERO));
+ fq = fInt.invertFq(q);
+ }
+ while (fq == null);
+ rf = fInt.resultant();
+
+ do
+ {
+ do
+ {
+ do
+ {
+ g = params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE ? DenseTernaryPolynomial.generateRandom(N, d + 1, d, new SecureRandom()) : ProductFormPolynomial.generateRandom(N, d1, d2, d3 + 1, d3, new SecureRandom());
+ gInt = g.toIntegerPolynomial();
+ }
+ while (primeCheck && gInt.resultant(_2n1).res.equals(ZERO));
+ }
+ while (gInt.invertFq(q) == null);
+ rg = gInt.resultant();
+ r = BigIntEuclidean.calculate(rf.res, rg.res);
+ }
+ while (!r.gcd.equals(ONE));
+
+ BigIntPolynomial A = (BigIntPolynomial)rf.rho.clone();
+ A.mult(r.x.multiply(BigInteger.valueOf(q)));
+ BigIntPolynomial B = (BigIntPolynomial)rg.rho.clone();
+ B.mult(r.y.multiply(BigInteger.valueOf(-q)));
+
+ BigIntPolynomial C;
+ if (params.keyGenAlg == NTRUSigningKeyGenerationParameters.KEY_GEN_ALG_RESULTANT)
+ {
+ int[] fRevCoeffs = new int[N];
+ int[] gRevCoeffs = new int[N];
+ fRevCoeffs[0] = fInt.coeffs[0];
+ gRevCoeffs[0] = gInt.coeffs[0];
+ for (int i = 1; i < N; i++)
+ {
+ fRevCoeffs[i] = fInt.coeffs[N - i];
+ gRevCoeffs[i] = gInt.coeffs[N - i];
+ }
+ IntegerPolynomial fRev = new IntegerPolynomial(fRevCoeffs);
+ IntegerPolynomial gRev = new IntegerPolynomial(gRevCoeffs);
+
+ IntegerPolynomial t = f.mult(fRev);
+ t.add(g.mult(gRev));
+ Resultant rt = t.resultant();
+ C = fRev.mult(B); // fRev.mult(B) is actually faster than new SparseTernaryPolynomial(fRev).mult(B), possibly due to cache locality?
+ C.add(gRev.mult(A));
+ C = C.mult(rt.rho);
+ C.div(rt.res);
+ }
+ else
+ { // KeyGenAlg.FLOAT
+ // calculate ceil(log10(N))
+ int log10N = 0;
+ for (int i = 1; i < N; i *= 10)
+ {
+ log10N++;
+ }
+
+ // * Cdec needs to be accurate to 1 decimal place so it can be correctly rounded;
+ // * fInv loses up to (#digits of longest coeff of B) places in fInv.mult(B);
+ // * multiplying fInv by B also multiplies the rounding error by a factor of N;
+ // so make #decimal places of fInv the sum of the above.
+ BigDecimalPolynomial fInv = rf.rho.div(new BigDecimal(rf.res), B.getMaxCoeffLength() + 1 + log10N);
+ BigDecimalPolynomial gInv = rg.rho.div(new BigDecimal(rg.res), A.getMaxCoeffLength() + 1 + log10N);
+
+ BigDecimalPolynomial Cdec = fInv.mult(B);
+ Cdec.add(gInv.mult(A));
+ Cdec.halve();
+ C = Cdec.round();
+ }
+
+ BigIntPolynomial F = (BigIntPolynomial)B.clone();
+ F.sub(f.mult(C));
+ BigIntPolynomial G = (BigIntPolynomial)A.clone();
+ G.sub(g.mult(C));
+
+ IntegerPolynomial FInt = new IntegerPolynomial(F);
+ IntegerPolynomial GInt = new IntegerPolynomial(G);
+ minimizeFG(fInt, gInt, FInt, GInt, N);
+
+ Polynomial fPrime;
+ IntegerPolynomial h;
+ if (basisType == NTRUSigningKeyGenerationParameters.BASIS_TYPE_STANDARD)
+ {
+ fPrime = FInt;
+ h = g.mult(fq, q);
+ }
+ else
+ {
+ fPrime = g;
+ h = FInt.mult(fq, q);
+ }
+ h.modPositive(q);
+
+ return new FGBasis(f, fPrime, h, FInt, GInt, params);
+ }
+
+ /**
+ * Creates a basis such that <code>|F| &lt; keyNormBound</code> and <code>|G| &lt; keyNormBound</code>
+ *
+ * @return a NTRUSigner basis
+ */
+ public NTRUSigningPrivateKeyParameters.Basis generateBoundedBasis()
+ {
+ while (true)
+ {
+ FGBasis basis = generateBasis();
+ if (basis.isNormOk())
+ {
+ return basis;
+ }
+ }
+ }
+
+ private class BasisGenerationTask
+ implements Callable<NTRUSigningPrivateKeyParameters.Basis>
+ {
+
+
+ public NTRUSigningPrivateKeyParameters.Basis call()
+ throws Exception
+ {
+ return generateBoundedBasis();
+ }
+ }
+
+ /**
+ * A subclass of Basis that additionally contains the polynomials <code>F</code> and <code>G</code>.
+ */
+ public class FGBasis
+ extends NTRUSigningPrivateKeyParameters.Basis
+ {
+ public IntegerPolynomial F;
+ public IntegerPolynomial G;
+
+ FGBasis(Polynomial f, Polynomial fPrime, IntegerPolynomial h, IntegerPolynomial F, IntegerPolynomial G, NTRUSigningKeyGenerationParameters params)
+ {
+ super(f, fPrime, h, params);
+ this.F = F;
+ this.G = G;
+ }
+
+ /**
+ * Returns <code>true</code> if the norms of the polynomials <code>F</code> and <code>G</code>
+ * are within {@link NTRUSigningKeyGenerationParameters#keyNormBound}.
+ *
+ * @return
+ */
+ boolean isNormOk()
+ {
+ double keyNormBoundSq = params.keyNormBoundSq;
+ int q = params.q;
+ return (F.centeredNormSq(q) < keyNormBoundSq && G.centeredNormSq(q) < keyNormBoundSq);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningParameters.java
new file mode 100644
index 00000000..bf70cafb
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningParameters.java
@@ -0,0 +1,269 @@
+package org.bouncycastle.pqc.crypto.ntru;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.DecimalFormat;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+
+/**
+ * A set of parameters for NtruSign. Several predefined parameter sets are available and new ones can be created as well.
+ */
+public class NTRUSigningParameters
+ implements Cloneable
+{
+ public int N;
+ public int q;
+ public int d, d1, d2, d3, B;
+ double beta;
+ public double betaSq;
+ double normBound;
+ public double normBoundSq;
+ public int signFailTolerance = 100;
+ int bitsF = 6; // max #bits needed to encode one coefficient of the polynomial F
+ public Digest hashAlg;
+
+ /**
+ * Constructs a parameter set that uses ternary private keys (i.e. </code>polyType=SIMPLE</code>).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param d number of -1's in the private polynomials <code>f</code> and <code>g</code>
+ * @param B number of perturbations
+ * @param beta balancing factor for the transpose lattice
+ * @param normBound maximum norm for valid signatures
+ * @param hashAlg a valid identifier for a <code>java.security.MessageDigest</code> instance such as <code>SHA-256</code>. The <code>MessageDigest</code> must support the <code>getDigestLength()</code> method.
+ */
+ public NTRUSigningParameters(int N, int q, int d, int B, double beta, double normBound, Digest hashAlg)
+ {
+ this.N = N;
+ this.q = q;
+ this.d = d;
+ this.B = B;
+ this.beta = beta;
+ this.normBound = normBound;
+ this.hashAlg = hashAlg;
+ init();
+ }
+
+ /**
+ * Constructs a parameter set that uses product-form private keys (i.e. </code>polyType=PRODUCT</code>).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param d1 number of -1's in the private polynomials <code>f</code> and <code>g</code>
+ * @param d2 number of -1's in the private polynomials <code>f</code> and <code>g</code>
+ * @param d3 number of -1's in the private polynomials <code>f</code> and <code>g</code>
+ * @param B number of perturbations
+ * @param beta balancing factor for the transpose lattice
+ * @param normBound maximum norm for valid signatures
+ * @param keyNormBound maximum norm for the ploynomials <code>F</code> and <code>G</code>
+ * @param hashAlg a valid identifier for a <code>java.security.MessageDigest</code> instance such as <code>SHA-256</code>. The <code>MessageDigest</code> must support the <code>getDigestLength()</code> method.
+ */
+ public NTRUSigningParameters(int N, int q, int d1, int d2, int d3, int B, double beta, double normBound, double keyNormBound, Digest hashAlg)
+ {
+ this.N = N;
+ this.q = q;
+ this.d1 = d1;
+ this.d2 = d2;
+ this.d3 = d3;
+ this.B = B;
+ this.beta = beta;
+ this.normBound = normBound;
+ this.hashAlg = hashAlg;
+ init();
+ }
+
+ private void init()
+ {
+ betaSq = beta * beta;
+ normBoundSq = normBound * normBound;
+ }
+
+ /**
+ * Reads a parameter set from an input stream.
+ *
+ * @param is an input stream
+ * @throws IOException
+ */
+ public NTRUSigningParameters(InputStream is)
+ throws IOException
+ {
+ DataInputStream dis = new DataInputStream(is);
+ N = dis.readInt();
+ q = dis.readInt();
+ d = dis.readInt();
+ d1 = dis.readInt();
+ d2 = dis.readInt();
+ d3 = dis.readInt();
+ B = dis.readInt();
+ beta = dis.readDouble();
+ normBound = dis.readDouble();
+ signFailTolerance = dis.readInt();
+ bitsF = dis.readInt();
+ String alg = dis.readUTF();
+ if ("SHA-512".equals(alg))
+ {
+ hashAlg = new SHA512Digest();
+ }
+ else if ("SHA-256".equals(alg))
+ {
+ hashAlg = new SHA256Digest();
+ }
+ init();
+ }
+
+ /**
+ * Writes the parameter set to an output stream
+ *
+ * @param os an output stream
+ * @throws IOException
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ DataOutputStream dos = new DataOutputStream(os);
+ dos.writeInt(N);
+ dos.writeInt(q);
+ dos.writeInt(d);
+ dos.writeInt(d1);
+ dos.writeInt(d2);
+ dos.writeInt(d3);
+ dos.writeInt(B);
+ dos.writeDouble(beta);
+ dos.writeDouble(normBound);
+ dos.writeInt(signFailTolerance);
+ dos.writeInt(bitsF);
+ dos.writeUTF(hashAlg.getAlgorithmName());
+ }
+
+ public NTRUSigningParameters clone()
+ {
+ return new NTRUSigningParameters(N, q, d, B, beta, normBound, hashAlg);
+ }
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + B;
+ result = prime * result + N;
+ long temp;
+ temp = Double.doubleToLongBits(beta);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(betaSq);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ result = prime * result + bitsF;
+ result = prime * result + d;
+ result = prime * result + d1;
+ result = prime * result + d2;
+ result = prime * result + d3;
+ result = prime * result + ((hashAlg == null) ? 0 : hashAlg.getAlgorithmName().hashCode());
+ temp = Double.doubleToLongBits(normBound);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(normBoundSq);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ result = prime * result + q;
+ result = prime * result + signFailTolerance;
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (!(obj instanceof NTRUSigningParameters))
+ {
+ return false;
+ }
+ NTRUSigningParameters other = (NTRUSigningParameters)obj;
+ if (B != other.B)
+ {
+ return false;
+ }
+ if (N != other.N)
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(beta) != Double.doubleToLongBits(other.beta))
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(betaSq) != Double.doubleToLongBits(other.betaSq))
+ {
+ return false;
+ }
+ if (bitsF != other.bitsF)
+ {
+ return false;
+ }
+ if (d != other.d)
+ {
+ return false;
+ }
+ if (d1 != other.d1)
+ {
+ return false;
+ }
+ if (d2 != other.d2)
+ {
+ return false;
+ }
+ if (d3 != other.d3)
+ {
+ return false;
+ }
+ if (hashAlg == null)
+ {
+ if (other.hashAlg != null)
+ {
+ return false;
+ }
+ }
+ else if (!hashAlg.getAlgorithmName().equals(other.hashAlg.getAlgorithmName()))
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(normBound) != Double.doubleToLongBits(other.normBound))
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(normBoundSq) != Double.doubleToLongBits(other.normBoundSq))
+ {
+ return false;
+ }
+ if (q != other.q)
+ {
+ return false;
+ }
+ if (signFailTolerance != other.signFailTolerance)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ public String toString()
+ {
+ DecimalFormat format = new DecimalFormat("0.00");
+
+ StringBuilder output = new StringBuilder("SignatureParameters(N=" + N + " q=" + q);
+
+ output.append(" B=" + B + " beta=" + format.format(beta) +
+ " normBound=" + format.format(normBound) +
+ " hashAlg=" + hashAlg + ")");
+ return output.toString();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningPrivateKeyParameters.java
new file mode 100644
index 00000000..515f3562
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningPrivateKeyParameters.java
@@ -0,0 +1,385 @@
+package org.bouncycastle.pqc.crypto.ntru;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.Polynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.ProductFormPolynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial;
+
+/**
+ * A NtruSign private key comprises one or more {@link NTRUSigningPrivateKeyParameters.Basis} of three polynomials each,
+ * except the zeroth basis for which <code>h</code> is undefined.
+ */
+public class NTRUSigningPrivateKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private List<Basis> bases;
+ private NTRUSigningPublicKeyParameters publicKey;
+
+ /**
+ * Constructs a new private key from a byte array
+ *
+ * @param b an encoded private key
+ * @param params the NtruSign parameters to use
+ */
+ public NTRUSigningPrivateKeyParameters(byte[] b, NTRUSigningKeyGenerationParameters params)
+ throws IOException
+ {
+ this(new ByteArrayInputStream(b), params);
+ }
+
+ /**
+ * Constructs a new private key from an input stream
+ *
+ * @param is an input stream
+ * @param params the NtruSign parameters to use
+ */
+ public NTRUSigningPrivateKeyParameters(InputStream is, NTRUSigningKeyGenerationParameters params)
+ throws IOException
+ {
+ super(true);
+ bases = new ArrayList<Basis>();
+ for (int i = 0; i <= params.B; i++)
+ // include a public key h[i] in all bases except for the first one
+ {
+ add(new Basis(is, params, i != 0));
+ }
+ publicKey = new NTRUSigningPublicKeyParameters(is, params.getSigningParameters());
+ }
+
+ public NTRUSigningPrivateKeyParameters(List<Basis> bases, NTRUSigningPublicKeyParameters publicKey)
+ {
+ super(true);
+ this.bases = new ArrayList<Basis>(bases);
+ this.publicKey = publicKey;
+ }
+
+ /**
+ * Adds a basis to the key.
+ *
+ * @param b a NtruSign basis
+ */
+ private void add(Basis b)
+ {
+ bases.add(b);
+ }
+
+ /**
+ * Returns the <code>i</code>-th basis
+ *
+ * @param i the index
+ * @return the basis at index <code>i</code>
+ */
+ public Basis getBasis(int i)
+ {
+ return bases.get(i);
+ }
+
+ public NTRUSigningPublicKeyParameters getPublicKey()
+ {
+ return publicKey;
+ }
+
+ /**
+ * Converts the key to a byte array
+ *
+ * @return the encoded key
+ */
+ public byte[] getEncoded()
+ throws IOException
+ {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ for (int i = 0; i < bases.size(); i++)
+ {
+ // all bases except for the first one contain a public key
+ bases.get(i).encode(os, i != 0);
+ }
+
+ os.write(publicKey.getEncoded());
+
+ return os.toByteArray();
+ }
+
+ /**
+ * Writes the key to an output stream
+ *
+ * @param os an output stream
+ * @throws IOException
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ os.write(getEncoded());
+ }
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((bases == null) ? 0 : bases.hashCode());
+ for (Basis basis : bases)
+ {
+ result += basis.hashCode();
+ }
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ NTRUSigningPrivateKeyParameters other = (NTRUSigningPrivateKeyParameters)obj;
+ if (bases == null)
+ {
+ if (other.bases != null)
+ {
+ return false;
+ }
+ }
+ if (bases.size() != other.bases.size())
+ {
+ return false;
+ }
+ for (int i = 0; i < bases.size(); i++)
+ {
+ Basis basis1 = bases.get(i);
+ Basis basis2 = other.bases.get(i);
+ if (!basis1.f.equals(basis2.f))
+ {
+ return false;
+ }
+ if (!basis1.fPrime.equals(basis2.fPrime))
+ {
+ return false;
+ }
+ if (i != 0 && !basis1.h.equals(basis2.h)) // don't compare h for the 0th basis
+ {
+ return false;
+ }
+ if (!basis1.params.equals(basis2.params))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * A NtruSign basis. Contains three polynomials <code>f, f', h</code>.
+ */
+ public static class Basis
+ {
+ public Polynomial f;
+ public Polynomial fPrime;
+ public IntegerPolynomial h;
+ NTRUSigningKeyGenerationParameters params;
+
+ /**
+ * Constructs a new basis from polynomials <code>f, f', h</code>.
+ *
+ * @param f
+ * @param fPrime
+ * @param h
+ * @param params NtruSign parameters
+ */
+ protected Basis(Polynomial f, Polynomial fPrime, IntegerPolynomial h, NTRUSigningKeyGenerationParameters params)
+ {
+ this.f = f;
+ this.fPrime = fPrime;
+ this.h = h;
+ this.params = params;
+ }
+
+ /**
+ * Reads a basis from an input stream and constructs a new basis.
+ *
+ * @param is an input stream
+ * @param params NtruSign parameters
+ * @param include_h whether to read the polynomial <code>h</code> (<code>true</code>) or only <code>f</code> and <code>f'</code> (<code>false</code>)
+ */
+ Basis(InputStream is, NTRUSigningKeyGenerationParameters params, boolean include_h)
+ throws IOException
+ {
+ int N = params.N;
+ int q = params.q;
+ int d1 = params.d1;
+ int d2 = params.d2;
+ int d3 = params.d3;
+ boolean sparse = params.sparse;
+ this.params = params;
+
+ if (params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT)
+ {
+ f = ProductFormPolynomial.fromBinary(is, N, d1, d2, d3 + 1, d3);
+ }
+ else
+ {
+ IntegerPolynomial fInt = IntegerPolynomial.fromBinary3Tight(is, N);
+ f = sparse ? new SparseTernaryPolynomial(fInt) : new DenseTernaryPolynomial(fInt);
+ }
+
+ if (params.basisType == NTRUSigningKeyGenerationParameters.BASIS_TYPE_STANDARD)
+ {
+ IntegerPolynomial fPrimeInt = IntegerPolynomial.fromBinary(is, N, q);
+ for (int i = 0; i < fPrimeInt.coeffs.length; i++)
+ {
+ fPrimeInt.coeffs[i] -= q / 2;
+ }
+ fPrime = fPrimeInt;
+ }
+ else if (params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT)
+ {
+ fPrime = ProductFormPolynomial.fromBinary(is, N, d1, d2, d3 + 1, d3);
+ }
+ else
+ {
+ fPrime = IntegerPolynomial.fromBinary3Tight(is, N);
+ }
+
+ if (include_h)
+ {
+ h = IntegerPolynomial.fromBinary(is, N, q);
+ }
+ }
+
+ /**
+ * Writes the basis to an output stream
+ *
+ * @param os an output stream
+ * @param include_h whether to write the polynomial <code>h</code> (<code>true</code>) or only <code>f</code> and <code>f'</code> (<code>false</code>)
+ * @throws IOException
+ */
+ void encode(OutputStream os, boolean include_h)
+ throws IOException
+ {
+ int q = params.q;
+
+ os.write(getEncoded(f));
+ if (params.basisType == NTRUSigningKeyGenerationParameters.BASIS_TYPE_STANDARD)
+ {
+ IntegerPolynomial fPrimeInt = fPrime.toIntegerPolynomial();
+ for (int i = 0; i < fPrimeInt.coeffs.length; i++)
+ {
+ fPrimeInt.coeffs[i] += q / 2;
+ }
+ os.write(fPrimeInt.toBinary(q));
+ }
+ else
+ {
+ os.write(getEncoded(fPrime));
+ }
+ if (include_h)
+ {
+ os.write(h.toBinary(q));
+ }
+ }
+
+ private byte[] getEncoded(Polynomial p)
+ {
+ if (p instanceof ProductFormPolynomial)
+ {
+ return ((ProductFormPolynomial)p).toBinary();
+ }
+ else
+ {
+ return p.toIntegerPolynomial().toBinary3Tight();
+ }
+ }
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((f == null) ? 0 : f.hashCode());
+ result = prime * result + ((fPrime == null) ? 0 : fPrime.hashCode());
+ result = prime * result + ((h == null) ? 0 : h.hashCode());
+ result = prime * result + ((params == null) ? 0 : params.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (!(obj instanceof Basis))
+ {
+ return false;
+ }
+ Basis other = (Basis)obj;
+ if (f == null)
+ {
+ if (other.f != null)
+ {
+ return false;
+ }
+ }
+ else if (!f.equals(other.f))
+ {
+ return false;
+ }
+ if (fPrime == null)
+ {
+ if (other.fPrime != null)
+ {
+ return false;
+ }
+ }
+ else if (!fPrime.equals(other.fPrime))
+ {
+ return false;
+ }
+ if (h == null)
+ {
+ if (other.h != null)
+ {
+ return false;
+ }
+ }
+ else if (!h.equals(other.h))
+ {
+ return false;
+ }
+ if (params == null)
+ {
+ if (other.params != null)
+ {
+ return false;
+ }
+ }
+ else if (!params.equals(other.params))
+ {
+ return false;
+ }
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningPublicKeyParameters.java
new file mode 100644
index 00000000..be51d0af
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningPublicKeyParameters.java
@@ -0,0 +1,132 @@
+package org.bouncycastle.pqc.crypto.ntru;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+
+/**
+ * A NtruSign public key is essentially a polynomial named <code>h</code>.
+ */
+public class NTRUSigningPublicKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private NTRUSigningParameters params;
+ public IntegerPolynomial h;
+
+ /**
+ * Constructs a new public key from a polynomial
+ *
+ * @param h the polynomial <code>h</code> which determines the key
+ * @param params the NtruSign parameters to use
+ */
+ public NTRUSigningPublicKeyParameters(IntegerPolynomial h, NTRUSigningParameters params)
+ {
+ super(false);
+ this.h = h;
+ this.params = params;
+ }
+
+ /**
+ * Converts a byte array to a polynomial <code>h</code> and constructs a new public key
+ *
+ * @param b an encoded polynomial
+ * @param params the NtruSign parameters to use
+ */
+ public NTRUSigningPublicKeyParameters(byte[] b, NTRUSigningParameters params)
+ {
+ super(false);
+ h = IntegerPolynomial.fromBinary(b, params.N, params.q);
+ this.params = params;
+ }
+
+ /**
+ * Reads a polynomial <code>h</code> from an input stream and constructs a new public key
+ *
+ * @param is an input stream
+ * @param params the NtruSign parameters to use
+ */
+ public NTRUSigningPublicKeyParameters(InputStream is, NTRUSigningParameters params)
+ throws IOException
+ {
+ super(false);
+ h = IntegerPolynomial.fromBinary(is, params.N, params.q);
+ this.params = params;
+ }
+
+
+ /**
+ * Converts the key to a byte array
+ *
+ * @return the encoded key
+ */
+ public byte[] getEncoded()
+ {
+ return h.toBinary(params.q);
+ }
+
+ /**
+ * Writes the key to an output stream
+ *
+ * @param os an output stream
+ * @throws IOException
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ os.write(getEncoded());
+ }
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((h == null) ? 0 : h.hashCode());
+ result = prime * result + ((params == null) ? 0 : params.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ NTRUSigningPublicKeyParameters other = (NTRUSigningPublicKeyParameters)obj;
+ if (h == null)
+ {
+ if (other.h != null)
+ {
+ return false;
+ }
+ }
+ else if (!h.equals(other.h))
+ {
+ return false;
+ }
+ if (params == null)
+ {
+ if (other.params != null)
+ {
+ return false;
+ }
+ }
+ else if (!params.equals(other.params))
+ {
+ return false;
+ }
+ return true;
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/Layer.java b/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/Layer.java
new file mode 100644
index 00000000..4c457ec5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/Layer.java
@@ -0,0 +1,322 @@
+package org.bouncycastle.pqc.crypto.rainbow;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.pqc.crypto.rainbow.util.GF2Field;
+import org.bouncycastle.pqc.crypto.rainbow.util.RainbowUtil;
+import org.bouncycastle.util.Arrays;
+
+
+/**
+ * This class represents a layer of the Rainbow Oil- and Vinegar Map. Each Layer
+ * consists of oi polynomials with their coefficients, generated at random.
+ * <p/>
+ * To sign a document, we solve a LES (linear equation system) for each layer in
+ * order to find the oil variables of that layer and to be able to use the
+ * variables to compute the signature. This functionality is implemented in the
+ * RainbowSignature-class, by the aid of the private key.
+ * <p/>
+ * Each layer is a part of the private key.
+ * <p/>
+ * More information about the layer can be found in the paper of Jintai Ding,
+ * Dieter Schmidt: Rainbow, a New Multivariable Polynomial Signature Scheme.
+ * ACNS 2005: 164-175 (http://dx.doi.org/10.1007/11496137_12)
+ */
+public class Layer
+{
+ private int vi; // number of vinegars in this layer
+ private int viNext; // number of vinegars in next layer
+ private int oi; // number of oils in this layer
+
+ /*
+ * k : index of polynomial
+ *
+ * i,j : indices of oil and vinegar variables
+ */
+ private short[/* k */][/* i */][/* j */] coeff_alpha;
+ private short[/* k */][/* i */][/* j */] coeff_beta;
+ private short[/* k */][/* i */] coeff_gamma;
+ private short[/* k */] coeff_eta;
+
+ /**
+ * Constructor
+ *
+ * @param vi number of vinegar variables of this layer
+ * @param viNext number of vinegar variables of next layer. It's the same as
+ * (num of oils) + (num of vinegars) of this layer.
+ * @param coeffAlpha alpha-coefficients in the polynomials of this layer
+ * @param coeffBeta beta-coefficients in the polynomials of this layer
+ * @param coeffGamma gamma-coefficients in the polynomials of this layer
+ * @param coeffEta eta-coefficients in the polynomials of this layer
+ */
+ public Layer(byte vi, byte viNext, short[][][] coeffAlpha,
+ short[][][] coeffBeta, short[][] coeffGamma, short[] coeffEta)
+ {
+ this.vi = vi & 0xff;
+ this.viNext = viNext & 0xff;
+ this.oi = this.viNext - this.vi;
+
+ // the secret coefficients of all polynomials in this layer
+ this.coeff_alpha = coeffAlpha;
+ this.coeff_beta = coeffBeta;
+ this.coeff_gamma = coeffGamma;
+ this.coeff_eta = coeffEta;
+ }
+
+ /**
+ * This function generates the coefficients of all polynomials in this layer
+ * at random using random generator.
+ *
+ * @param sr the random generator which is to be used
+ */
+ public Layer(int vi, int viNext, SecureRandom sr)
+ {
+ this.vi = vi;
+ this.viNext = viNext;
+ this.oi = viNext - vi;
+
+ // the coefficients of all polynomials in this layer
+ this.coeff_alpha = new short[this.oi][this.oi][this.vi];
+ this.coeff_beta = new short[this.oi][this.vi][this.vi];
+ this.coeff_gamma = new short[this.oi][this.viNext];
+ this.coeff_eta = new short[this.oi];
+
+ int numOfPoly = this.oi; // number of polynomials per layer
+
+ // Alpha coeffs
+ for (int k = 0; k < numOfPoly; k++)
+ {
+ for (int i = 0; i < this.oi; i++)
+ {
+ for (int j = 0; j < this.vi; j++)
+ {
+ coeff_alpha[k][i][j] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+ }
+ }
+ // Beta coeffs
+ for (int k = 0; k < numOfPoly; k++)
+ {
+ for (int i = 0; i < this.vi; i++)
+ {
+ for (int j = 0; j < this.vi; j++)
+ {
+ coeff_beta[k][i][j] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+ }
+ }
+ // Gamma coeffs
+ for (int k = 0; k < numOfPoly; k++)
+ {
+ for (int i = 0; i < this.viNext; i++)
+ {
+ coeff_gamma[k][i] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+ }
+ // Eta
+ for (int k = 0; k < numOfPoly; k++)
+ {
+ coeff_eta[k] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+ }
+
+ /**
+ * This method plugs in the vinegar variables into the polynomials of this
+ * layer and computes the coefficients of the Oil-variables as well as the
+ * free coefficient in each polynomial.
+ * <p/>
+ * It is needed for computing the Oil variables while signing.
+ *
+ * @param x vinegar variables of this layer that should be plugged into
+ * the polynomials.
+ * @return coeff the coefficients of Oil variables and the free coeff in the
+ * polynomials of this layer.
+ */
+ public short[][] plugInVinegars(short[] x)
+ {
+ // temporary variable needed for the multiplication
+ short tmpMult = 0;
+ // coeff: 1st index = which polynomial, 2nd index=which variable
+ short[][] coeff = new short[oi][oi + 1]; // gets returned
+ // free coefficient per polynomial
+ short[] sum = new short[oi];
+
+ /*
+ * evaluate the beta-part of the polynomials (it contains no oil
+ * variables)
+ */
+ for (int k = 0; k < oi; k++)
+ {
+ for (int i = 0; i < vi; i++)
+ {
+ for (int j = 0; j < vi; j++)
+ {
+ // tmp = beta * xi (plug in)
+ tmpMult = GF2Field.multElem(coeff_beta[k][i][j], x[i]);
+ // tmp = tmp * xj
+ tmpMult = GF2Field.multElem(tmpMult, x[j]);
+ // accumulate into the array for the free coefficients.
+ sum[k] = GF2Field.addElem(sum[k], tmpMult);
+ }
+ }
+ }
+
+ /* evaluate the alpha-part (it contains oils) */
+ for (int k = 0; k < oi; k++)
+ {
+ for (int i = 0; i < oi; i++)
+ {
+ for (int j = 0; j < vi; j++)
+ {
+ // alpha * xj (plug in)
+ tmpMult = GF2Field.multElem(coeff_alpha[k][i][j], x[j]);
+ // accumulate
+ coeff[k][i] = GF2Field.addElem(coeff[k][i], tmpMult);
+ }
+ }
+ }
+ /* evaluate the gama-part of the polynomial (containing no oils) */
+ for (int k = 0; k < oi; k++)
+ {
+ for (int i = 0; i < vi; i++)
+ {
+ // gamma * xi (plug in)
+ tmpMult = GF2Field.multElem(coeff_gamma[k][i], x[i]);
+ // accumulate in the array for the free coefficients (per
+ // polynomial).
+ sum[k] = GF2Field.addElem(sum[k], tmpMult);
+ }
+ }
+ /* evaluate the gama-part of the polynomial (but containing oils) */
+ for (int k = 0; k < oi; k++)
+ {
+ for (int i = vi; i < viNext; i++)
+ { // oils
+ // accumulate the coefficients of the oil variables (per
+ // polynomial).
+ coeff[k][i - vi] = GF2Field.addElem(coeff_gamma[k][i],
+ coeff[k][i - vi]);
+ }
+ }
+ /* evaluate the eta-part of the polynomial */
+ for (int k = 0; k < oi; k++)
+ {
+ // accumulate in the array for the free coefficients per polynomial.
+ sum[k] = GF2Field.addElem(sum[k], coeff_eta[k]);
+ }
+
+ /* put the free coefficients (sum) into the coeff-array as last column */
+ for (int k = 0; k < oi; k++)
+ {
+ coeff[k][oi] = sum[k];
+ }
+ return coeff;
+ }
+
+ /**
+ * Getter for the number of vinegar variables of this layer.
+ *
+ * @return the number of vinegar variables of this layer.
+ */
+ public int getVi()
+ {
+ return vi;
+ }
+
+ /**
+ * Getter for the number of vinegar variables of the next layer.
+ *
+ * @return the number of vinegar variables of the next layer.
+ */
+ public int getViNext()
+ {
+ return viNext;
+ }
+
+ /**
+ * Getter for the number of Oil variables of this layer.
+ *
+ * @return the number of oil variables of this layer.
+ */
+ public int getOi()
+ {
+ return oi;
+ }
+
+ /**
+ * Getter for the alpha-coefficients of the polynomials in this layer.
+ *
+ * @return the coefficients of alpha-terms of this layer.
+ */
+ public short[][][] getCoeffAlpha()
+ {
+ return coeff_alpha;
+ }
+
+ /**
+ * Getter for the beta-coefficients of the polynomials in this layer.
+ *
+ * @return the coefficients of beta-terms of this layer.
+ */
+
+ public short[][][] getCoeffBeta()
+ {
+ return coeff_beta;
+ }
+
+ /**
+ * Getter for the gamma-coefficients of the polynomials in this layer.
+ *
+ * @return the coefficients of gamma-terms of this layer
+ */
+ public short[][] getCoeffGamma()
+ {
+ return coeff_gamma;
+ }
+
+ /**
+ * Getter for the eta-coefficients of the polynomials in this layer.
+ *
+ * @return the coefficients eta of this layer
+ */
+ public short[] getCoeffEta()
+ {
+ return coeff_eta;
+ }
+
+ /**
+ * This function compares this Layer with another object.
+ *
+ * @param other the other object
+ * @return the result of the comparison
+ */
+ public boolean equals(Object other)
+ {
+ if (other == null || !(other instanceof Layer))
+ {
+ return false;
+ }
+ Layer otherLayer = (Layer)other;
+
+ return vi == otherLayer.getVi()
+ && viNext == otherLayer.getViNext()
+ && oi == otherLayer.getOi()
+ && RainbowUtil.equals(coeff_alpha, otherLayer.getCoeffAlpha())
+ && RainbowUtil.equals(coeff_beta, otherLayer.getCoeffBeta())
+ && RainbowUtil.equals(coeff_gamma, otherLayer.getCoeffGamma())
+ && RainbowUtil.equals(coeff_eta, otherLayer.getCoeffEta());
+ }
+
+ public int hashCode()
+ {
+ int hash = vi;
+ hash = hash * 37 + viNext;
+ hash = hash * 37 + oi;
+ hash = hash * 37 + Arrays.hashCode(coeff_alpha);
+ hash = hash * 37 + Arrays.hashCode(coeff_beta);
+ hash = hash * 37 + Arrays.hashCode(coeff_gamma);
+ hash = hash * 37 + Arrays.hashCode(coeff_eta);
+
+ return hash;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowKeyGenerationParameters.java
new file mode 100644
index 00000000..b634f9cc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowKeyGenerationParameters.java
@@ -0,0 +1,26 @@
+package org.bouncycastle.pqc.crypto.rainbow;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.KeyGenerationParameters;
+
+public class RainbowKeyGenerationParameters
+ extends KeyGenerationParameters
+{
+ private RainbowParameters params;
+
+ public RainbowKeyGenerationParameters(
+ SecureRandom random,
+ RainbowParameters params)
+ {
+ // TODO: key size?
+ super(random, params.getVi()[params.getVi().length - 1] - params.getVi()[0]);
+ this.params = params;
+ }
+
+ public RainbowParameters getParameters()
+ {
+ return params;
+ }
+}
+
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowKeyPairGenerator.java
new file mode 100644
index 00000000..e7fe0593
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowKeyPairGenerator.java
@@ -0,0 +1,414 @@
+package org.bouncycastle.pqc.crypto.rainbow;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.pqc.crypto.rainbow.util.ComputeInField;
+import org.bouncycastle.pqc.crypto.rainbow.util.GF2Field;
+
+/**
+ * This class implements AsymmetricCipherKeyPairGenerator. It is used
+ * as a generator for the private and public key of the Rainbow Signature
+ * Scheme.
+ * <p/>
+ * Detailed information about the key generation is to be found in the paper of
+ * Jintai Ding, Dieter Schmidt: Rainbow, a New Multivariable Polynomial
+ * Signature Scheme. ACNS 2005: 164-175 (http://dx.doi.org/10.1007/11496137_12)
+ */
+public class RainbowKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+ private boolean initialized = false;
+ private SecureRandom sr;
+ private RainbowKeyGenerationParameters rainbowParams;
+
+ /* linear affine map L1: */
+ private short[][] A1; // matrix of the lin. affine map L1(n-v1 x n-v1 matrix)
+ private short[][] A1inv; // inverted A1
+ private short[] b1; // translation element of the lin.affine map L1
+
+ /* linear affine map L2: */
+ private short[][] A2; // matrix of the lin. affine map (n x n matrix)
+ private short[][] A2inv; // inverted A2
+ private short[] b2; // translation elemt of the lin.affine map L2
+
+ /* components of F: */
+ private int numOfLayers; // u (number of sets S)
+ private Layer layers[]; // layers of polynomials of F
+ private int[] vi; // set of vinegar vars per layer.
+
+ /* components of Public Key */
+ private short[][] pub_quadratic; // quadratic(mixed) coefficients
+ private short[][] pub_singular; // singular coefficients
+ private short[] pub_scalar; // scalars
+
+ // TODO
+
+ /**
+ * The standard constructor tries to generate the Rainbow algorithm identifier
+ * with the corresponding OID.
+ * <p/>
+ */
+ public RainbowKeyPairGenerator()
+ {
+ }
+
+
+ /**
+ * This function generates a Rainbow key pair.
+ *
+ * @return the generated key pair
+ */
+ public AsymmetricCipherKeyPair genKeyPair()
+ {
+ RainbowPrivateKeyParameters privKey;
+ RainbowPublicKeyParameters pubKey;
+
+ if (!initialized)
+ {
+ initializeDefault();
+ }
+
+ /* choose all coefficients at random */
+ keygen();
+
+ /* now marshall them to PrivateKey */
+ privKey = new RainbowPrivateKeyParameters(A1inv, b1, A2inv, b2, vi, layers);
+
+
+ /* marshall to PublicKey */
+ pubKey = new RainbowPublicKeyParameters(vi[vi.length - 1] - vi[0], pub_quadratic, pub_singular, pub_scalar);
+
+ return new AsymmetricCipherKeyPair(pubKey, privKey);
+ }
+
+ // TODO
+ public void initialize(
+ KeyGenerationParameters param)
+ {
+ this.rainbowParams = (RainbowKeyGenerationParameters)param;
+
+ // set source of randomness
+ this.sr = new SecureRandom();
+
+ // unmarshalling:
+ this.vi = this.rainbowParams.getParameters().getVi();
+ this.numOfLayers = this.rainbowParams.getParameters().getNumOfLayers();
+
+ this.initialized = true;
+ }
+
+ private void initializeDefault()
+ {
+ RainbowKeyGenerationParameters rbKGParams = new RainbowKeyGenerationParameters(new SecureRandom(), new RainbowParameters());
+ initialize(rbKGParams);
+ }
+
+ /**
+ * This function calls the functions for the random generation of the coefficients
+ * and the matrices needed for the private key and the method for computing the public key.
+ */
+ private void keygen()
+ {
+ generateL1();
+ generateL2();
+ generateF();
+ computePublicKey();
+ }
+
+ /**
+ * This function generates the invertible affine linear map L1 = A1*x + b1
+ * <p/>
+ * The translation part b1, is stored in a separate array. The inverse of
+ * the matrix-part of L1 A1inv is also computed here.
+ * <p/>
+ * This linear map hides the output of the map F. It is on k^(n-v1).
+ */
+ private void generateL1()
+ {
+
+ // dimension = n-v1 = vi[last] - vi[first]
+ int dim = vi[vi.length - 1] - vi[0];
+ this.A1 = new short[dim][dim];
+ this.A1inv = null;
+ ComputeInField c = new ComputeInField();
+
+ /* generation of A1 at random */
+ while (A1inv == null)
+ {
+ for (int i = 0; i < dim; i++)
+ {
+ for (int j = 0; j < dim; j++)
+ {
+ A1[i][j] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+ }
+ A1inv = c.inverse(A1);
+ }
+
+ /* generation of the translation vector at random */
+ b1 = new short[dim];
+ for (int i = 0; i < dim; i++)
+ {
+ b1[i] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+ }
+
+ /**
+ * This function generates the invertible affine linear map L2 = A2*x + b2
+ * <p/>
+ * The translation part b2, is stored in a separate array. The inverse of
+ * the matrix-part of L2 A2inv is also computed here.
+ * <p/>
+ * This linear map hides the output of the map F. It is on k^(n).
+ */
+ private void generateL2()
+ {
+
+ // dimension = n = vi[last]
+ int dim = vi[vi.length - 1];
+ this.A2 = new short[dim][dim];
+ this.A2inv = null;
+ ComputeInField c = new ComputeInField();
+
+ /* generation of A2 at random */
+ while (this.A2inv == null)
+ {
+ for (int i = 0; i < dim; i++)
+ {
+ for (int j = 0; j < dim; j++)
+ { // one col extra for b
+ A2[i][j] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+ }
+ this.A2inv = c.inverse(A2);
+ }
+ /* generation of the translation vector at random */
+ b2 = new short[dim];
+ for (int i = 0; i < dim; i++)
+ {
+ b2[i] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+
+ }
+
+ /**
+ * This function generates the private map F, which consists of u-1 layers.
+ * Each layer consists of oi polynomials where oi = vi[i+1]-vi[i].
+ * <p/>
+ * The methods for the generation of the coefficients of these polynomials
+ * are called here.
+ */
+ private void generateF()
+ {
+
+ this.layers = new Layer[this.numOfLayers];
+ for (int i = 0; i < this.numOfLayers; i++)
+ {
+ layers[i] = new Layer(this.vi[i], this.vi[i + 1], sr);
+ }
+ }
+
+ /**
+ * This function computes the public key from the private key.
+ * <p/>
+ * The composition of F with L2 is computed, followed by applying L1 to the
+ * composition's result. The singular and scalar values constitute to the
+ * public key as is, the quadratic terms are compacted in
+ * <tt>compactPublicKey()</tt>
+ */
+ private void computePublicKey()
+ {
+
+ ComputeInField c = new ComputeInField();
+ int rows = this.vi[this.vi.length - 1] - this.vi[0];
+ int vars = this.vi[this.vi.length - 1];
+ // Fpub
+ short[][][] coeff_quadratic_3dim = new short[rows][vars][vars];
+ this.pub_singular = new short[rows][vars];
+ this.pub_scalar = new short[rows];
+
+ // Coefficients of layers of Private Key F
+ short[][][] coeff_alpha;
+ short[][][] coeff_beta;
+ short[][] coeff_gamma;
+ short[] coeff_eta;
+
+ // Needed for counters;
+ int oils = 0;
+ int vins = 0;
+ int crnt_row = 0; // current row (polynomial)
+
+ short vect_tmp[] = new short[vars]; // vector tmp;
+ short sclr_tmp = 0;
+
+ // Composition of F and L2: Insert L2 = A2*x+b2 in F
+ for (int l = 0; l < this.layers.length; l++)
+ {
+ // get coefficients of current layer
+ coeff_alpha = this.layers[l].getCoeffAlpha();
+ coeff_beta = this.layers[l].getCoeffBeta();
+ coeff_gamma = this.layers[l].getCoeffGamma();
+ coeff_eta = this.layers[l].getCoeffEta();
+ oils = coeff_alpha[0].length;// this.layers[l].getOi();
+ vins = coeff_beta[0].length;// this.layers[l].getVi();
+ // compute polynomials of layer
+ for (int p = 0; p < oils; p++)
+ {
+ // multiply alphas
+ for (int x1 = 0; x1 < oils; x1++)
+ {
+ for (int x2 = 0; x2 < vins; x2++)
+ {
+ // multiply polynomial1 with polynomial2
+ vect_tmp = c.multVect(coeff_alpha[p][x1][x2],
+ this.A2[x1 + vins]);
+ coeff_quadratic_3dim[crnt_row + p] = c.addSquareMatrix(
+ coeff_quadratic_3dim[crnt_row + p], c
+ .multVects(vect_tmp, this.A2[x2]));
+ // mul poly1 with scalar2
+ vect_tmp = c.multVect(this.b2[x2], vect_tmp);
+ this.pub_singular[crnt_row + p] = c.addVect(vect_tmp,
+ this.pub_singular[crnt_row + p]);
+ // mul scalar1 with poly2
+ vect_tmp = c.multVect(coeff_alpha[p][x1][x2],
+ this.A2[x2]);
+ vect_tmp = c.multVect(b2[x1 + vins], vect_tmp);
+ this.pub_singular[crnt_row + p] = c.addVect(vect_tmp,
+ this.pub_singular[crnt_row + p]);
+ // mul scalar1 with scalar2
+ sclr_tmp = GF2Field.multElem(coeff_alpha[p][x1][x2],
+ this.b2[x1 + vins]);
+ this.pub_scalar[crnt_row + p] = GF2Field.addElem(
+ this.pub_scalar[crnt_row + p], GF2Field
+ .multElem(sclr_tmp, this.b2[x2]));
+ }
+ }
+ // multiply betas
+ for (int x1 = 0; x1 < vins; x1++)
+ {
+ for (int x2 = 0; x2 < vins; x2++)
+ {
+ // multiply polynomial1 with polynomial2
+ vect_tmp = c.multVect(coeff_beta[p][x1][x2],
+ this.A2[x1]);
+ coeff_quadratic_3dim[crnt_row + p] = c.addSquareMatrix(
+ coeff_quadratic_3dim[crnt_row + p], c
+ .multVects(vect_tmp, this.A2[x2]));
+ // mul poly1 with scalar2
+ vect_tmp = c.multVect(this.b2[x2], vect_tmp);
+ this.pub_singular[crnt_row + p] = c.addVect(vect_tmp,
+ this.pub_singular[crnt_row + p]);
+ // mul scalar1 with poly2
+ vect_tmp = c.multVect(coeff_beta[p][x1][x2],
+ this.A2[x2]);
+ vect_tmp = c.multVect(this.b2[x1], vect_tmp);
+ this.pub_singular[crnt_row + p] = c.addVect(vect_tmp,
+ this.pub_singular[crnt_row + p]);
+ // mul scalar1 with scalar2
+ sclr_tmp = GF2Field.multElem(coeff_beta[p][x1][x2],
+ this.b2[x1]);
+ this.pub_scalar[crnt_row + p] = GF2Field.addElem(
+ this.pub_scalar[crnt_row + p], GF2Field
+ .multElem(sclr_tmp, this.b2[x2]));
+ }
+ }
+ // multiply gammas
+ for (int n = 0; n < vins + oils; n++)
+ {
+ // mul poly with scalar
+ vect_tmp = c.multVect(coeff_gamma[p][n], this.A2[n]);
+ this.pub_singular[crnt_row + p] = c.addVect(vect_tmp,
+ this.pub_singular[crnt_row + p]);
+ // mul scalar with scalar
+ this.pub_scalar[crnt_row + p] = GF2Field.addElem(
+ this.pub_scalar[crnt_row + p], GF2Field.multElem(
+ coeff_gamma[p][n], this.b2[n]));
+ }
+ // add eta
+ this.pub_scalar[crnt_row + p] = GF2Field.addElem(
+ this.pub_scalar[crnt_row + p], coeff_eta[p]);
+ }
+ crnt_row = crnt_row + oils;
+ }
+
+ // Apply L1 = A1*x+b1 to composition of F and L2
+ {
+ // temporary coefficient arrays
+ short[][][] tmp_c_quad = new short[rows][vars][vars];
+ short[][] tmp_c_sing = new short[rows][vars];
+ short[] tmp_c_scal = new short[rows];
+ for (int r = 0; r < rows; r++)
+ {
+ for (int q = 0; q < A1.length; q++)
+ {
+ tmp_c_quad[r] = c.addSquareMatrix(tmp_c_quad[r], c
+ .multMatrix(A1[r][q], coeff_quadratic_3dim[q]));
+ tmp_c_sing[r] = c.addVect(tmp_c_sing[r], c.multVect(
+ A1[r][q], this.pub_singular[q]));
+ tmp_c_scal[r] = GF2Field.addElem(tmp_c_scal[r], GF2Field
+ .multElem(A1[r][q], this.pub_scalar[q]));
+ }
+ tmp_c_scal[r] = GF2Field.addElem(tmp_c_scal[r], b1[r]);
+ }
+ // set public key
+ coeff_quadratic_3dim = tmp_c_quad;
+ this.pub_singular = tmp_c_sing;
+ this.pub_scalar = tmp_c_scal;
+ }
+ compactPublicKey(coeff_quadratic_3dim);
+ }
+
+ /**
+ * The quadratic (or mixed) terms of the public key are compacted from a n x
+ * n matrix per polynomial to an upper diagonal matrix stored in one integer
+ * array of n (n + 1) / 2 elements per polynomial. The ordering of elements
+ * is lexicographic and the result is updating <tt>this.pub_quadratic</tt>,
+ * which stores the quadratic elements of the public key.
+ *
+ * @param coeff_quadratic_to_compact 3-dimensional array containing a n x n Matrix for each of the
+ * n - v1 polynomials
+ */
+ private void compactPublicKey(short[][][] coeff_quadratic_to_compact)
+ {
+ int polynomials = coeff_quadratic_to_compact.length;
+ int n = coeff_quadratic_to_compact[0].length;
+ int entries = n * (n + 1) / 2;// the small gauss
+ this.pub_quadratic = new short[polynomials][entries];
+ int offset = 0;
+
+ for (int p = 0; p < polynomials; p++)
+ {
+ offset = 0;
+ for (int x = 0; x < n; x++)
+ {
+ for (int y = x; y < n; y++)
+ {
+ if (y == x)
+ {
+ this.pub_quadratic[p][offset] = coeff_quadratic_to_compact[p][x][y];
+ }
+ else
+ {
+ this.pub_quadratic[p][offset] = GF2Field.addElem(
+ coeff_quadratic_to_compact[p][x][y],
+ coeff_quadratic_to_compact[p][y][x]);
+ }
+ offset++;
+ }
+ }
+ }
+ }
+
+ public void init(KeyGenerationParameters param)
+ {
+ this.initialize(param);
+ }
+
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ return genKeyPair();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowKeyParameters.java
new file mode 100644
index 00000000..9dec6853
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowKeyParameters.java
@@ -0,0 +1,25 @@
+package org.bouncycastle.pqc.crypto.rainbow;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+
+public class RainbowKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private int docLength;
+
+ public RainbowKeyParameters(
+ boolean isPrivate,
+ int docLength)
+ {
+ super(isPrivate);
+ this.docLength = docLength;
+ }
+
+ /**
+ * @return the docLength
+ */
+ public int getDocLength()
+ {
+ return this.docLength;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowParameters.java
new file mode 100644
index 00000000..147c55e9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowParameters.java
@@ -0,0 +1,111 @@
+package org.bouncycastle.pqc.crypto.rainbow;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public class RainbowParameters
+ implements CipherParameters
+{
+
+ /**
+ * DEFAULT PARAMS
+ */
+ /*
+ * Vi = vinegars per layer whereas n is vu (vu = 33 = n) such that
+ *
+ * v1 = 6; o1 = 12-6 = 6
+ *
+ * v2 = 12; o2 = 17-12 = 5
+ *
+ * v3 = 17; o3 = 22-17 = 5
+ *
+ * v4 = 22; o4 = 33-22 = 11
+ *
+ * v5 = 33; (o5 = 0)
+ */
+ private final int[] DEFAULT_VI = {6, 12, 17, 22, 33};
+
+ private int[] vi;// set of vinegar vars per layer.
+
+ /**
+ * Default Constructor The elements of the array containing the number of
+ * Vinegar variables in each layer are set to the default values here.
+ */
+ public RainbowParameters()
+ {
+ this.vi = this.DEFAULT_VI;
+ }
+
+ /**
+ * Constructor with parameters
+ *
+ * @param vi The elements of the array containing the number of Vinegar
+ * variables per layer are set to the values of the input array.
+ */
+ public RainbowParameters(int[] vi)
+ {
+ this.vi = vi;
+ try
+ {
+ checkParams();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void checkParams()
+ throws Exception
+ {
+ if (vi == null)
+ {
+ throw new Exception("no layers defined.");
+ }
+ if (vi.length > 1)
+ {
+ for (int i = 0; i < vi.length - 1; i++)
+ {
+ if (vi[i] >= vi[i + 1])
+ {
+ throw new Exception(
+ "v[i] has to be smaller than v[i+1]");
+ }
+ }
+ }
+ else
+ {
+ throw new Exception(
+ "Rainbow needs at least 1 layer, such that v1 < v2.");
+ }
+ }
+
+ /**
+ * Getter for the number of layers
+ *
+ * @return the number of layers
+ */
+ public int getNumOfLayers()
+ {
+ return this.vi.length - 1;
+ }
+
+ /**
+ * Getter for the number of all the polynomials in Rainbow
+ *
+ * @return the number of the polynomials
+ */
+ public int getDocLength()
+ {
+ return vi[vi.length - 1] - vi[0];
+ }
+
+ /**
+ * Getter for the array containing the number of Vinegar-variables per layer
+ *
+ * @return the numbers of vinegars per layer
+ */
+ public int[] getVi()
+ {
+ return this.vi;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowPrivateKeyParameters.java
new file mode 100644
index 00000000..98768820
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowPrivateKeyParameters.java
@@ -0,0 +1,117 @@
+package org.bouncycastle.pqc.crypto.rainbow;
+
+public class RainbowPrivateKeyParameters
+ extends RainbowKeyParameters
+{
+ /**
+ * Constructor
+ *
+ * @param A1inv the inverse of A1(the matrix part of the affine linear map L1)
+ * (n-v1 x n-v1 matrix)
+ * @param b1 translation vector, part of the linear affine map L1
+ * @param A2inv the inverse of A2(the matrix part of the affine linear map L2)
+ * (n x n matrix)
+ * @param b2 translation vector, part of the linear affine map L2
+ * @param vi the number of Vinegar-variables per layer
+ * @param layers the polynomials with their coefficients of private map F
+ */
+ public RainbowPrivateKeyParameters(short[][] A1inv, short[] b1,
+ short[][] A2inv, short[] b2, int[] vi, Layer[] layers)
+ {
+ super(true, vi[vi.length - 1] - vi[0]);
+
+ this.A1inv = A1inv;
+ this.b1 = b1;
+ this.A2inv = A2inv;
+ this.b2 = b2;
+ this.vi = vi;
+ this.layers = layers;
+ }
+
+ /*
+ * invertible affine linear map L1
+ */
+ // the inverse of A1, (n-v1 x n-v1 matrix)
+ private short[][] A1inv;
+
+ // translation vector of L1
+ private short[] b1;
+
+ /*
+ * invertible affine linear map L2
+ */
+ // the inverse of A2, (n x n matrix)
+ private short[][] A2inv;
+
+ // translation vector of L2
+ private short[] b2;
+
+ /*
+ * components of F
+ */
+ // the number of Vinegar-variables per layer.
+ private int[] vi;
+
+ // contains the polynomials with their coefficients of private map F
+ private Layer[] layers;
+
+ /**
+ * Getter for the translation part of the private quadratic map L1.
+ *
+ * @return b1 the translation part of L1
+ */
+ public short[] getB1()
+ {
+ return this.b1;
+ }
+
+ /**
+ * Getter for the inverse matrix of A1.
+ *
+ * @return the A1inv inverse
+ */
+ public short[][] getInvA1()
+ {
+ return this.A1inv;
+ }
+
+ /**
+ * Getter for the translation part of the private quadratic map L2.
+ *
+ * @return b2 the translation part of L2
+ */
+ public short[] getB2()
+ {
+ return this.b2;
+ }
+
+ /**
+ * Getter for the inverse matrix of A2
+ *
+ * @return the A2inv
+ */
+ public short[][] getInvA2()
+ {
+ return this.A2inv;
+ }
+
+ /**
+ * Returns the layers contained in the private key
+ *
+ * @return layers
+ */
+ public Layer[] getLayers()
+ {
+ return this.layers;
+ }
+
+ /**
+ * /** Returns the array of vi-s
+ *
+ * @return the vi
+ */
+ public int[] getVi()
+ {
+ return vi;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowPublicKeyParameters.java
new file mode 100644
index 00000000..6f3e46f2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowPublicKeyParameters.java
@@ -0,0 +1,53 @@
+package org.bouncycastle.pqc.crypto.rainbow;
+
+public class RainbowPublicKeyParameters
+ extends RainbowKeyParameters
+{
+ private short[][] coeffquadratic;
+ private short[][] coeffsingular;
+ private short[] coeffscalar;
+
+ /**
+ * Constructor
+ *
+ * @param docLength
+ * @param coeffQuadratic
+ * @param coeffSingular
+ * @param coeffScalar
+ */
+ public RainbowPublicKeyParameters(int docLength,
+ short[][] coeffQuadratic, short[][] coeffSingular,
+ short[] coeffScalar)
+ {
+ super(false, docLength);
+
+ this.coeffquadratic = coeffQuadratic;
+ this.coeffsingular = coeffSingular;
+ this.coeffscalar = coeffScalar;
+
+ }
+
+ /**
+ * @return the coeffquadratic
+ */
+ public short[][] getCoeffQuadratic()
+ {
+ return coeffquadratic;
+ }
+
+ /**
+ * @return the coeffsingular
+ */
+ public short[][] getCoeffSingular()
+ {
+ return coeffsingular;
+ }
+
+ /**
+ * @return the coeffscalar
+ */
+ public short[] getCoeffScalar()
+ {
+ return coeffscalar;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowSigner.java
new file mode 100644
index 00000000..b6014a54
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowSigner.java
@@ -0,0 +1,301 @@
+package org.bouncycastle.pqc.crypto.rainbow;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.pqc.crypto.MessageSigner;
+import org.bouncycastle.pqc.crypto.rainbow.util.ComputeInField;
+import org.bouncycastle.pqc.crypto.rainbow.util.GF2Field;
+
+/**
+ * It implements the sign and verify functions for the Rainbow Signature Scheme.
+ * Here the message, which has to be signed, is updated. The use of
+ * different hash functions is possible.
+ * <p/>
+ * Detailed information about the signature and the verify-method is to be found
+ * in the paper of Jintai Ding, Dieter Schmidt: Rainbow, a New Multivariable
+ * Polynomial Signature Scheme. ACNS 2005: 164-175
+ * (http://dx.doi.org/10.1007/11496137_12)
+ */
+public class RainbowSigner
+ implements MessageSigner
+{
+ // Source of randomness
+ private SecureRandom random;
+
+ // The length of a document that can be signed with the privKey
+ int signableDocumentLength;
+
+ // Container for the oil and vinegar variables of all the layers
+ private short[] x;
+
+ private ComputeInField cf = new ComputeInField();
+
+ RainbowKeyParameters key;
+
+ public void init(boolean forSigning,
+ CipherParameters param)
+ {
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.random = rParam.getRandom();
+ this.key = (RainbowPrivateKeyParameters)rParam.getParameters();
+
+ }
+ else
+ {
+
+ this.random = new SecureRandom();
+ this.key = (RainbowPrivateKeyParameters)param;
+ }
+ }
+ else
+ {
+ this.key = (RainbowPublicKeyParameters)param;
+ }
+
+ this.signableDocumentLength = this.key.getDocLength();
+ }
+
+
+ /**
+ * initial operations before solving the Linear equation system.
+ *
+ * @param layer the current layer for which a LES is to be solved.
+ * @param msg the message that should be signed.
+ * @return Y_ the modified document needed for solving LES, (Y_ =
+ * A1^{-1}*(Y-b1)) linear map L1 = A1 x + b1.
+ */
+ private short[] initSign(Layer[] layer, short[] msg)
+ {
+
+ /* preparation: Modifies the document with the inverse of L1 */
+ // tmp = Y - b1:
+ short[] tmpVec = new short[msg.length];
+
+ tmpVec = cf.addVect(((RainbowPrivateKeyParameters)this.key).getB1(), msg);
+
+ // Y_ = A1^{-1} * (Y - b1) :
+ short[] Y_ = cf.multiplyMatrix(((RainbowPrivateKeyParameters)this.key).getInvA1(), tmpVec);
+
+ /* generates the vinegar vars of the first layer at random */
+ for (int i = 0; i < layer[0].getVi(); i++)
+ {
+ x[i] = (short)random.nextInt();
+ x[i] = (short)(x[i] & GF2Field.MASK);
+ }
+
+ return Y_;
+ }
+
+ /**
+ * This function signs the message that has been updated, making use of the
+ * private key.
+ * <p/>
+ * For computing the signature, L1 and L2 are needed, as well as LES should
+ * be solved for each layer in order to find the Oil-variables in the layer.
+ * <p/>
+ * The Vinegar-variables of the first layer are random generated.
+ *
+ * @param message the message
+ * @return the signature of the message.
+ */
+ public byte[] generateSignature(byte[] message)
+ {
+ Layer[] layer = ((RainbowPrivateKeyParameters)this.key).getLayers();
+ int numberOfLayers = layer.length;
+
+ x = new short[((RainbowPrivateKeyParameters)this.key).getInvA2().length]; // all variables
+
+ short[] Y_; // modified document
+ short[] y_i; // part of Y_ each polynomial
+ int counter; // index of the current part of the doc
+
+ short[] solVec; // the solution of LES pro layer
+ short[] tmpVec;
+
+ // the signature as an array of shorts:
+ short[] signature;
+ // the signature as a byte-array:
+ byte[] S = new byte[layer[numberOfLayers - 1].getViNext()];
+
+ short[] msgHashVals = makeMessageRepresentative(message);
+
+ // shows if an exception is caught
+ boolean ok;
+ do
+ {
+ ok = true;
+ counter = 0;
+ try
+ {
+ Y_ = initSign(layer, msgHashVals);
+
+ for (int i = 0; i < numberOfLayers; i++)
+ {
+
+ y_i = new short[layer[i].getOi()];
+ solVec = new short[layer[i].getOi()]; // solution of LES
+
+ /* copy oi elements of Y_ into y_i */
+ for (int k = 0; k < layer[i].getOi(); k++)
+ {
+ y_i[k] = Y_[counter];
+ counter++; // current index of Y_
+ }
+
+ /*
+ * plug in the vars of the previous layer in order to get
+ * the vars of the current layer
+ */
+ solVec = cf.solveEquation(layer[i].plugInVinegars(x), y_i);
+
+ if (solVec == null)
+ { // LES is not solveable
+ throw new Exception("LES is not solveable!");
+ }
+
+ /* copy the new vars into the x-array */
+ for (int j = 0; j < solVec.length; j++)
+ {
+ x[layer[i].getVi() + j] = solVec[j];
+ }
+ }
+
+ /* apply the inverse of L2: (signature = A2^{-1}*(b2+x)) */
+ tmpVec = cf.addVect(((RainbowPrivateKeyParameters)this.key).getB2(), x);
+ signature = cf.multiplyMatrix(((RainbowPrivateKeyParameters)this.key).getInvA2(), tmpVec);
+
+ /* cast signature from short[] to byte[] */
+ for (int i = 0; i < S.length; i++)
+ {
+ S[i] = ((byte)signature[i]);
+ }
+ }
+ catch (Exception se)
+ {
+ // if one of the LESs was not solveable - sign again
+ ok = false;
+ }
+ }
+ while (!ok);
+ /* return the signature in bytes */
+ return S;
+ }
+
+ /**
+ * This function verifies the signature of the message that has been
+ * updated, with the aid of the public key.
+ *
+ * @param message the message
+ * @param signature the signature of the message
+ * @return true if the signature has been verified, false otherwise.
+ */
+ public boolean verifySignature(byte[] message, byte[] signature)
+ {
+ short[] sigInt = new short[signature.length];
+ short tmp;
+
+ for (int i = 0; i < signature.length; i++)
+ {
+ tmp = (short)signature[i];
+ tmp &= (short)0xff;
+ sigInt[i] = tmp;
+ }
+
+ short[] msgHashVal = makeMessageRepresentative(message);
+
+ // verify
+ short[] verificationResult = verifySignatureIntern(sigInt);
+
+ // compare
+ boolean verified = true;
+ if (msgHashVal.length != verificationResult.length)
+ {
+ return false;
+ }
+ for (int i = 0; i < msgHashVal.length; i++)
+ {
+ verified = verified && msgHashVal[i] == verificationResult[i];
+ }
+
+ return verified;
+ }
+
+ /**
+ * Signature verification using public key
+ *
+ * @param signature vector of dimension n
+ * @return document hash of length n - v1
+ */
+ private short[] verifySignatureIntern(short[] signature)
+ {
+
+ short[][] coeff_quadratic = ((RainbowPublicKeyParameters)this.key).getCoeffQuadratic();
+ short[][] coeff_singular = ((RainbowPublicKeyParameters)this.key).getCoeffSingular();
+ short[] coeff_scalar = ((RainbowPublicKeyParameters)this.key).getCoeffScalar();
+
+ short[] rslt = new short[coeff_quadratic.length];// n - v1
+ int n = coeff_singular[0].length;
+ int offset = 0; // array position
+ short tmp = 0; // for scalar
+
+ for (int p = 0; p < coeff_quadratic.length; p++)
+ { // no of polynomials
+ offset = 0;
+ for (int x = 0; x < n; x++)
+ {
+ // calculate quadratic terms
+ for (int y = x; y < n; y++)
+ {
+ tmp = GF2Field.multElem(coeff_quadratic[p][offset],
+ GF2Field.multElem(signature[x], signature[y]));
+ rslt[p] = GF2Field.addElem(rslt[p], tmp);
+ offset++;
+ }
+ // calculate singular terms
+ tmp = GF2Field.multElem(coeff_singular[p][x], signature[x]);
+ rslt[p] = GF2Field.addElem(rslt[p], tmp);
+ }
+ // add scalar
+ rslt[p] = GF2Field.addElem(rslt[p], coeff_scalar[p]);
+ }
+
+ return rslt;
+ }
+
+ /**
+ * This function creates the representative of the message which gets signed
+ * or verified.
+ *
+ * @param message the message
+ * @return message representative
+ */
+ private short[] makeMessageRepresentative(byte[] message)
+ {
+ // the message representative
+ short[] output = new short[this.signableDocumentLength];
+
+ int h = 0;
+ int i = 0;
+ do
+ {
+ if (i >= message.length)
+ {
+ break;
+ }
+ output[i] = (short)message[h];
+ output[i] &= (short)0xff;
+ h++;
+ i++;
+ }
+ while (i < output.length);
+
+ return output;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/util/ComputeInField.java b/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/util/ComputeInField.java
new file mode 100644
index 00000000..9a1115da
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/util/ComputeInField.java
@@ -0,0 +1,490 @@
+package org.bouncycastle.pqc.crypto.rainbow.util;
+
+/**
+ * This class offers different operations on matrices in field GF2^8.
+ * <p/>
+ * Implemented are functions:
+ * - finding inverse of a matrix
+ * - solving linear equation systems using the Gauss-Elimination method
+ * - basic operations like matrix multiplication, addition and so on.
+ */
+
+public class ComputeInField
+{
+
+ private short[][] A; // used by solveEquation and inverse
+ short[] x;
+
+ /**
+ * Constructor with no parameters
+ */
+ public ComputeInField()
+ {
+ }
+
+
+ /**
+ * This function finds a solution of the equation Bx = b.
+ * Exception is thrown if the linear equation system has no solution
+ *
+ * @param B this matrix is the left part of the
+ * equation (B in the equation above)
+ * @param b the right part of the equation
+ * (b in the equation above)
+ * @return x the solution of the equation if it is solvable
+ * null otherwise
+ * @throws RuntimeException if LES is not solvable
+ */
+ public short[] solveEquation(short[][] B, short[] b)
+ {
+ try
+ {
+
+ if (B.length != b.length)
+ {
+ throw new RuntimeException(
+ "The equation system is not solvable");
+ }
+
+ /** initialize **/
+ // this matrix stores B and b from the equation B*x = b
+ // b is stored as the last column.
+ // B contains one column more than rows.
+ // In this column we store a free coefficient that should be later subtracted from b
+ A = new short[B.length][B.length + 1];
+ // stores the solution of the LES
+ x = new short[B.length];
+
+ /** copy B into the global matrix A **/
+ for (int i = 0; i < B.length; i++)
+ { // rows
+ for (int j = 0; j < B[0].length; j++)
+ { // cols
+ A[i][j] = B[i][j];
+ }
+ }
+
+ /** copy the vector b into the global A **/
+ //the free coefficient, stored in the last column of A( A[i][b.length]
+ // is to be subtracted from b
+ for (int i = 0; i < b.length; i++)
+ {
+ A[i][b.length] = GF2Field.addElem(b[i], A[i][b.length]);
+ }
+
+ /** call the methods for gauss elimination and backward substitution **/
+ computeZerosUnder(false); // obtain zeros under the diagonal
+ substitute();
+
+ return x;
+
+ }
+ catch (RuntimeException rte)
+ {
+ return null; // the LES is not solvable!
+ }
+ }
+
+ /**
+ * This function computes the inverse of a given matrix using the Gauss-
+ * Elimination method.
+ * <p/>
+ * An exception is thrown if the matrix has no inverse
+ *
+ * @param coef the matrix which inverse matrix is needed
+ * @return inverse matrix of the input matrix.
+ * If the matrix is singular, null is returned.
+ * @throws RuntimeException if the given matrix is not invertible
+ */
+ public short[][] inverse(short[][] coef)
+ {
+ try
+ {
+ /** Initialization: **/
+ short factor;
+ short[][] inverse;
+ A = new short[coef.length][2 * coef.length];
+ if (coef.length != coef[0].length)
+ {
+ throw new RuntimeException(
+ "The matrix is not invertible. Please choose another one!");
+ }
+
+ /** prepare: Copy coef and the identity matrix into the global A. **/
+ for (int i = 0; i < coef.length; i++)
+ {
+ for (int j = 0; j < coef.length; j++)
+ {
+ //copy the input matrix coef into A
+ A[i][j] = coef[i][j];
+ }
+ // copy the identity matrix into A.
+ for (int j = coef.length; j < 2 * coef.length; j++)
+ {
+ A[i][j] = 0;
+ }
+ A[i][i + A.length] = 1;
+ }
+
+ /** Elimination operations to get the identity matrix from the left side of A. **/
+ // modify A to get 0s under the diagonal.
+ computeZerosUnder(true);
+
+ // modify A to get only 1s on the diagonal: A[i][j] =A[i][j]/A[i][i].
+ for (int i = 0; i < A.length; i++)
+ {
+ factor = GF2Field.invElem(A[i][i]);
+ for (int j = i; j < 2 * A.length; j++)
+ {
+ A[i][j] = GF2Field.multElem(A[i][j], factor);
+ }
+ }
+
+ //modify A to get only 0s above the diagonal.
+ computeZerosAbove();
+
+ // copy the result (the second half of A) in the matrix inverse.
+ inverse = new short[A.length][A.length];
+ for (int i = 0; i < A.length; i++)
+ {
+ for (int j = A.length; j < 2 * A.length; j++)
+ {
+ inverse[i][j - A.length] = A[i][j];
+ }
+ }
+ return inverse;
+
+ }
+ catch (RuntimeException rte)
+ {
+ // The matrix is not invertible! A new one should be generated!
+ return null;
+ }
+ }
+
+ /**
+ * Elimination under the diagonal.
+ * This function changes a matrix so that it contains only zeros under the
+ * diagonal(Ai,i) using only Gauss-Elimination operations.
+ * <p/>
+ * It is used in solveEquaton as well as in the function for
+ * finding an inverse of a matrix: {@link}inverse. Both of them use the
+ * Gauss-Elimination Method.
+ * <p/>
+ * The result is stored in the global matrix A
+ *
+ * @param usedForInverse This parameter shows if the function is used by the
+ * solveEquation-function or by the inverse-function and according
+ * to this creates matrices of different sizes.
+ * @throws RuntimeException in case a multiplicative inverse of 0 is needed
+ */
+ private void computeZerosUnder(boolean usedForInverse)
+ throws RuntimeException
+ {
+
+ //the number of columns in the global A where the tmp results are stored
+ int length;
+ short tmp = 0;
+
+ //the function is used in inverse() - A should have 2 times more columns than rows
+ if (usedForInverse)
+ {
+ length = 2 * A.length;
+ }
+ //the function is used in solveEquation - A has 1 column more than rows
+ else
+ {
+ length = A.length + 1;
+ }
+
+ //elimination operations to modify A so that that it contains only 0s under the diagonal
+ for (int k = 0; k < A.length - 1; k++)
+ { // the fixed row
+ for (int i = k + 1; i < A.length; i++)
+ { // rows
+ short factor1 = A[i][k];
+ short factor2 = GF2Field.invElem(A[k][k]);
+
+ //The element which multiplicative inverse is needed, is 0
+ //in this case is the input matrix not invertible
+ if (factor2 == 0)
+ {
+ throw new RuntimeException("Matrix not invertible! We have to choose another one!");
+ }
+
+ for (int j = k; j < length; j++)
+ {// columns
+ // tmp=A[k,j] / A[k,k]
+ tmp = GF2Field.multElem(A[k][j], factor2);
+ // tmp = A[i,k] * A[k,j] / A[k,k]
+ tmp = GF2Field.multElem(factor1, tmp);
+ // A[i,j]=A[i,j]-A[i,k]/A[k,k]*A[k,j];
+ A[i][j] = GF2Field.addElem(A[i][j], tmp);
+ }
+ }
+ }
+ }
+
+ /**
+ * Elimination above the diagonal.
+ * This function changes a matrix so that it contains only zeros above the
+ * diagonal(Ai,i) using only Gauss-Elimination operations.
+ * <p/>
+ * It is used in the inverse-function
+ * The result is stored in the global matrix A
+ *
+ * @throws RuntimeException in case a multiplicative inverse of 0 is needed
+ */
+ private void computeZerosAbove()
+ throws RuntimeException
+ {
+ short tmp = 0;
+ for (int k = A.length - 1; k > 0; k--)
+ { // the fixed row
+ for (int i = k - 1; i >= 0; i--)
+ { // rows
+ short factor1 = A[i][k];
+ short factor2 = GF2Field.invElem(A[k][k]);
+ if (factor2 == 0)
+ {
+ throw new RuntimeException("The matrix is not invertible");
+ }
+ for (int j = k; j < 2 * A.length; j++)
+ { // columns
+ // tmp = A[k,j] / A[k,k]
+ tmp = GF2Field.multElem(A[k][j], factor2);
+ // tmp = A[i,k] * A[k,j] / A[k,k]
+ tmp = GF2Field.multElem(factor1, tmp);
+ // A[i,j] = A[i,j] - A[i,k] / A[k,k] * A[k,j];
+ A[i][j] = GF2Field.addElem(A[i][j], tmp);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * This function uses backward substitution to find x
+ * of the linear equation system (LES) B*x = b,
+ * where A a triangle-matrix is (contains only zeros under the diagonal)
+ * and b is a vector
+ * <p/>
+ * If the multiplicative inverse of 0 is needed, an exception is thrown.
+ * In this case is the LES not solvable
+ *
+ * @throws RuntimeException in case a multiplicative inverse of 0 is needed
+ */
+ private void substitute()
+ throws RuntimeException
+ {
+
+ // for the temporary results of the operations in field
+ short tmp, temp;
+
+ temp = GF2Field.invElem(A[A.length - 1][A.length - 1]);
+ if (temp == 0)
+ {
+ throw new RuntimeException("The equation system is not solvable");
+ }
+
+ /** backward substitution **/
+ x[A.length - 1] = GF2Field.multElem(A[A.length - 1][A.length], temp);
+ for (int i = A.length - 2; i >= 0; i--)
+ {
+ tmp = A[i][A.length];
+ for (int j = A.length - 1; j > i; j--)
+ {
+ temp = GF2Field.multElem(A[i][j], x[j]);
+ tmp = GF2Field.addElem(tmp, temp);
+ }
+
+ temp = GF2Field.invElem(A[i][i]);
+ if (temp == 0)
+ {
+ throw new RuntimeException("Not solvable equation system");
+ }
+ x[i] = GF2Field.multElem(tmp, temp);
+ }
+ }
+
+
+ /**
+ * This function multiplies two given matrices.
+ * If the given matrices cannot be multiplied due
+ * to different sizes, an exception is thrown.
+ *
+ * @param M1 -the 1st matrix
+ * @param M2 -the 2nd matrix
+ * @return A = M1*M2
+ * @throws RuntimeException in case the given matrices cannot be multiplied
+ * due to different dimensions.
+ */
+ public short[][] multiplyMatrix(short[][] M1, short[][] M2)
+ throws RuntimeException
+ {
+
+ if (M1[0].length != M2.length)
+ {
+ throw new RuntimeException("Multiplication is not possible!");
+ }
+ short tmp = 0;
+ A = new short[M1.length][M2[0].length];
+ for (int i = 0; i < M1.length; i++)
+ {
+ for (int j = 0; j < M2.length; j++)
+ {
+ for (int k = 0; k < M2[0].length; k++)
+ {
+ tmp = GF2Field.multElem(M1[i][j], M2[j][k]);
+ A[i][k] = GF2Field.addElem(A[i][k], tmp);
+ }
+ }
+ }
+ return A;
+ }
+
+ /**
+ * This function multiplies a given matrix with a one-dimensional array.
+ * <p/>
+ * An exception is thrown, if the number of columns in the matrix and
+ * the number of rows in the one-dim. array differ.
+ *
+ * @param M1 the matrix to be multiplied
+ * @param m the one-dimensional array to be multiplied
+ * @return M1*m
+ * @throws RuntimeException in case of dimension inconsistency
+ */
+ public short[] multiplyMatrix(short[][] M1, short[] m)
+ throws RuntimeException
+ {
+ if (M1[0].length != m.length)
+ {
+ throw new RuntimeException("Multiplication is not possible!");
+ }
+ short tmp = 0;
+ short[] B = new short[M1.length];
+ for (int i = 0; i < M1.length; i++)
+ {
+ for (int j = 0; j < m.length; j++)
+ {
+ tmp = GF2Field.multElem(M1[i][j], m[j]);
+ B[i] = GF2Field.addElem(B[i], tmp);
+ }
+ }
+ return B;
+ }
+
+ /**
+ * Addition of two vectors
+ *
+ * @param vector1 first summand, always of dim n
+ * @param vector2 second summand, always of dim n
+ * @return addition of vector1 and vector2
+ * @throws RuntimeException in case the addition is impossible
+ * due to inconsistency in the dimensions
+ */
+ public short[] addVect(short[] vector1, short[] vector2)
+ {
+ if (vector1.length != vector2.length)
+ {
+ throw new RuntimeException("Multiplication is not possible!");
+ }
+ short rslt[] = new short[vector1.length];
+ for (int n = 0; n < rslt.length; n++)
+ {
+ rslt[n] = GF2Field.addElem(vector1[n], vector2[n]);
+ }
+ return rslt;
+ }
+
+ /**
+ * Multiplication of column vector with row vector
+ *
+ * @param vector1 column vector, always n x 1
+ * @param vector2 row vector, always 1 x n
+ * @return resulting n x n matrix of multiplication
+ * @throws RuntimeException in case the multiplication is impossible due to
+ * inconsistency in the dimensions
+ */
+ public short[][] multVects(short[] vector1, short[] vector2)
+ {
+ if (vector1.length != vector2.length)
+ {
+ throw new RuntimeException("Multiplication is not possible!");
+ }
+ short rslt[][] = new short[vector1.length][vector2.length];
+ for (int i = 0; i < vector1.length; i++)
+ {
+ for (int j = 0; j < vector2.length; j++)
+ {
+ rslt[i][j] = GF2Field.multElem(vector1[i], vector2[j]);
+ }
+ }
+ return rslt;
+ }
+
+ /**
+ * Multiplies vector with scalar
+ *
+ * @param scalar galois element to multiply vector with
+ * @param vector vector to be multiplied
+ * @return vector multiplied with scalar
+ */
+ public short[] multVect(short scalar, short[] vector)
+ {
+ short rslt[] = new short[vector.length];
+ for (int n = 0; n < rslt.length; n++)
+ {
+ rslt[n] = GF2Field.multElem(scalar, vector[n]);
+ }
+ return rslt;
+ }
+
+ /**
+ * Multiplies matrix with scalar
+ *
+ * @param scalar galois element to multiply matrix with
+ * @param matrix 2-dim n x n matrix to be multiplied
+ * @return matrix multiplied with scalar
+ */
+ public short[][] multMatrix(short scalar, short[][] matrix)
+ {
+ short[][] rslt = new short[matrix.length][matrix[0].length];
+ for (int i = 0; i < matrix.length; i++)
+ {
+ for (int j = 0; j < matrix[0].length; j++)
+ {
+ rslt[i][j] = GF2Field.multElem(scalar, matrix[i][j]);
+ }
+ }
+ return rslt;
+ }
+
+ /**
+ * Adds the n x n matrices matrix1 and matrix2
+ *
+ * @param matrix1 first summand
+ * @param matrix2 second summand
+ * @return addition of matrix1 and matrix2; both having the dimensions n x n
+ * @throws RuntimeException in case the addition is not possible because of
+ * different dimensions of the matrices
+ */
+ public short[][] addSquareMatrix(short[][] matrix1, short[][] matrix2)
+ {
+ if (matrix1.length != matrix2.length || matrix1[0].length != matrix2[0].length)
+ {
+ throw new RuntimeException("Addition is not possible!");
+ }
+
+ short[][] rslt = new short[matrix1.length][matrix1.length];//
+ for (int i = 0; i < matrix1.length; i++)
+ {
+ for (int j = 0; j < matrix2.length; j++)
+ {
+ rslt[i][j] = GF2Field.addElem(matrix1[i][j], matrix2[i][j]);
+ }
+ }
+ return rslt;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/util/GF2Field.java b/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/util/GF2Field.java
new file mode 100644
index 00000000..7c286491
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/util/GF2Field.java
@@ -0,0 +1,139 @@
+package org.bouncycastle.pqc.crypto.rainbow.util;
+
+/**
+ * This class provides the basic operations like addition, multiplication and
+ * finding the multiplicative inverse of an element in GF2^8.
+ * <p/>
+ * The operations are implemented using the irreducible polynomial
+ * 1+x^2+x^3+x^6+x^8 ( 1 0100 1101 = 0x14d )
+ * <p/>
+ * This class makes use of lookup tables(exps and logs) for implementing the
+ * operations in order to increase the efficiency of Rainbow.
+ */
+public class GF2Field
+{
+
+ public static final int MASK = 0xff;
+
+ /*
+ * this lookup table is needed for multiplication and computing the
+ * multiplicative inverse
+ */
+ static final short exps[] = {1, 2, 4, 8, 16, 32, 64, 128, 77, 154, 121, 242,
+ 169, 31, 62, 124, 248, 189, 55, 110, 220, 245, 167, 3, 6, 12, 24,
+ 48, 96, 192, 205, 215, 227, 139, 91, 182, 33, 66, 132, 69, 138, 89,
+ 178, 41, 82, 164, 5, 10, 20, 40, 80, 160, 13, 26, 52, 104, 208,
+ 237, 151, 99, 198, 193, 207, 211, 235, 155, 123, 246, 161, 15, 30,
+ 60, 120, 240, 173, 23, 46, 92, 184, 61, 122, 244, 165, 7, 14, 28,
+ 56, 112, 224, 141, 87, 174, 17, 34, 68, 136, 93, 186, 57, 114, 228,
+ 133, 71, 142, 81, 162, 9, 18, 36, 72, 144, 109, 218, 249, 191, 51,
+ 102, 204, 213, 231, 131, 75, 150, 97, 194, 201, 223, 243, 171, 27,
+ 54, 108, 216, 253, 183, 35, 70, 140, 85, 170, 25, 50, 100, 200,
+ 221, 247, 163, 11, 22, 44, 88, 176, 45, 90, 180, 37, 74, 148, 101,
+ 202, 217, 255, 179, 43, 86, 172, 21, 42, 84, 168, 29, 58, 116, 232,
+ 157, 119, 238, 145, 111, 222, 241, 175, 19, 38, 76, 152, 125, 250,
+ 185, 63, 126, 252, 181, 39, 78, 156, 117, 234, 153, 127, 254, 177,
+ 47, 94, 188, 53, 106, 212, 229, 135, 67, 134, 65, 130, 73, 146,
+ 105, 210, 233, 159, 115, 230, 129, 79, 158, 113, 226, 137, 95, 190,
+ 49, 98, 196, 197, 199, 195, 203, 219, 251, 187, 59, 118, 236, 149,
+ 103, 206, 209, 239, 147, 107, 214, 225, 143, 83, 166, 1};
+
+ /*
+ * this lookup table is needed for multiplication and computing the
+ * multiplicative inverse
+ */
+ static final short logs[] = {0, 0, 1, 23, 2, 46, 24, 83, 3, 106, 47, 147,
+ 25, 52, 84, 69, 4, 92, 107, 182, 48, 166, 148, 75, 26, 140, 53,
+ 129, 85, 170, 70, 13, 5, 36, 93, 135, 108, 155, 183, 193, 49, 43,
+ 167, 163, 149, 152, 76, 202, 27, 230, 141, 115, 54, 205, 130, 18,
+ 86, 98, 171, 240, 71, 79, 14, 189, 6, 212, 37, 210, 94, 39, 136,
+ 102, 109, 214, 156, 121, 184, 8, 194, 223, 50, 104, 44, 253, 168,
+ 138, 164, 90, 150, 41, 153, 34, 77, 96, 203, 228, 28, 123, 231, 59,
+ 142, 158, 116, 244, 55, 216, 206, 249, 131, 111, 19, 178, 87, 225,
+ 99, 220, 172, 196, 241, 175, 72, 10, 80, 66, 15, 186, 190, 199, 7,
+ 222, 213, 120, 38, 101, 211, 209, 95, 227, 40, 33, 137, 89, 103,
+ 252, 110, 177, 215, 248, 157, 243, 122, 58, 185, 198, 9, 65, 195,
+ 174, 224, 219, 51, 68, 105, 146, 45, 82, 254, 22, 169, 12, 139,
+ 128, 165, 74, 91, 181, 151, 201, 42, 162, 154, 192, 35, 134, 78,
+ 188, 97, 239, 204, 17, 229, 114, 29, 61, 124, 235, 232, 233, 60,
+ 234, 143, 125, 159, 236, 117, 30, 245, 62, 56, 246, 217, 63, 207,
+ 118, 250, 31, 132, 160, 112, 237, 20, 144, 179, 126, 88, 251, 226,
+ 32, 100, 208, 221, 119, 173, 218, 197, 64, 242, 57, 176, 247, 73,
+ 180, 11, 127, 81, 21, 67, 145, 16, 113, 187, 238, 191, 133, 200,
+ 161};
+
+ /**
+ * This function calculates the sum of two elements as an operation in GF2^8
+ *
+ * @param x the first element that is to be added
+ * @param y the second element that should be add
+ * @return the sum of the two elements x and y in GF2^8
+ */
+ public static short addElem(short x, short y)
+ {
+ return (short)(x ^ y);
+ }
+
+ /**
+ * This function computes the multiplicative inverse of a given element in
+ * GF2^8 The 0 has no multiplicative inverse and in this case 0 is returned.
+ *
+ * @param x the element which multiplicative inverse is to be computed
+ * @return the multiplicative inverse of the given element, in case it
+ * exists or 0, otherwise
+ */
+ public static short invElem(short x)
+ {
+ if (x == 0)
+ {
+ return 0;
+ }
+ return (exps[255 - logs[x]]);
+ }
+
+ /**
+ * This function multiplies two elements in GF2^8. If one of the two
+ * elements is 0, 0 is returned.
+ *
+ * @param x the first element to be multiplied.
+ * @param y the second element to be multiplied.
+ * @return the product of the two input elements in GF2^8.
+ */
+ public static short multElem(short x, short y)
+ {
+ if (x == 0 || y == 0)
+ {
+ return 0;
+ }
+ else
+ {
+ return (exps[(logs[x] + logs[y]) % 255]);
+ }
+ }
+
+ /**
+ * This function returns the values of exps-lookup table which correspond to
+ * the input
+ *
+ * @param x the index in the lookup table exps
+ * @return exps-value, corresponding to the input
+ */
+ public static short getExp(short x)
+ {
+ return exps[x];
+ }
+
+ /**
+ * This function returns the values of logs-lookup table which correspond to
+ * the input
+ *
+ * @param x the index in the lookup table logs
+ * @return logs-value, corresponding to the input
+ */
+ public static short getLog(short x)
+ {
+ return logs[x];
+ }
+
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/util/RainbowUtil.java b/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/util/RainbowUtil.java
new file mode 100644
index 00000000..2b073b1b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/crypto/rainbow/util/RainbowUtil.java
@@ -0,0 +1,230 @@
+package org.bouncycastle.pqc.crypto.rainbow.util;
+
+/**
+ * This class is needed for the conversions while encoding and decoding, as well as for
+ * comparison between arrays of some dimensions
+ */
+public class RainbowUtil
+{
+
+ /**
+ * This function converts an one-dimensional array of bytes into a
+ * one-dimensional array of int
+ *
+ * @param in the array to be converted
+ * @return out
+ * the one-dimensional int-array that corresponds the input
+ */
+ public static int[] convertArraytoInt(byte[] in)
+ {
+ int[] out = new int[in.length];
+ for (int i = 0; i < in.length; i++)
+ {
+ out[i] = in[i] & GF2Field.MASK;
+ }
+ return out;
+ }
+
+ /**
+ * This function converts an one-dimensional array of bytes into a
+ * one-dimensional array of type short
+ *
+ * @param in the array to be converted
+ * @return out
+ * one-dimensional short-array that corresponds the input
+ */
+ public static short[] convertArray(byte[] in)
+ {
+ short[] out = new short[in.length];
+ for (int i = 0; i < in.length; i++)
+ {
+ out[i] = (short)(in[i] & GF2Field.MASK);
+ }
+ return out;
+ }
+
+ /**
+ * This function converts a matrix of bytes into a matrix of type short
+ *
+ * @param in the matrix to be converted
+ * @return out
+ * short-matrix that corresponds the input
+ */
+ public static short[][] convertArray(byte[][] in)
+ {
+ short[][] out = new short[in.length][in[0].length];
+ for (int i = 0; i < in.length; i++)
+ {
+ for (int j = 0; j < in[0].length; j++)
+ {
+ out[i][j] = (short)(in[i][j] & GF2Field.MASK);
+ }
+ }
+ return out;
+ }
+
+ /**
+ * This function converts a 3-dimensional array of bytes into a 3-dimensional array of type short
+ *
+ * @param in the array to be converted
+ * @return out
+ * short-array that corresponds the input
+ */
+ public static short[][][] convertArray(byte[][][] in)
+ {
+ short[][][] out = new short[in.length][in[0].length][in[0][0].length];
+ for (int i = 0; i < in.length; i++)
+ {
+ for (int j = 0; j < in[0].length; j++)
+ {
+ for (int k = 0; k < in[0][0].length; k++)
+ {
+ out[i][j][k] = (short)(in[i][j][k] & GF2Field.MASK);
+ }
+ }
+ }
+ return out;
+ }
+
+ /**
+ * This function converts an array of type int into an array of type byte
+ *
+ * @param in the array to be converted
+ * @return out
+ * the byte-array that corresponds the input
+ */
+ public static byte[] convertIntArray(int[] in)
+ {
+ byte[] out = new byte[in.length];
+ for (int i = 0; i < in.length; i++)
+ {
+ out[i] = (byte)in[i];
+ }
+ return out;
+ }
+
+
+ /**
+ * This function converts an array of type short into an array of type byte
+ *
+ * @param in the array to be converted
+ * @return out
+ * the byte-array that corresponds the input
+ */
+ public static byte[] convertArray(short[] in)
+ {
+ byte[] out = new byte[in.length];
+ for (int i = 0; i < in.length; i++)
+ {
+ out[i] = (byte)in[i];
+ }
+ return out;
+ }
+
+ /**
+ * This function converts a matrix of type short into a matrix of type byte
+ *
+ * @param in the matrix to be converted
+ * @return out
+ * the byte-matrix that corresponds the input
+ */
+ public static byte[][] convertArray(short[][] in)
+ {
+ byte[][] out = new byte[in.length][in[0].length];
+ for (int i = 0; i < in.length; i++)
+ {
+ for (int j = 0; j < in[0].length; j++)
+ {
+ out[i][j] = (byte)in[i][j];
+ }
+ }
+ return out;
+ }
+
+ /**
+ * This function converts a 3-dimensional array of type short into a 3-dimensional array of type byte
+ *
+ * @param in the array to be converted
+ * @return out
+ * the byte-array that corresponds the input
+ */
+ public static byte[][][] convertArray(short[][][] in)
+ {
+ byte[][][] out = new byte[in.length][in[0].length][in[0][0].length];
+ for (int i = 0; i < in.length; i++)
+ {
+ for (int j = 0; j < in[0].length; j++)
+ {
+ for (int k = 0; k < in[0][0].length; k++)
+ {
+ out[i][j][k] = (byte)in[i][j][k];
+ }
+ }
+ }
+ return out;
+ }
+
+ /**
+ * Compare two short arrays. No null checks are performed.
+ *
+ * @param left the first short array
+ * @param right the second short array
+ * @return the result of the comparison
+ */
+ public static boolean equals(short[] left, short[] right)
+ {
+ if (left.length != right.length)
+ {
+ return false;
+ }
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ result &= left[i] == right[i];
+ }
+ return result;
+ }
+
+ /**
+ * Compare two two-dimensional short arrays. No null checks are performed.
+ *
+ * @param left the first short array
+ * @param right the second short array
+ * @return the result of the comparison
+ */
+ public static boolean equals(short[][] left, short[][] right)
+ {
+ if (left.length != right.length)
+ {
+ return false;
+ }
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ result &= equals(left[i], right[i]);
+ }
+ return result;
+ }
+
+ /**
+ * Compare two three-dimensional short arrays. No null checks are performed.
+ *
+ * @param left the first short array
+ * @param right the second short array
+ * @return the result of the comparison
+ */
+ public static boolean equals(short[][][] left, short[][][] right)
+ {
+ if (left.length != right.length)
+ {
+ return false;
+ }
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ result &= equals(left[i], right[i]);
+ }
+ return result;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/BigEndianConversions.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/BigEndianConversions.java
new file mode 100644
index 00000000..90926f67
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/BigEndianConversions.java
@@ -0,0 +1,306 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+
+/**
+ * This is a utility class containing data type conversions using big-endian
+ * byte order.
+ *
+ * @see LittleEndianConversions
+ */
+public final class BigEndianConversions
+{
+
+ /**
+ * Default constructor (private).
+ */
+ private BigEndianConversions()
+ {
+ // empty
+ }
+
+ /**
+ * Convert an integer to an octet string of length 4 according to IEEE 1363,
+ * Section 5.5.3.
+ *
+ * @param x the integer to convert
+ * @return the converted integer
+ */
+ public static byte[] I2OSP(int x)
+ {
+ byte[] result = new byte[4];
+ result[0] = (byte)(x >>> 24);
+ result[1] = (byte)(x >>> 16);
+ result[2] = (byte)(x >>> 8);
+ result[3] = (byte)x;
+ return result;
+ }
+
+ /**
+ * Convert an integer to an octet string according to IEEE 1363, Section
+ * 5.5.3. Length checking is performed.
+ *
+ * @param x the integer to convert
+ * @param oLen the desired length of the octet string
+ * @return an octet string of length <tt>oLen</tt> representing the
+ * integer <tt>x</tt>, or <tt>null</tt> if the integer is
+ * negative
+ * @throws ArithmeticException if <tt>x</tt> can't be encoded into <tt>oLen</tt>
+ * octets.
+ */
+ public static byte[] I2OSP(int x, int oLen)
+ throws ArithmeticException
+ {
+ if (x < 0)
+ {
+ return null;
+ }
+ int octL = IntegerFunctions.ceilLog256(x);
+ if (octL > oLen)
+ {
+ throw new ArithmeticException(
+ "Cannot encode given integer into specified number of octets.");
+ }
+ byte[] result = new byte[oLen];
+ for (int i = oLen - 1; i >= oLen - octL; i--)
+ {
+ result[i] = (byte)(x >>> (8 * (oLen - 1 - i)));
+ }
+ return result;
+ }
+
+ /**
+ * Convert an integer to an octet string of length 4 according to IEEE 1363,
+ * Section 5.5.3.
+ *
+ * @param input the integer to convert
+ * @param output byte array holding the output
+ * @param outOff offset in output array where the result is stored
+ */
+ public static void I2OSP(int input, byte[] output, int outOff)
+ {
+ output[outOff++] = (byte)(input >>> 24);
+ output[outOff++] = (byte)(input >>> 16);
+ output[outOff++] = (byte)(input >>> 8);
+ output[outOff] = (byte)input;
+ }
+
+ /**
+ * Convert an integer to an octet string of length 8 according to IEEE 1363,
+ * Section 5.5.3.
+ *
+ * @param input the integer to convert
+ * @return the converted integer
+ */
+ public static byte[] I2OSP(long input)
+ {
+ byte[] output = new byte[8];
+ output[0] = (byte)(input >>> 56);
+ output[1] = (byte)(input >>> 48);
+ output[2] = (byte)(input >>> 40);
+ output[3] = (byte)(input >>> 32);
+ output[4] = (byte)(input >>> 24);
+ output[5] = (byte)(input >>> 16);
+ output[6] = (byte)(input >>> 8);
+ output[7] = (byte)input;
+ return output;
+ }
+
+ /**
+ * Convert an integer to an octet string of length 8 according to IEEE 1363,
+ * Section 5.5.3.
+ *
+ * @param input the integer to convert
+ * @param output byte array holding the output
+ * @param outOff offset in output array where the result is stored
+ */
+ public static void I2OSP(long input, byte[] output, int outOff)
+ {
+ output[outOff++] = (byte)(input >>> 56);
+ output[outOff++] = (byte)(input >>> 48);
+ output[outOff++] = (byte)(input >>> 40);
+ output[outOff++] = (byte)(input >>> 32);
+ output[outOff++] = (byte)(input >>> 24);
+ output[outOff++] = (byte)(input >>> 16);
+ output[outOff++] = (byte)(input >>> 8);
+ output[outOff] = (byte)input;
+ }
+
+ /**
+ * Convert an integer to an octet string of the specified length according
+ * to IEEE 1363, Section 5.5.3. No length checking is performed (i.e., if
+ * the integer cannot be encoded into <tt>length</tt> octets, it is
+ * truncated).
+ *
+ * @param input the integer to convert
+ * @param output byte array holding the output
+ * @param outOff offset in output array where the result is stored
+ * @param length the length of the encoding
+ */
+ public static void I2OSP(int input, byte[] output, int outOff, int length)
+ {
+ for (int i = length - 1; i >= 0; i--)
+ {
+ output[outOff + i] = (byte)(input >>> (8 * (length - 1 - i)));
+ }
+ }
+
+ /**
+ * Convert an octet string to an integer according to IEEE 1363, Section
+ * 5.5.3.
+ *
+ * @param input the byte array holding the octet string
+ * @return an integer representing the octet string <tt>input</tt>, or
+ * <tt>0</tt> if the represented integer is negative or too large
+ * or the byte array is empty
+ * @throws ArithmeticException if the length of the given octet string is larger than 4.
+ */
+ public static int OS2IP(byte[] input)
+ {
+ if (input.length > 4)
+ {
+ throw new ArithmeticException("invalid input length");
+ }
+ if (input.length == 0)
+ {
+ return 0;
+ }
+ int result = 0;
+ for (int j = 0; j < input.length; j++)
+ {
+ result |= (input[j] & 0xff) << (8 * (input.length - 1 - j));
+ }
+ return result;
+ }
+
+ /**
+ * Convert a byte array of length 4 beginning at <tt>offset</tt> into an
+ * integer.
+ *
+ * @param input the byte array
+ * @param inOff the offset into the byte array
+ * @return the resulting integer
+ */
+ public static int OS2IP(byte[] input, int inOff)
+ {
+ int result = (input[inOff++] & 0xff) << 24;
+ result |= (input[inOff++] & 0xff) << 16;
+ result |= (input[inOff++] & 0xff) << 8;
+ result |= input[inOff] & 0xff;
+ return result;
+ }
+
+ /**
+ * Convert an octet string to an integer according to IEEE 1363, Section
+ * 5.5.3.
+ *
+ * @param input the byte array holding the octet string
+ * @param inOff the offset in the input byte array where the octet string
+ * starts
+ * @param inLen the length of the encoded integer
+ * @return an integer representing the octet string <tt>bytes</tt>, or
+ * <tt>0</tt> if the represented integer is negative or too large
+ * or the byte array is empty
+ */
+ public static int OS2IP(byte[] input, int inOff, int inLen)
+ {
+ if ((input.length == 0) || input.length < inOff + inLen - 1)
+ {
+ return 0;
+ }
+ int result = 0;
+ for (int j = 0; j < inLen; j++)
+ {
+ result |= (input[inOff + j] & 0xff) << (8 * (inLen - j - 1));
+ }
+ return result;
+ }
+
+ /**
+ * Convert a byte array of length 8 beginning at <tt>inOff</tt> into a
+ * long integer.
+ *
+ * @param input the byte array
+ * @param inOff the offset into the byte array
+ * @return the resulting long integer
+ */
+ public static long OS2LIP(byte[] input, int inOff)
+ {
+ long result = ((long)input[inOff++] & 0xff) << 56;
+ result |= ((long)input[inOff++] & 0xff) << 48;
+ result |= ((long)input[inOff++] & 0xff) << 40;
+ result |= ((long)input[inOff++] & 0xff) << 32;
+ result |= ((long)input[inOff++] & 0xff) << 24;
+ result |= (input[inOff++] & 0xff) << 16;
+ result |= (input[inOff++] & 0xff) << 8;
+ result |= input[inOff] & 0xff;
+ return result;
+ }
+
+ /**
+ * Convert an int array into a byte array.
+ *
+ * @param input the int array
+ * @return the converted array
+ */
+ public static byte[] toByteArray(final int[] input)
+ {
+ byte[] result = new byte[input.length << 2];
+ for (int i = 0; i < input.length; i++)
+ {
+ I2OSP(input[i], result, i << 2);
+ }
+ return result;
+ }
+
+ /**
+ * Convert an int array into a byte array of the specified length. No length
+ * checking is performed (i.e., if the last integer cannot be encoded into
+ * <tt>length % 4</tt> octets, it is truncated).
+ *
+ * @param input the int array
+ * @param length the length of the converted array
+ * @return the converted array
+ */
+ public static byte[] toByteArray(final int[] input, int length)
+ {
+ final int intLen = input.length;
+ byte[] result = new byte[length];
+ int index = 0;
+ for (int i = 0; i <= intLen - 2; i++, index += 4)
+ {
+ I2OSP(input[i], result, index);
+ }
+ I2OSP(input[intLen - 1], result, index, length - index);
+ return result;
+ }
+
+ /**
+ * Convert a byte array into an int array.
+ *
+ * @param input the byte array
+ * @return the converted array
+ */
+ public static int[] toIntArray(byte[] input)
+ {
+ final int intLen = (input.length + 3) / 4;
+ final int lastLen = input.length & 0x03;
+ int[] result = new int[intLen];
+
+ int index = 0;
+ for (int i = 0; i <= intLen - 2; i++, index += 4)
+ {
+ result[i] = OS2IP(input, index);
+ }
+ if (lastLen != 0)
+ {
+ result[intLen - 1] = OS2IP(input, index, lastLen);
+ }
+ else
+ {
+ result[intLen - 1] = OS2IP(input, index);
+ }
+
+ return result;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/BigIntUtils.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/BigIntUtils.java
new file mode 100644
index 00000000..b99ed41b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/BigIntUtils.java
@@ -0,0 +1,138 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+import java.math.BigInteger;
+
+/**
+ * FIXME: is this really necessary?!
+ */
+public final class BigIntUtils
+{
+
+ /**
+ * Default constructor (private).
+ */
+ private BigIntUtils()
+ {
+ // empty
+ }
+
+ /**
+ * Checks if two BigInteger arrays contain the same entries
+ *
+ * @param a first BigInteger array
+ * @param b second BigInteger array
+ * @return true or false
+ */
+ public static boolean equals(BigInteger[] a, BigInteger[] b)
+ {
+ int flag = 0;
+
+ if (a.length != b.length)
+ {
+ return false;
+ }
+ for (int i = 0; i < a.length; i++)
+ {
+ // avoid branches here!
+ // problem: compareTo on BigIntegers is not
+ // guaranteed constant-time!
+ flag |= a[i].compareTo(b[i]);
+ }
+ return flag == 0;
+ }
+
+ /**
+ * Fill the given BigInteger array with the given value.
+ *
+ * @param array the array
+ * @param value the value
+ */
+ public static void fill(BigInteger[] array, BigInteger value)
+ {
+ for (int i = array.length - 1; i >= 0; i--)
+ {
+ array[i] = value;
+ }
+ }
+
+ /**
+ * Generates a subarray of a given BigInteger array.
+ *
+ * @param input -
+ * the input BigInteger array
+ * @param start -
+ * the start index
+ * @param end -
+ * the end index
+ * @return a subarray of <tt>input</tt>, ranging from <tt>start</tt> to
+ * <tt>end</tt>
+ */
+ public static BigInteger[] subArray(BigInteger[] input, int start, int end)
+ {
+ BigInteger[] result = new BigInteger[end - start];
+ System.arraycopy(input, start, result, 0, end - start);
+ return result;
+ }
+
+ /**
+ * Converts a BigInteger array into an integer array
+ *
+ * @param input -
+ * the BigInteger array
+ * @return the integer array
+ */
+ public static int[] toIntArray(BigInteger[] input)
+ {
+ int[] result = new int[input.length];
+ for (int i = 0; i < input.length; i++)
+ {
+ result[i] = input[i].intValue();
+ }
+ return result;
+ }
+
+ /**
+ * Converts a BigInteger array into an integer array, reducing all
+ * BigIntegers mod q.
+ *
+ * @param q -
+ * the modulus
+ * @param input -
+ * the BigInteger array
+ * @return the integer array
+ */
+ public static int[] toIntArrayModQ(int q, BigInteger[] input)
+ {
+ BigInteger bq = BigInteger.valueOf(q);
+ int[] result = new int[input.length];
+ for (int i = 0; i < input.length; i++)
+ {
+ result[i] = input[i].mod(bq).intValue();
+ }
+ return result;
+ }
+
+ /**
+ * Return the value of <tt>big</tt> as a byte array. Although BigInteger
+ * has such a method, it uses an extra bit to indicate the sign of the
+ * number. For elliptic curve cryptography, the numbers usually are
+ * positive. Thus, this helper method returns a byte array of minimal
+ * length, ignoring the sign of the number.
+ *
+ * @param value the <tt>BigInteger</tt> value to be converted to a byte
+ * array
+ * @return the value <tt>big</tt> as byte array
+ */
+ public static byte[] toMinimalByteArray(BigInteger value)
+ {
+ byte[] valBytes = value.toByteArray();
+ if ((valBytes.length == 1) || (value.bitLength() & 0x07) != 0)
+ {
+ return valBytes;
+ }
+ byte[] result = new byte[value.bitLength() >> 3];
+ System.arraycopy(valBytes, 1, result, 0, result.length);
+ return result;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/ByteUtils.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/ByteUtils.java
new file mode 100644
index 00000000..5ad91f41
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/ByteUtils.java
@@ -0,0 +1,414 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+/**
+ * This class is a utility class for manipulating byte arrays.
+ */
+public final class ByteUtils
+{
+
+ private static final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5',
+ '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+ /**
+ * Default constructor (private)
+ */
+ private ByteUtils()
+ {
+ // empty
+ }
+
+ /**
+ * Compare two byte arrays (perform null checks beforehand).
+ *
+ * @param left the first byte array
+ * @param right the second byte array
+ * @return the result of the comparison
+ */
+ public static boolean equals(byte[] left, byte[] right)
+ {
+ if (left == null)
+ {
+ return right == null;
+ }
+ if (right == null)
+ {
+ return false;
+ }
+
+ if (left.length != right.length)
+ {
+ return false;
+ }
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ result &= left[i] == right[i];
+ }
+ return result;
+ }
+
+ /**
+ * Compare two two-dimensional byte arrays. No null checks are performed.
+ *
+ * @param left the first byte array
+ * @param right the second byte array
+ * @return the result of the comparison
+ */
+ public static boolean equals(byte[][] left, byte[][] right)
+ {
+ if (left.length != right.length)
+ {
+ return false;
+ }
+
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ result &= ByteUtils.equals(left[i], right[i]);
+ }
+
+ return result;
+ }
+
+ /**
+ * Compare two three-dimensional byte arrays. No null checks are performed.
+ *
+ * @param left the first byte array
+ * @param right the second byte array
+ * @return the result of the comparison
+ */
+ public static boolean equals(byte[][][] left, byte[][][] right)
+ {
+ if (left.length != right.length)
+ {
+ return false;
+ }
+
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ if (left[i].length != right[i].length)
+ {
+ return false;
+ }
+ for (int j = left[i].length - 1; j >= 0; j--)
+ {
+ result &= ByteUtils.equals(left[i][j], right[i][j]);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Computes a hashcode based on the contents of a one-dimensional byte array
+ * rather than its identity.
+ *
+ * @param array the array to compute the hashcode of
+ * @return the hashcode
+ */
+ public static int deepHashCode(byte[] array)
+ {
+ int result = 1;
+ for (int i = 0; i < array.length; i++)
+ {
+ result = 31 * result + array[i];
+ }
+ return result;
+ }
+
+ /**
+ * Computes a hashcode based on the contents of a two-dimensional byte array
+ * rather than its identity.
+ *
+ * @param array the array to compute the hashcode of
+ * @return the hashcode
+ */
+ public static int deepHashCode(byte[][] array)
+ {
+ int result = 1;
+ for (int i = 0; i < array.length; i++)
+ {
+ result = 31 * result + deepHashCode(array[i]);
+ }
+ return result;
+ }
+
+ /**
+ * Computes a hashcode based on the contents of a three-dimensional byte
+ * array rather than its identity.
+ *
+ * @param array the array to compute the hashcode of
+ * @return the hashcode
+ */
+ public static int deepHashCode(byte[][][] array)
+ {
+ int result = 1;
+ for (int i = 0; i < array.length; i++)
+ {
+ result = 31 * result + deepHashCode(array[i]);
+ }
+ return result;
+ }
+
+
+ /**
+ * Return a clone of the given byte array (performs null check beforehand).
+ *
+ * @param array the array to clone
+ * @return the clone of the given array, or <tt>null</tt> if the array is
+ * <tt>null</tt>
+ */
+ public static byte[] clone(byte[] array)
+ {
+ if (array == null)
+ {
+ return null;
+ }
+ byte[] result = new byte[array.length];
+ System.arraycopy(array, 0, result, 0, array.length);
+ return result;
+ }
+
+ /**
+ * Convert a string containing hexadecimal characters to a byte-array.
+ *
+ * @param s a hex string
+ * @return a byte array with the corresponding value
+ */
+ public static byte[] fromHexString(String s)
+ {
+ char[] rawChars = s.toUpperCase().toCharArray();
+
+ int hexChars = 0;
+ for (int i = 0; i < rawChars.length; i++)
+ {
+ if ((rawChars[i] >= '0' && rawChars[i] <= '9')
+ || (rawChars[i] >= 'A' && rawChars[i] <= 'F'))
+ {
+ hexChars++;
+ }
+ }
+
+ byte[] byteString = new byte[(hexChars + 1) >> 1];
+
+ int pos = hexChars & 1;
+
+ for (int i = 0; i < rawChars.length; i++)
+ {
+ if (rawChars[i] >= '0' && rawChars[i] <= '9')
+ {
+ byteString[pos >> 1] <<= 4;
+ byteString[pos >> 1] |= rawChars[i] - '0';
+ }
+ else if (rawChars[i] >= 'A' && rawChars[i] <= 'F')
+ {
+ byteString[pos >> 1] <<= 4;
+ byteString[pos >> 1] |= rawChars[i] - 'A' + 10;
+ }
+ else
+ {
+ continue;
+ }
+ pos++;
+ }
+
+ return byteString;
+ }
+
+ /**
+ * Convert a byte array to the corresponding hexstring.
+ *
+ * @param input the byte array to be converted
+ * @return the corresponding hexstring
+ */
+ public static String toHexString(byte[] input)
+ {
+ String result = "";
+ for (int i = 0; i < input.length; i++)
+ {
+ result += HEX_CHARS[(input[i] >>> 4) & 0x0f];
+ result += HEX_CHARS[(input[i]) & 0x0f];
+ }
+ return result;
+ }
+
+ /**
+ * Convert a byte array to the corresponding hex string.
+ *
+ * @param input the byte array to be converted
+ * @param prefix the prefix to put at the beginning of the hex string
+ * @param seperator a separator string
+ * @return the corresponding hex string
+ */
+ public static String toHexString(byte[] input, String prefix,
+ String seperator)
+ {
+ String result = new String(prefix);
+ for (int i = 0; i < input.length; i++)
+ {
+ result += HEX_CHARS[(input[i] >>> 4) & 0x0f];
+ result += HEX_CHARS[(input[i]) & 0x0f];
+ if (i < input.length - 1)
+ {
+ result += seperator;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Convert a byte array to the corresponding bit string.
+ *
+ * @param input the byte array to be converted
+ * @return the corresponding bit string
+ */
+ public static String toBinaryString(byte[] input)
+ {
+ String result = "";
+ int i;
+ for (i = 0; i < input.length; i++)
+ {
+ int e = input[i];
+ for (int ii = 0; ii < 8; ii++)
+ {
+ int b = (e >>> ii) & 1;
+ result += b;
+ }
+ if (i != input.length - 1)
+ {
+ result += " ";
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Compute the bitwise XOR of two arrays of bytes. The arrays have to be of
+ * same length. No length checking is performed.
+ *
+ * @param x1 the first array
+ * @param x2 the second array
+ * @return x1 XOR x2
+ */
+ public static byte[] xor(byte[] x1, byte[] x2)
+ {
+ byte[] out = new byte[x1.length];
+
+ for (int i = x1.length - 1; i >= 0; i--)
+ {
+ out[i] = (byte)(x1[i] ^ x2[i]);
+ }
+ return out;
+ }
+
+ /**
+ * Concatenate two byte arrays. No null checks are performed.
+ *
+ * @param x1 the first array
+ * @param x2 the second array
+ * @return (x2||x1) (little-endian order, i.e. x1 is at lower memory
+ * addresses)
+ */
+ public static byte[] concatenate(byte[] x1, byte[] x2)
+ {
+ byte[] result = new byte[x1.length + x2.length];
+
+ System.arraycopy(x1, 0, result, 0, x1.length);
+ System.arraycopy(x2, 0, result, x1.length, x2.length);
+
+ return result;
+ }
+
+ /**
+ * Convert a 2-dimensional byte array into a 1-dimensional byte array by
+ * concatenating all entries.
+ *
+ * @param array a 2-dimensional byte array
+ * @return the concatenated input array
+ */
+ public static byte[] concatenate(byte[][] array)
+ {
+ int rowLength = array[0].length;
+ byte[] result = new byte[array.length * rowLength];
+ int index = 0;
+ for (int i = 0; i < array.length; i++)
+ {
+ System.arraycopy(array[i], 0, result, index, rowLength);
+ index += rowLength;
+ }
+ return result;
+ }
+
+ /**
+ * Split a byte array <tt>input</tt> into two arrays at <tt>index</tt>,
+ * i.e. the first array will have the lower <tt>index</tt> bytes, the
+ * second one the higher <tt>input.length - index</tt> bytes.
+ *
+ * @param input the byte array to be split
+ * @param index the index where the byte array is split
+ * @return the splitted input array as an array of two byte arrays
+ * @throws ArrayIndexOutOfBoundsException if <tt>index</tt> is out of bounds
+ */
+ public static byte[][] split(byte[] input, int index)
+ throws ArrayIndexOutOfBoundsException
+ {
+ if (index > input.length)
+ {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ byte[][] result = new byte[2][];
+ result[0] = new byte[index];
+ result[1] = new byte[input.length - index];
+ System.arraycopy(input, 0, result[0], 0, index);
+ System.arraycopy(input, index, result[1], 0, input.length - index);
+ return result;
+ }
+
+ /**
+ * Generate a subarray of a given byte array.
+ *
+ * @param input the input byte array
+ * @param start the start index
+ * @param end the end index
+ * @return a subarray of <tt>input</tt>, ranging from <tt>start</tt>
+ * (inclusively) to <tt>end</tt> (exclusively)
+ */
+ public static byte[] subArray(byte[] input, int start, int end)
+ {
+ byte[] result = new byte[end - start];
+ System.arraycopy(input, start, result, 0, end - start);
+ return result;
+ }
+
+ /**
+ * Generate a subarray of a given byte array.
+ *
+ * @param input the input byte array
+ * @param start the start index
+ * @return a subarray of <tt>input</tt>, ranging from <tt>start</tt> to
+ * the end of the array
+ */
+ public static byte[] subArray(byte[] input, int start)
+ {
+ return subArray(input, start, input.length);
+ }
+
+ /**
+ * Rewrite a byte array as a char array
+ *
+ * @param input -
+ * the byte array
+ * @return char array
+ */
+ public static char[] toCharArray(byte[] input)
+ {
+ char[] result = new char[input.length];
+ for (int i = 0; i < input.length; i++)
+ {
+ result[i] = (char)input[i];
+ }
+ return result;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/CharUtils.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/CharUtils.java
new file mode 100644
index 00000000..1800685d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/CharUtils.java
@@ -0,0 +1,98 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+public final class CharUtils
+{
+
+ /**
+ * Default constructor (private)
+ */
+ private CharUtils()
+ {
+ // empty
+ }
+
+ /**
+ * Return a clone of the given char array. No null checks are performed.
+ *
+ * @param array the array to clone
+ * @return the clone of the given array
+ */
+ public static char[] clone(char[] array)
+ {
+ char[] result = new char[array.length];
+ System.arraycopy(array, 0, result, 0, array.length);
+ return result;
+ }
+
+ /**
+ * Convert the given char array into a byte array.
+ *
+ * @param chars the char array
+ * @return the converted array
+ */
+ public static byte[] toByteArray(char[] chars)
+ {
+ byte[] result = new byte[chars.length];
+ for (int i = chars.length - 1; i >= 0; i--)
+ {
+ result[i] = (byte)chars[i];
+ }
+ return result;
+ }
+
+ /**
+ * Convert the given char array into a
+ * byte array for use with PBE encryption.
+ *
+ * @param chars the char array
+ * @return the converted array
+ */
+ public static byte[] toByteArrayForPBE(char[] chars)
+ {
+
+ byte[] out = new byte[chars.length];
+
+ for (int i = 0; i < chars.length; i++)
+ {
+ out[i] = (byte)chars[i];
+ }
+
+ int length = out.length * 2;
+ byte[] ret = new byte[length + 2];
+
+ int j = 0;
+ for (int i = 0; i < out.length; i++)
+ {
+ j = i * 2;
+ ret[j] = 0;
+ ret[j + 1] = out[i];
+ }
+
+ ret[length] = 0;
+ ret[length + 1] = 0;
+
+ return ret;
+ }
+
+ /**
+ * Compare two char arrays. No null checks are performed.
+ *
+ * @param left the char byte array
+ * @param right the second char array
+ * @return the result of the comparison
+ */
+ public static boolean equals(char[] left, char[] right)
+ {
+ if (left.length != right.length)
+ {
+ return false;
+ }
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ result &= left[i] == right[i];
+ }
+ return result;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Matrix.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Matrix.java
new file mode 100644
index 00000000..a61f9507
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Matrix.java
@@ -0,0 +1,1323 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+import java.security.SecureRandom;
+
+/**
+ * This class describes some operations with matrices over finite field GF(2)
+ * and is used in ecc and MQ-PKC (also has some specific methods and
+ * implementation)
+ */
+public class GF2Matrix
+ extends Matrix
+{
+
+ /**
+ * For the matrix representation the array of type int[][] is used, thus one
+ * element of the array keeps 32 elements of the matrix (from one row and 32
+ * columns)
+ */
+ private int[][] matrix;
+
+ /**
+ * the length of each array representing a row of this matrix, computed as
+ * <tt>(numColumns + 31) / 32</tt>
+ */
+ private int length;
+
+ /**
+ * Create the matrix from encoded form.
+ *
+ * @param enc the encoded matrix
+ */
+ public GF2Matrix(byte[] enc)
+ {
+ if (enc.length < 9)
+ {
+ throw new ArithmeticException(
+ "given array is not an encoded matrix over GF(2)");
+ }
+
+ numRows = LittleEndianConversions.OS2IP(enc, 0);
+ numColumns = LittleEndianConversions.OS2IP(enc, 4);
+
+ int n = ((numColumns + 7) >>> 3) * numRows;
+
+ if ((numRows <= 0) || (n != (enc.length - 8)))
+ {
+ throw new ArithmeticException(
+ "given array is not an encoded matrix over GF(2)");
+ }
+
+ length = (numColumns + 31) >>> 5;
+ matrix = new int[numRows][length];
+
+ // number of "full" integer
+ int q = numColumns >> 5;
+ // number of bits in non-full integer
+ int r = numColumns & 0x1f;
+
+ int count = 8;
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < q; j++, count += 4)
+ {
+ matrix[i][j] = LittleEndianConversions.OS2IP(enc, count);
+ }
+ for (int j = 0; j < r; j += 8)
+ {
+ matrix[i][q] ^= (enc[count++] & 0xff) << j;
+ }
+ }
+ }
+
+ /**
+ * Create the matrix with the contents of the given array. The matrix is not
+ * copied. Unused coefficients are masked out.
+ *
+ * @param numColumns the number of columns
+ * @param matrix the element array
+ */
+ public GF2Matrix(int numColumns, int[][] matrix)
+ {
+ if (matrix[0].length != (numColumns + 31) >> 5)
+ {
+ throw new ArithmeticException(
+ "Int array does not match given number of columns.");
+ }
+ this.numColumns = numColumns;
+ numRows = matrix.length;
+ length = matrix[0].length;
+ int rest = numColumns & 0x1f;
+ int bitMask;
+ if (rest == 0)
+ {
+ bitMask = 0xffffffff;
+ }
+ else
+ {
+ bitMask = (1 << rest) - 1;
+ }
+ for (int i = 0; i < numRows; i++)
+ {
+ matrix[i][length - 1] &= bitMask;
+ }
+ this.matrix = matrix;
+ }
+
+ /**
+ * Create an nxn matrix of the given type.
+ *
+ * @param n the number of rows (and columns)
+ * @param typeOfMatrix the martix type (see {@link Matrix} for predefined
+ * constants)
+ */
+ public GF2Matrix(int n, char typeOfMatrix)
+ {
+ this(n, typeOfMatrix, new java.security.SecureRandom());
+ }
+
+ /**
+ * Create an nxn matrix of the given type.
+ *
+ * @param n the matrix size
+ * @param typeOfMatrix the matrix type
+ * @param sr the source of randomness
+ */
+ public GF2Matrix(int n, char typeOfMatrix, SecureRandom sr)
+ {
+ if (n <= 0)
+ {
+ throw new ArithmeticException("Size of matrix is non-positive.");
+ }
+
+ switch (typeOfMatrix)
+ {
+
+ case Matrix.MATRIX_TYPE_ZERO:
+ assignZeroMatrix(n, n);
+ break;
+
+ case Matrix.MATRIX_TYPE_UNIT:
+ assignUnitMatrix(n);
+ break;
+
+ case Matrix.MATRIX_TYPE_RANDOM_LT:
+ assignRandomLowerTriangularMatrix(n, sr);
+ break;
+
+ case Matrix.MATRIX_TYPE_RANDOM_UT:
+ assignRandomUpperTriangularMatrix(n, sr);
+ break;
+
+ case Matrix.MATRIX_TYPE_RANDOM_REGULAR:
+ assignRandomRegularMatrix(n, sr);
+ break;
+
+ default:
+ throw new ArithmeticException("Unknown matrix type.");
+ }
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param a another {@link GF2Matrix}
+ */
+ public GF2Matrix(GF2Matrix a)
+ {
+ numColumns = a.getNumColumns();
+ numRows = a.getNumRows();
+ length = a.length;
+ matrix = new int[a.matrix.length][];
+ for (int i = 0; i < matrix.length; i++)
+ {
+ matrix[i] = IntUtils.clone(a.matrix[i]);
+ }
+
+ }
+
+ /**
+ * create the mxn zero matrix
+ */
+ private GF2Matrix(int m, int n)
+ {
+ if ((n <= 0) || (m <= 0))
+ {
+ throw new ArithmeticException("size of matrix is non-positive");
+ }
+
+ assignZeroMatrix(m, n);
+ }
+
+ /**
+ * Create the mxn zero matrix.
+ *
+ * @param m number of rows
+ * @param n number of columns
+ */
+ private void assignZeroMatrix(int m, int n)
+ {
+ numRows = m;
+ numColumns = n;
+ length = (n + 31) >>> 5;
+ matrix = new int[numRows][length];
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < length; j++)
+ {
+ matrix[i][j] = 0;
+ }
+ }
+ }
+
+ /**
+ * Create the mxn unit matrix.
+ *
+ * @param n number of rows (and columns)
+ */
+ private void assignUnitMatrix(int n)
+ {
+ numRows = n;
+ numColumns = n;
+ length = (n + 31) >>> 5;
+ matrix = new int[numRows][length];
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < length; j++)
+ {
+ matrix[i][j] = 0;
+ }
+ }
+ for (int i = 0; i < numRows; i++)
+ {
+ int rest = i & 0x1f;
+ matrix[i][i >>> 5] = 1 << rest;
+ }
+ }
+
+ /**
+ * Create a nxn random lower triangular matrix.
+ *
+ * @param n number of rows (and columns)
+ * @param sr source of randomness
+ */
+ private void assignRandomLowerTriangularMatrix(int n, SecureRandom sr)
+ {
+ numRows = n;
+ numColumns = n;
+ length = (n + 31) >>> 5;
+ matrix = new int[numRows][length];
+ for (int i = 0; i < numRows; i++)
+ {
+ int q = i >>> 5;
+ int r = i & 0x1f;
+ int s = 31 - r;
+ r = 1 << r;
+ for (int j = 0; j < q; j++)
+ {
+ matrix[i][j] = sr.nextInt();
+ }
+ matrix[i][q] = (sr.nextInt() >>> s) | r;
+ for (int j = q + 1; j < length; j++)
+ {
+ matrix[i][j] = 0;
+ }
+
+ }
+
+ }
+
+ /**
+ * Create a nxn random upper triangular matrix.
+ *
+ * @param n number of rows (and columns)
+ * @param sr source of randomness
+ */
+ private void assignRandomUpperTriangularMatrix(int n, SecureRandom sr)
+ {
+ numRows = n;
+ numColumns = n;
+ length = (n + 31) >>> 5;
+ matrix = new int[numRows][length];
+ int rest = n & 0x1f;
+ int help;
+ if (rest == 0)
+ {
+ help = 0xffffffff;
+ }
+ else
+ {
+ help = (1 << rest) - 1;
+ }
+ for (int i = 0; i < numRows; i++)
+ {
+ int q = i >>> 5;
+ int r = i & 0x1f;
+ int s = r;
+ r = 1 << r;
+ for (int j = 0; j < q; j++)
+ {
+ matrix[i][j] = 0;
+ }
+ matrix[i][q] = (sr.nextInt() << s) | r;
+ for (int j = q + 1; j < length; j++)
+ {
+ matrix[i][j] = sr.nextInt();
+ }
+ matrix[i][length - 1] &= help;
+ }
+
+ }
+
+ /**
+ * Create an nxn random regular matrix.
+ *
+ * @param n number of rows (and columns)
+ * @param sr source of randomness
+ */
+ private void assignRandomRegularMatrix(int n, SecureRandom sr)
+ {
+ numRows = n;
+ numColumns = n;
+ length = (n + 31) >>> 5;
+ matrix = new int[numRows][length];
+ GF2Matrix lm = new GF2Matrix(n, Matrix.MATRIX_TYPE_RANDOM_LT, sr);
+ GF2Matrix um = new GF2Matrix(n, Matrix.MATRIX_TYPE_RANDOM_UT, sr);
+ GF2Matrix rm = (GF2Matrix)lm.rightMultiply(um);
+ Permutation perm = new Permutation(n, sr);
+ int[] p = perm.getVector();
+ for (int i = 0; i < n; i++)
+ {
+ System.arraycopy(rm.matrix[i], 0, matrix[p[i]], 0, length);
+ }
+ }
+
+ /**
+ * Create a nxn random regular matrix and its inverse.
+ *
+ * @param n number of rows (and columns)
+ * @param sr source of randomness
+ * @return the created random regular matrix and its inverse
+ */
+ public static GF2Matrix[] createRandomRegularMatrixAndItsInverse(int n,
+ SecureRandom sr)
+ {
+
+ GF2Matrix[] result = new GF2Matrix[2];
+
+ // ------------------------------------
+ // First part: create regular matrix
+ // ------------------------------------
+
+ // ------
+ int length = (n + 31) >> 5;
+ GF2Matrix lm = new GF2Matrix(n, Matrix.MATRIX_TYPE_RANDOM_LT, sr);
+ GF2Matrix um = new GF2Matrix(n, Matrix.MATRIX_TYPE_RANDOM_UT, sr);
+ GF2Matrix rm = (GF2Matrix)lm.rightMultiply(um);
+ Permutation p = new Permutation(n, sr);
+ int[] pVec = p.getVector();
+
+ int[][] matrix = new int[n][length];
+ for (int i = 0; i < n; i++)
+ {
+ System.arraycopy(rm.matrix[pVec[i]], 0, matrix[i], 0, length);
+ }
+
+ result[0] = new GF2Matrix(n, matrix);
+
+ // ------------------------------------
+ // Second part: create inverse matrix
+ // ------------------------------------
+
+ // inverse to lm
+ GF2Matrix invLm = new GF2Matrix(n, Matrix.MATRIX_TYPE_UNIT);
+ for (int i = 0; i < n; i++)
+ {
+ int rest = i & 0x1f;
+ int q = i >>> 5;
+ int r = 1 << rest;
+ for (int j = i + 1; j < n; j++)
+ {
+ int b = (lm.matrix[j][q]) & r;
+ if (b != 0)
+ {
+ for (int k = 0; k <= q; k++)
+ {
+ invLm.matrix[j][k] ^= invLm.matrix[i][k];
+ }
+ }
+ }
+ }
+ // inverse to um
+ GF2Matrix invUm = new GF2Matrix(n, Matrix.MATRIX_TYPE_UNIT);
+ for (int i = n - 1; i >= 0; i--)
+ {
+ int rest = i & 0x1f;
+ int q = i >>> 5;
+ int r = 1 << rest;
+ for (int j = i - 1; j >= 0; j--)
+ {
+ int b = (um.matrix[j][q]) & r;
+ if (b != 0)
+ {
+ for (int k = q; k < length; k++)
+ {
+ invUm.matrix[j][k] ^= invUm.matrix[i][k];
+ }
+ }
+ }
+ }
+
+ // inverse matrix
+ result[1] = (GF2Matrix)invUm.rightMultiply(invLm.rightMultiply(p));
+
+ return result;
+ }
+
+ /**
+ * @return the array keeping the matrix elements
+ */
+ public int[][] getIntArray()
+ {
+ return matrix;
+ }
+
+ /**
+ * @return the length of each array representing a row of this matrix
+ */
+ public int getLength()
+ {
+ return length;
+ }
+
+ /**
+ * Return the row of this matrix with the given index.
+ *
+ * @param index the index
+ * @return the row of this matrix with the given index
+ */
+ public int[] getRow(int index)
+ {
+ return matrix[index];
+ }
+
+ /**
+ * Returns encoded matrix, i.e., this matrix in byte array form
+ *
+ * @return the encoded matrix
+ */
+ public byte[] getEncoded()
+ {
+ int n = (numColumns + 7) >>> 3;
+ n *= numRows;
+ n += 8;
+ byte[] enc = new byte[n];
+
+ LittleEndianConversions.I2OSP(numRows, enc, 0);
+ LittleEndianConversions.I2OSP(numColumns, enc, 4);
+
+ // number of "full" integer
+ int q = numColumns >>> 5;
+ // number of bits in non-full integer
+ int r = numColumns & 0x1f;
+
+ int count = 8;
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < q; j++, count += 4)
+ {
+ LittleEndianConversions.I2OSP(matrix[i][j], enc, count);
+ }
+ for (int j = 0; j < r; j += 8)
+ {
+ enc[count++] = (byte)((matrix[i][q] >>> j) & 0xff);
+ }
+
+ }
+ return enc;
+ }
+
+
+ /**
+ * Returns the percentage of the number of "ones" in this matrix.
+ *
+ * @return the Hamming weight of this matrix (as a ratio).
+ */
+ public double getHammingWeight()
+ {
+ double counter = 0.0;
+ double elementCounter = 0.0;
+ int rest = numColumns & 0x1f;
+ int d;
+ if (rest == 0)
+ {
+ d = length;
+ }
+ else
+ {
+ d = length - 1;
+ }
+
+ for (int i = 0; i < numRows; i++)
+ {
+
+ for (int j = 0; j < d; j++)
+ {
+ int a = matrix[i][j];
+ for (int k = 0; k < 32; k++)
+ {
+ int b = (a >>> k) & 1;
+ counter = counter + b;
+ elementCounter = elementCounter + 1;
+ }
+ }
+ int a = matrix[i][length - 1];
+ for (int k = 0; k < rest; k++)
+ {
+ int b = (a >>> k) & 1;
+ counter = counter + b;
+ elementCounter = elementCounter + 1;
+ }
+ }
+
+ return counter / elementCounter;
+ }
+
+ /**
+ * Check if this is the zero matrix (i.e., all entries are zero).
+ *
+ * @return <tt>true</tt> if this is the zero matrix
+ */
+ public boolean isZero()
+ {
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < length; j++)
+ {
+ if (matrix[i][j] != 0)
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Get the quadratic submatrix of this matrix consisting of the leftmost
+ * <tt>numRows</tt> columns.
+ *
+ * @return the <tt>(numRows x numRows)</tt> submatrix
+ */
+ public GF2Matrix getLeftSubMatrix()
+ {
+ if (numColumns <= numRows)
+ {
+ throw new ArithmeticException("empty submatrix");
+ }
+ int length = (numRows + 31) >> 5;
+ int[][] result = new int[numRows][length];
+ int bitMask = (1 << (numRows & 0x1f)) - 1;
+ if (bitMask == 0)
+ {
+ bitMask = -1;
+ }
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ System.arraycopy(matrix[i], 0, result[i], 0, length);
+ result[i][length - 1] &= bitMask;
+ }
+ return new GF2Matrix(numRows, result);
+ }
+
+ /**
+ * Compute the full form matrix <tt>(this | Id)</tt> from this matrix in
+ * left compact form, where <tt>Id</tt> is the <tt>k x k</tt> identity
+ * matrix and <tt>k</tt> is the number of rows of this matrix.
+ *
+ * @return <tt>(this | Id)</tt>
+ */
+ public GF2Matrix extendLeftCompactForm()
+ {
+ int newNumColumns = numColumns + numRows;
+ GF2Matrix result = new GF2Matrix(numRows, newNumColumns);
+
+ int ind = numRows - 1 + numColumns;
+ for (int i = numRows - 1; i >= 0; i--, ind--)
+ {
+ // copy this matrix to first columns
+ System.arraycopy(matrix[i], 0, result.matrix[i], 0, length);
+ // store the identity in last columns
+ result.matrix[i][ind >> 5] |= 1 << (ind & 0x1f);
+ }
+
+ return result;
+ }
+
+ /**
+ * Get the submatrix of this matrix consisting of the rightmost
+ * <tt>numColumns-numRows</tt> columns.
+ *
+ * @return the <tt>(numRows x (numColumns-numRows))</tt> submatrix
+ */
+ public GF2Matrix getRightSubMatrix()
+ {
+ if (numColumns <= numRows)
+ {
+ throw new ArithmeticException("empty submatrix");
+ }
+
+ int q = numRows >> 5;
+ int r = numRows & 0x1f;
+
+ GF2Matrix result = new GF2Matrix(numRows, numColumns - numRows);
+
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ // if words have to be shifted
+ if (r != 0)
+ {
+ int ind = q;
+ // process all but last word
+ for (int j = 0; j < result.length - 1; j++)
+ {
+ // shift to correct position
+ result.matrix[i][j] = (matrix[i][ind++] >>> r)
+ | (matrix[i][ind] << (32 - r));
+ }
+ // process last word
+ result.matrix[i][result.length - 1] = matrix[i][ind++] >>> r;
+ if (ind < length)
+ {
+ result.matrix[i][result.length - 1] |= matrix[i][ind] << (32 - r);
+ }
+ }
+ else
+ {
+ // no shifting necessary
+ System.arraycopy(matrix[i], q, result.matrix[i], 0,
+ result.length);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Compute the full form matrix <tt>(Id | this)</tt> from this matrix in
+ * right compact form, where <tt>Id</tt> is the <tt>k x k</tt> identity
+ * matrix and <tt>k</tt> is the number of rows of this matrix.
+ *
+ * @return <tt>(Id | this)</tt>
+ */
+ public GF2Matrix extendRightCompactForm()
+ {
+ GF2Matrix result = new GF2Matrix(numRows, numRows + numColumns);
+
+ int q = numRows >> 5;
+ int r = numRows & 0x1f;
+
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ // store the identity in first columns
+ result.matrix[i][i >> 5] |= 1 << (i & 0x1f);
+
+ // copy this matrix to last columns
+
+ // if words have to be shifted
+ if (r != 0)
+ {
+ int ind = q;
+ // process all but last word
+ for (int j = 0; j < length - 1; j++)
+ {
+ // obtain matrix word
+ int mw = matrix[i][j];
+ // shift to correct position
+ result.matrix[i][ind++] |= mw << r;
+ result.matrix[i][ind] |= mw >>> (32 - r);
+ }
+ // process last word
+ int mw = matrix[i][length - 1];
+ result.matrix[i][ind++] |= mw << r;
+ if (ind < result.length)
+ {
+ result.matrix[i][ind] |= mw >>> (32 - r);
+ }
+ }
+ else
+ {
+ // no shifting necessary
+ System.arraycopy(matrix[i], 0, result.matrix[i], q, length);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute the transpose of this matrix.
+ *
+ * @return <tt>(this)<sup>T</sup></tt>
+ */
+ public Matrix computeTranspose()
+ {
+ int[][] result = new int[numColumns][(numRows + 31) >>> 5];
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < numColumns; j++)
+ {
+ int qs = j >>> 5;
+ int rs = j & 0x1f;
+ int b = (matrix[i][qs] >>> rs) & 1;
+ int qt = i >>> 5;
+ int rt = i & 0x1f;
+ if (b == 1)
+ {
+ result[j][qt] |= 1 << rt;
+ }
+ }
+ }
+
+ return new GF2Matrix(numRows, result);
+ }
+
+ /**
+ * Compute the inverse of this matrix.
+ *
+ * @return the inverse of this matrix (newly created).
+ * @throws ArithmeticException if this matrix is not invertible.
+ */
+ public Matrix computeInverse()
+ {
+ if (numRows != numColumns)
+ {
+ throw new ArithmeticException("Matrix is not invertible.");
+ }
+
+ // clone this matrix
+ int[][] tmpMatrix = new int[numRows][length];
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ tmpMatrix[i] = IntUtils.clone(matrix[i]);
+ }
+
+ // initialize inverse matrix as unit matrix
+ int[][] invMatrix = new int[numRows][length];
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ int q = i >> 5;
+ int r = i & 0x1f;
+ invMatrix[i][q] = 1 << r;
+ }
+
+ // simultaneously compute Gaussian reduction of tmpMatrix and unit
+ // matrix
+ for (int i = 0; i < numRows; i++)
+ {
+ // i = q * 32 + (i mod 32)
+ int q = i >> 5;
+ int bitMask = 1 << (i & 0x1f);
+ // if diagonal element is zero
+ if ((tmpMatrix[i][q] & bitMask) == 0)
+ {
+ boolean foundNonZero = false;
+ // find a non-zero element in the same column
+ for (int j = i + 1; j < numRows; j++)
+ {
+ if ((tmpMatrix[j][q] & bitMask) != 0)
+ {
+ // found it, swap rows ...
+ foundNonZero = true;
+ swapRows(tmpMatrix, i, j);
+ swapRows(invMatrix, i, j);
+ // ... and quit searching
+ j = numRows;
+ continue;
+ }
+ }
+ // if no non-zero element was found ...
+ if (!foundNonZero)
+ {
+ // ... the matrix is not invertible
+ throw new ArithmeticException("Matrix is not invertible.");
+ }
+ }
+
+ // normalize all but i-th row
+ for (int j = numRows - 1; j >= 0; j--)
+ {
+ if ((j != i) && ((tmpMatrix[j][q] & bitMask) != 0))
+ {
+ addToRow(tmpMatrix[i], tmpMatrix[j], q);
+ addToRow(invMatrix[i], invMatrix[j], 0);
+ }
+ }
+ }
+
+ return new GF2Matrix(numColumns, invMatrix);
+ }
+
+ /**
+ * Compute the product of a permutation matrix (which is generated from an
+ * n-permutation) and this matrix.
+ *
+ * @param p the permutation
+ * @return {@link GF2Matrix} <tt>P*this</tt>
+ */
+ public Matrix leftMultiply(Permutation p)
+ {
+ int[] pVec = p.getVector();
+ if (pVec.length != numRows)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ int[][] result = new int[numRows][];
+
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ result[i] = IntUtils.clone(matrix[pVec[i]]);
+ }
+
+ return new GF2Matrix(numRows, result);
+ }
+
+ /**
+ * compute product a row vector and this matrix
+ *
+ * @param vec a vector over GF(2)
+ * @return Vector product a*matrix
+ */
+ public Vector leftMultiply(Vector vec)
+ {
+
+ if (!(vec instanceof GF2Vector))
+ {
+ throw new ArithmeticException("vector is not defined over GF(2)");
+ }
+
+ if (vec.length != numRows)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ int[] v = ((GF2Vector)vec).getVecArray();
+ int[] res = new int[length];
+
+ int q = numRows >> 5;
+ int r = 1 << (numRows & 0x1f);
+
+ // compute scalar products with full words of vector
+ int row = 0;
+ for (int i = 0; i < q; i++)
+ {
+ int bitMask = 1;
+ do
+ {
+ int b = v[i] & bitMask;
+ if (b != 0)
+ {
+ for (int j = 0; j < length; j++)
+ {
+ res[j] ^= matrix[row][j];
+ }
+ }
+ row++;
+ bitMask <<= 1;
+ }
+ while (bitMask != 0);
+ }
+
+ // compute scalar products with last word of vector
+ int bitMask = 1;
+ while (bitMask != r)
+ {
+ int b = v[q] & bitMask;
+ if (b != 0)
+ {
+ for (int j = 0; j < length; j++)
+ {
+ res[j] ^= matrix[row][j];
+ }
+ }
+ row++;
+ bitMask <<= 1;
+ }
+
+ return new GF2Vector(res, numColumns);
+ }
+
+ /**
+ * Compute the product of the matrix <tt>(this | Id)</tt> and a column
+ * vector, where <tt>Id</tt> is a <tt>(numRows x numRows)</tt> unit
+ * matrix.
+ *
+ * @param vec the vector over GF(2)
+ * @return <tt>(this | Id)*vector</tt>
+ */
+ public Vector leftMultiplyLeftCompactForm(Vector vec)
+ {
+ if (!(vec instanceof GF2Vector))
+ {
+ throw new ArithmeticException("vector is not defined over GF(2)");
+ }
+
+ if (vec.length != numRows)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ int[] v = ((GF2Vector)vec).getVecArray();
+ int[] res = new int[(numRows + numColumns + 31) >>> 5];
+
+ // process full words of vector
+ int words = numRows >>> 5;
+ int row = 0;
+ for (int i = 0; i < words; i++)
+ {
+ int bitMask = 1;
+ do
+ {
+ int b = v[i] & bitMask;
+ if (b != 0)
+ {
+ // compute scalar product part
+ for (int j = 0; j < length; j++)
+ {
+ res[j] ^= matrix[row][j];
+ }
+ // set last bit
+ int q = (numColumns + row) >>> 5;
+ int r = (numColumns + row) & 0x1f;
+ res[q] |= 1 << r;
+ }
+ row++;
+ bitMask <<= 1;
+ }
+ while (bitMask != 0);
+ }
+
+ // process last word of vector
+ int rem = 1 << (numRows & 0x1f);
+ int bitMask = 1;
+ while (bitMask != rem)
+ {
+ int b = v[words] & bitMask;
+ if (b != 0)
+ {
+ // compute scalar product part
+ for (int j = 0; j < length; j++)
+ {
+ res[j] ^= matrix[row][j];
+ }
+ // set last bit
+ int q = (numColumns + row) >>> 5;
+ int r = (numColumns + row) & 0x1f;
+ res[q] |= 1 << r;
+ }
+ row++;
+ bitMask <<= 1;
+ }
+
+ return new GF2Vector(res, numRows + numColumns);
+ }
+
+ /**
+ * Compute the product of this matrix and a matrix A over GF(2).
+ *
+ * @param mat a matrix A over GF(2)
+ * @return matrix product <tt>this*matrixA</tt>
+ */
+ public Matrix rightMultiply(Matrix mat)
+ {
+ if (!(mat instanceof GF2Matrix))
+ {
+ throw new ArithmeticException("matrix is not defined over GF(2)");
+ }
+
+ if (mat.numRows != numColumns)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ GF2Matrix a = (GF2Matrix)mat;
+ GF2Matrix result = new GF2Matrix(numRows, mat.numColumns);
+
+ int d;
+ int rest = numColumns & 0x1f;
+ if (rest == 0)
+ {
+ d = length;
+ }
+ else
+ {
+ d = length - 1;
+ }
+ for (int i = 0; i < numRows; i++)
+ {
+ int count = 0;
+ for (int j = 0; j < d; j++)
+ {
+ int e = matrix[i][j];
+ for (int h = 0; h < 32; h++)
+ {
+ int b = e & (1 << h);
+ if (b != 0)
+ {
+ for (int g = 0; g < a.length; g++)
+ {
+ result.matrix[i][g] ^= a.matrix[count][g];
+ }
+ }
+ count++;
+ }
+ }
+ int e = matrix[i][length - 1];
+ for (int h = 0; h < rest; h++)
+ {
+ int b = e & (1 << h);
+ if (b != 0)
+ {
+ for (int g = 0; g < a.length; g++)
+ {
+ result.matrix[i][g] ^= a.matrix[count][g];
+ }
+ }
+ count++;
+ }
+
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute the product of this matrix and a permutation matrix which is
+ * generated from an n-permutation.
+ *
+ * @param p the permutation
+ * @return {@link GF2Matrix} <tt>this*P</tt>
+ */
+ public Matrix rightMultiply(Permutation p)
+ {
+
+ int[] pVec = p.getVector();
+ if (pVec.length != numColumns)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ GF2Matrix result = new GF2Matrix(numRows, numColumns);
+
+ for (int i = numColumns - 1; i >= 0; i--)
+ {
+ int q = i >>> 5;
+ int r = i & 0x1f;
+ int pq = pVec[i] >>> 5;
+ int pr = pVec[i] & 0x1f;
+ for (int j = numRows - 1; j >= 0; j--)
+ {
+ result.matrix[j][q] |= ((matrix[j][pq] >>> pr) & 1) << r;
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute the product of this matrix and the given column vector.
+ *
+ * @param vec the vector over GF(2)
+ * @return <tt>this*vector</tt>
+ */
+ public Vector rightMultiply(Vector vec)
+ {
+ if (!(vec instanceof GF2Vector))
+ {
+ throw new ArithmeticException("vector is not defined over GF(2)");
+ }
+
+ if (vec.length != numColumns)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ int[] v = ((GF2Vector)vec).getVecArray();
+ int[] res = new int[(numRows + 31) >>> 5];
+
+ for (int i = 0; i < numRows; i++)
+ {
+ // compute full word scalar products
+ int help = 0;
+ for (int j = 0; j < length; j++)
+ {
+ help ^= matrix[i][j] & v[j];
+ }
+ // compute single word scalar product
+ int bitValue = 0;
+ for (int j = 0; j < 32; j++)
+ {
+ bitValue ^= (help >>> j) & 1;
+ }
+ // set result bit
+ if (bitValue == 1)
+ {
+ res[i >>> 5] |= 1 << (i & 0x1f);
+ }
+ }
+
+ return new GF2Vector(res, numRows);
+ }
+
+ /**
+ * Compute the product of the matrix <tt>(Id | this)</tt> and a column
+ * vector, where <tt>Id</tt> is a <tt>(numRows x numRows)</tt> unit
+ * matrix.
+ *
+ * @param vec the vector over GF(2)
+ * @return <tt>(Id | this)*vector</tt>
+ */
+ public Vector rightMultiplyRightCompactForm(Vector vec)
+ {
+ if (!(vec instanceof GF2Vector))
+ {
+ throw new ArithmeticException("vector is not defined over GF(2)");
+ }
+
+ if (vec.length != numColumns + numRows)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ int[] v = ((GF2Vector)vec).getVecArray();
+ int[] res = new int[(numRows + 31) >>> 5];
+
+ int q = numRows >> 5;
+ int r = numRows & 0x1f;
+
+ // for all rows
+ for (int i = 0; i < numRows; i++)
+ {
+ // get vector bit
+ int help = (v[i >> 5] >>> (i & 0x1f)) & 1;
+
+ // compute full word scalar products
+ int vInd = q;
+ // if words have to be shifted
+ if (r != 0)
+ {
+ int vw = 0;
+ // process all but last word
+ for (int j = 0; j < length - 1; j++)
+ {
+ // shift to correct position
+ vw = (v[vInd++] >>> r) | (v[vInd] << (32 - r));
+ help ^= matrix[i][j] & vw;
+ }
+ // process last word
+ vw = v[vInd++] >>> r;
+ if (vInd < v.length)
+ {
+ vw |= v[vInd] << (32 - r);
+ }
+ help ^= matrix[i][length - 1] & vw;
+ }
+ else
+ {
+ // no shifting necessary
+ for (int j = 0; j < length; j++)
+ {
+ help ^= matrix[i][j] & v[vInd++];
+ }
+ }
+
+ // compute single word scalar product
+ int bitValue = 0;
+ for (int j = 0; j < 32; j++)
+ {
+ bitValue ^= help & 1;
+ help >>>= 1;
+ }
+
+ // set result bit
+ if (bitValue == 1)
+ {
+ res[i >> 5] |= 1 << (i & 0x1f);
+ }
+ }
+
+ return new GF2Vector(res, numRows);
+ }
+
+ /**
+ * Compare this matrix with another object.
+ *
+ * @param other another object
+ * @return the result of the comparison
+ */
+ public boolean equals(Object other)
+ {
+
+ if (!(other instanceof GF2Matrix))
+ {
+ return false;
+ }
+ GF2Matrix otherMatrix = (GF2Matrix)other;
+
+ if ((numRows != otherMatrix.numRows)
+ || (numColumns != otherMatrix.numColumns)
+ || (length != otherMatrix.length))
+ {
+ return false;
+ }
+
+ for (int i = 0; i < numRows; i++)
+ {
+ if (!IntUtils.equals(matrix[i], otherMatrix.matrix[i]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @return the hash code of this matrix
+ */
+ public int hashCode()
+ {
+ int hash = (numRows * 31 + numColumns) * 31 + length;
+ for (int i = 0; i < numRows; i++)
+ {
+ hash = hash * 31 + matrix[i].hashCode();
+ }
+ return hash;
+ }
+
+ /**
+ * @return a human readable form of the matrix
+ */
+ public String toString()
+ {
+ int rest = numColumns & 0x1f;
+ int d;
+ if (rest == 0)
+ {
+ d = length;
+ }
+ else
+ {
+ d = length - 1;
+ }
+
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < numRows; i++)
+ {
+ buf.append(i + ": ");
+ for (int j = 0; j < d; j++)
+ {
+ int a = matrix[i][j];
+ for (int k = 0; k < 32; k++)
+ {
+ int b = (a >>> k) & 1;
+ if (b == 0)
+ {
+ buf.append('0');
+ }
+ else
+ {
+ buf.append('1');
+ }
+ }
+ buf.append(' ');
+ }
+ int a = matrix[i][length - 1];
+ for (int k = 0; k < rest; k++)
+ {
+ int b = (a >>> k) & 1;
+ if (b == 0)
+ {
+ buf.append('0');
+ }
+ else
+ {
+ buf.append('1');
+ }
+ }
+ buf.append('\n');
+ }
+
+ return buf.toString();
+ }
+
+ /**
+ * Swap two rows of the given matrix.
+ *
+ * @param matrix the matrix
+ * @param first the index of the first row
+ * @param second the index of the second row
+ */
+ private static void swapRows(int[][] matrix, int first, int second)
+ {
+ int[] tmp = matrix[first];
+ matrix[first] = matrix[second];
+ matrix[second] = tmp;
+ }
+
+ /**
+ * Partially add one row to another.
+ *
+ * @param fromRow the addend
+ * @param toRow the row to add to
+ * @param startIndex the array index to start from
+ */
+ private static void addToRow(int[] fromRow, int[] toRow, int startIndex)
+ {
+ for (int i = toRow.length - 1; i >= startIndex; i--)
+ {
+ toRow[i] = fromRow[i] ^ toRow[i];
+ }
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Polynomial.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Polynomial.java
new file mode 100644
index 00000000..64e21e7a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Polynomial.java
@@ -0,0 +1,2039 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+
+import java.math.BigInteger;
+import java.util.Random;
+
+
+/**
+ * This class stores very long strings of bits and does some basic arithmetics.
+ * It is used by <tt>GF2nField</tt>, <tt>GF2nPolynomialField</tt> and
+ * <tt>GFnPolynomialElement</tt>.
+ *
+ * @see GF2nPolynomialElement
+ * @see GF2nField
+ */
+public class GF2Polynomial
+{
+
+ // number of bits stored in this GF2Polynomial
+ private int len;
+
+ // number of int used in value
+ private int blocks;
+
+ // storage
+ private int[] value;
+
+ // Random source
+ private static Random rand = new Random();
+
+ // Lookup-Table for vectorMult: parity[a]= #1(a) mod 2 == 1
+ private static final boolean[] parity = {false, true, true, false, true,
+ false, false, true, true, false, false, true, false, true, true,
+ false, true, false, false, true, false, true, true, false, false,
+ true, true, false, true, false, false, true, true, false, false,
+ true, false, true, true, false, false, true, true, false, true,
+ false, false, true, false, true, true, false, true, false, false,
+ true, true, false, false, true, false, true, true, false, true,
+ false, false, true, false, true, true, false, false, true, true,
+ false, true, false, false, true, false, true, true, false, true,
+ false, false, true, true, false, false, true, false, true, true,
+ false, false, true, true, false, true, false, false, true, true,
+ false, false, true, false, true, true, false, true, false, false,
+ true, false, true, true, false, false, true, true, false, true,
+ false, false, true, true, false, false, true, false, true, true,
+ false, false, true, true, false, true, false, false, true, false,
+ true, true, false, true, false, false, true, true, false, false,
+ true, false, true, true, false, false, true, true, false, true,
+ false, false, true, true, false, false, true, false, true, true,
+ false, true, false, false, true, false, true, true, false, false,
+ true, true, false, true, false, false, true, false, true, true,
+ false, true, false, false, true, true, false, false, true, false,
+ true, true, false, true, false, false, true, false, true, true,
+ false, false, true, true, false, true, false, false, true, true,
+ false, false, true, false, true, true, false, false, true, true,
+ false, true, false, false, true, false, true, true, false, true,
+ false, false, true, true, false, false, true, false, true, true,
+ false};
+
+ // Lookup-Table for Squaring: squaringTable[a]=a^2
+ private static final short[] squaringTable = {0x0000, 0x0001, 0x0004,
+ 0x0005, 0x0010, 0x0011, 0x0014, 0x0015, 0x0040, 0x0041, 0x0044,
+ 0x0045, 0x0050, 0x0051, 0x0054, 0x0055, 0x0100, 0x0101, 0x0104,
+ 0x0105, 0x0110, 0x0111, 0x0114, 0x0115, 0x0140, 0x0141, 0x0144,
+ 0x0145, 0x0150, 0x0151, 0x0154, 0x0155, 0x0400, 0x0401, 0x0404,
+ 0x0405, 0x0410, 0x0411, 0x0414, 0x0415, 0x0440, 0x0441, 0x0444,
+ 0x0445, 0x0450, 0x0451, 0x0454, 0x0455, 0x0500, 0x0501, 0x0504,
+ 0x0505, 0x0510, 0x0511, 0x0514, 0x0515, 0x0540, 0x0541, 0x0544,
+ 0x0545, 0x0550, 0x0551, 0x0554, 0x0555, 0x1000, 0x1001, 0x1004,
+ 0x1005, 0x1010, 0x1011, 0x1014, 0x1015, 0x1040, 0x1041, 0x1044,
+ 0x1045, 0x1050, 0x1051, 0x1054, 0x1055, 0x1100, 0x1101, 0x1104,
+ 0x1105, 0x1110, 0x1111, 0x1114, 0x1115, 0x1140, 0x1141, 0x1144,
+ 0x1145, 0x1150, 0x1151, 0x1154, 0x1155, 0x1400, 0x1401, 0x1404,
+ 0x1405, 0x1410, 0x1411, 0x1414, 0x1415, 0x1440, 0x1441, 0x1444,
+ 0x1445, 0x1450, 0x1451, 0x1454, 0x1455, 0x1500, 0x1501, 0x1504,
+ 0x1505, 0x1510, 0x1511, 0x1514, 0x1515, 0x1540, 0x1541, 0x1544,
+ 0x1545, 0x1550, 0x1551, 0x1554, 0x1555, 0x4000, 0x4001, 0x4004,
+ 0x4005, 0x4010, 0x4011, 0x4014, 0x4015, 0x4040, 0x4041, 0x4044,
+ 0x4045, 0x4050, 0x4051, 0x4054, 0x4055, 0x4100, 0x4101, 0x4104,
+ 0x4105, 0x4110, 0x4111, 0x4114, 0x4115, 0x4140, 0x4141, 0x4144,
+ 0x4145, 0x4150, 0x4151, 0x4154, 0x4155, 0x4400, 0x4401, 0x4404,
+ 0x4405, 0x4410, 0x4411, 0x4414, 0x4415, 0x4440, 0x4441, 0x4444,
+ 0x4445, 0x4450, 0x4451, 0x4454, 0x4455, 0x4500, 0x4501, 0x4504,
+ 0x4505, 0x4510, 0x4511, 0x4514, 0x4515, 0x4540, 0x4541, 0x4544,
+ 0x4545, 0x4550, 0x4551, 0x4554, 0x4555, 0x5000, 0x5001, 0x5004,
+ 0x5005, 0x5010, 0x5011, 0x5014, 0x5015, 0x5040, 0x5041, 0x5044,
+ 0x5045, 0x5050, 0x5051, 0x5054, 0x5055, 0x5100, 0x5101, 0x5104,
+ 0x5105, 0x5110, 0x5111, 0x5114, 0x5115, 0x5140, 0x5141, 0x5144,
+ 0x5145, 0x5150, 0x5151, 0x5154, 0x5155, 0x5400, 0x5401, 0x5404,
+ 0x5405, 0x5410, 0x5411, 0x5414, 0x5415, 0x5440, 0x5441, 0x5444,
+ 0x5445, 0x5450, 0x5451, 0x5454, 0x5455, 0x5500, 0x5501, 0x5504,
+ 0x5505, 0x5510, 0x5511, 0x5514, 0x5515, 0x5540, 0x5541, 0x5544,
+ 0x5545, 0x5550, 0x5551, 0x5554, 0x5555};
+
+ // pre-computed Bitmask for fast masking, bitMask[a]=0x1 << a
+ private static final int[] bitMask = {0x00000001, 0x00000002, 0x00000004,
+ 0x00000008, 0x00000010, 0x00000020, 0x00000040, 0x00000080,
+ 0x00000100, 0x00000200, 0x00000400, 0x00000800, 0x00001000,
+ 0x00002000, 0x00004000, 0x00008000, 0x00010000, 0x00020000,
+ 0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000,
+ 0x00800000, 0x01000000, 0x02000000, 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x00000000};
+
+ // pre-computed Bitmask for fast masking, rightMask[a]=0xffffffff >>> (32-a)
+ private static final int[] reverseRightMask = {0x00000000, 0x00000001,
+ 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f,
+ 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff,
+ 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff,
+ 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, 0x001fffff,
+ 0x003fffff, 0x007fffff, 0x00ffffff, 0x01ffffff, 0x03ffffff,
+ 0x07ffffff, 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff,
+ 0xffffffff};
+
+ /**
+ * Creates a new GF2Polynomial of the given <i>length</i> and value zero.
+ *
+ * @param length the desired number of bits to store
+ */
+ public GF2Polynomial(int length)
+ {
+ int l = length;
+ if (l < 1)
+ {
+ l = 1;
+ }
+ blocks = ((l - 1) >> 5) + 1;
+ value = new int[blocks];
+ len = l;
+ }
+
+ /**
+ * Creates a new GF2Polynomial of the given <i>length</i> and random value.
+ *
+ * @param length the desired number of bits to store
+ * @param rand SecureRandom to use for randomization
+ */
+ public GF2Polynomial(int length, Random rand)
+ {
+ int l = length;
+ if (l < 1)
+ {
+ l = 1;
+ }
+ blocks = ((l - 1) >> 5) + 1;
+ value = new int[blocks];
+ len = l;
+ randomize(rand);
+ }
+
+ /**
+ * Creates a new GF2Polynomial of the given <i>length</i> and value
+ * selected by <i>value</i>:
+ * <UL>
+ * <LI>ZERO</LI>
+ * <LI>ONE</LI>
+ * <LI>RANDOM</LI>
+ * <LI>X</LI>
+ * <LI>ALL</LI>
+ * </UL>
+ *
+ * @param length the desired number of bits to store
+ * @param value the value described by a String
+ */
+ public GF2Polynomial(int length, String value)
+ {
+ int l = length;
+ if (l < 1)
+ {
+ l = 1;
+ }
+ blocks = ((l - 1) >> 5) + 1;
+ this.value = new int[blocks];
+ len = l;
+ if (value.equalsIgnoreCase("ZERO"))
+ {
+ assignZero();
+ }
+ else if (value.equalsIgnoreCase("ONE"))
+ {
+ assignOne();
+ }
+ else if (value.equalsIgnoreCase("RANDOM"))
+ {
+ randomize();
+ }
+ else if (value.equalsIgnoreCase("X"))
+ {
+ assignX();
+ }
+ else if (value.equalsIgnoreCase("ALL"))
+ {
+ assignAll();
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "Error: GF2Polynomial was called using " + value
+ + " as value!");
+ }
+
+ }
+
+ /**
+ * Creates a new GF2Polynomial of the given <i>length</i> using the given
+ * int[]. LSB is contained in bs[0].
+ *
+ * @param length the desired number of bits to store
+ * @param bs contains the desired value, LSB in bs[0]
+ */
+ public GF2Polynomial(int length, int[] bs)
+ {
+ int leng = length;
+ if (leng < 1)
+ {
+ leng = 1;
+ }
+ blocks = ((leng - 1) >> 5) + 1;
+ value = new int[blocks];
+ len = leng;
+ int l = Math.min(blocks, bs.length);
+ System.arraycopy(bs, 0, value, 0, l);
+ zeroUnusedBits();
+ }
+
+ /**
+ * Creates a new GF2Polynomial by converting the given byte[] <i>os</i>
+ * according to 1363 and using the given <i>length</i>.
+ *
+ * @param length the intended length of this polynomial
+ * @param os the octet string to assign to this polynomial
+ * @see "P1363 5.5.2 p22f, OS2BSP"
+ */
+ public GF2Polynomial(int length, byte[] os)
+ {
+ int l = length;
+ if (l < 1)
+ {
+ l = 1;
+ }
+ blocks = ((l - 1) >> 5) + 1;
+ value = new int[blocks];
+ len = l;
+ int i, m;
+ int k = Math.min(((os.length - 1) >> 2) + 1, blocks);
+ for (i = 0; i < k - 1; i++)
+ {
+ m = os.length - (i << 2) - 1;
+ value[i] = (os[m]) & 0x000000ff;
+ value[i] |= (os[m - 1] << 8) & 0x0000ff00;
+ value[i] |= (os[m - 2] << 16) & 0x00ff0000;
+ value[i] |= (os[m - 3] << 24) & 0xff000000;
+ }
+ i = k - 1;
+ m = os.length - (i << 2) - 1;
+ value[i] = os[m] & 0x000000ff;
+ if (m > 0)
+ {
+ value[i] |= (os[m - 1] << 8) & 0x0000ff00;
+ }
+ if (m > 1)
+ {
+ value[i] |= (os[m - 2] << 16) & 0x00ff0000;
+ }
+ if (m > 2)
+ {
+ value[i] |= (os[m - 3] << 24) & 0xff000000;
+ }
+ zeroUnusedBits();
+ reduceN();
+ }
+
+ /**
+ * Creates a new GF2Polynomial by converting the given FlexiBigInt <i>bi</i>
+ * according to 1363 and using the given <i>length</i>.
+ *
+ * @param length the intended length of this polynomial
+ * @param bi the FlexiBigInt to assign to this polynomial
+ * @see "P1363 5.5.1 p22, I2BSP"
+ */
+ public GF2Polynomial(int length, BigInteger bi)
+ {
+ int l = length;
+ if (l < 1)
+ {
+ l = 1;
+ }
+ blocks = ((l - 1) >> 5) + 1;
+ value = new int[blocks];
+ len = l;
+ int i;
+ byte[] val = bi.toByteArray();
+ if (val[0] == 0)
+ {
+ byte[] dummy = new byte[val.length - 1];
+ System.arraycopy(val, 1, dummy, 0, dummy.length);
+ val = dummy;
+ }
+ int ov = val.length & 0x03;
+ int k = ((val.length - 1) >> 2) + 1;
+ for (i = 0; i < ov; i++)
+ {
+ value[k - 1] |= (val[i] & 0x000000ff) << ((ov - 1 - i) << 3);
+ }
+ int m = 0;
+ for (i = 0; i <= (val.length - 4) >> 2; i++)
+ {
+ m = val.length - 1 - (i << 2);
+ value[i] = (val[m]) & 0x000000ff;
+ value[i] |= ((val[m - 1]) << 8) & 0x0000ff00;
+ value[i] |= ((val[m - 2]) << 16) & 0x00ff0000;
+ value[i] |= ((val[m - 3]) << 24) & 0xff000000;
+ }
+ if ((len & 0x1f) != 0)
+ {
+ value[blocks - 1] &= reverseRightMask[len & 0x1f];
+ }
+ reduceN();
+ }
+
+ /**
+ * Creates a new GF2Polynomial by cloneing the given GF2Polynomial <i>b</i>.
+ *
+ * @param b the GF2Polynomial to clone
+ */
+ public GF2Polynomial(GF2Polynomial b)
+ {
+ len = b.len;
+ blocks = b.blocks;
+ value = IntUtils.clone(b.value);
+ }
+
+ /**
+ * @return a copy of this GF2Polynomial
+ */
+ public Object clone()
+ {
+ return new GF2Polynomial(this);
+ }
+
+ /**
+ * Returns the length of this GF2Polynomial. The length can be greater than
+ * the degree. To get the degree call reduceN() before calling getLength().
+ *
+ * @return the length of this GF2Polynomial
+ */
+ public int getLength()
+ {
+ return len;
+ }
+
+ /**
+ * Returns the value of this GF2Polynomial in an int[].
+ *
+ * @return the value of this GF2Polynomial in a new int[], LSB in int[0]
+ */
+ public int[] toIntegerArray()
+ {
+ int[] result;
+ result = new int[blocks];
+ System.arraycopy(value, 0, result, 0, blocks);
+ return result;
+ }
+
+ /**
+ * Returns a string representing this GF2Polynomials value using hexadecimal
+ * or binary radix in MSB-first order.
+ *
+ * @param radix the radix to use (2 or 16, otherwise 2 is used)
+ * @return a String representing this GF2Polynomials value.
+ */
+ public String toString(int radix)
+ {
+ final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
+ '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ final String[] BIN_CHARS = {"0000", "0001", "0010", "0011", "0100",
+ "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100",
+ "1101", "1110", "1111"};
+ String res;
+ int i;
+ res = new String();
+ if (radix == 16)
+ {
+ for (i = blocks - 1; i >= 0; i--)
+ {
+ res += HEX_CHARS[(value[i] >>> 28) & 0x0f];
+ res += HEX_CHARS[(value[i] >>> 24) & 0x0f];
+ res += HEX_CHARS[(value[i] >>> 20) & 0x0f];
+ res += HEX_CHARS[(value[i] >>> 16) & 0x0f];
+ res += HEX_CHARS[(value[i] >>> 12) & 0x0f];
+ res += HEX_CHARS[(value[i] >>> 8) & 0x0f];
+ res += HEX_CHARS[(value[i] >>> 4) & 0x0f];
+ res += HEX_CHARS[(value[i]) & 0x0f];
+ res += " ";
+ }
+ }
+ else
+ {
+ for (i = blocks - 1; i >= 0; i--)
+ {
+ res += BIN_CHARS[(value[i] >>> 28) & 0x0f];
+ res += BIN_CHARS[(value[i] >>> 24) & 0x0f];
+ res += BIN_CHARS[(value[i] >>> 20) & 0x0f];
+ res += BIN_CHARS[(value[i] >>> 16) & 0x0f];
+ res += BIN_CHARS[(value[i] >>> 12) & 0x0f];
+ res += BIN_CHARS[(value[i] >>> 8) & 0x0f];
+ res += BIN_CHARS[(value[i] >>> 4) & 0x0f];
+ res += BIN_CHARS[(value[i]) & 0x0f];
+ res += " ";
+ }
+ }
+ return res;
+ }
+
+ /**
+ * Converts this polynomial to a byte[] (octet string) according to 1363.
+ *
+ * @return a byte[] representing the value of this polynomial
+ * @see "P1363 5.5.2 p22f, BS2OSP"
+ */
+ public byte[] toByteArray()
+ {
+ int k = ((len - 1) >> 3) + 1;
+ int ov = k & 0x03;
+ int m;
+ byte[] res = new byte[k];
+ int i;
+ for (i = 0; i < (k >> 2); i++)
+ {
+ m = k - (i << 2) - 1;
+ res[m] = (byte)((value[i] & 0x000000ff));
+ res[m - 1] = (byte)((value[i] & 0x0000ff00) >>> 8);
+ res[m - 2] = (byte)((value[i] & 0x00ff0000) >>> 16);
+ res[m - 3] = (byte)((value[i] & 0xff000000) >>> 24);
+ }
+ for (i = 0; i < ov; i++)
+ {
+ m = (ov - i - 1) << 3;
+ res[i] = (byte)((value[blocks - 1] & (0x000000ff << m)) >>> m);
+ }
+ return res;
+ }
+
+ /**
+ * Converts this polynomial to an integer according to 1363.
+ *
+ * @return a FlexiBigInt representing the value of this polynomial
+ * @see "P1363 5.5.1 p22, BS2IP"
+ */
+ public BigInteger toFlexiBigInt()
+ {
+ if (len == 0 || isZero())
+ {
+ return new BigInteger(0, new byte[0]);
+ }
+ return new BigInteger(1, toByteArray());
+ }
+
+ /**
+ * Sets the LSB to 1 and all other to 0, assigning 'one' to this
+ * GF2Polynomial.
+ */
+ public void assignOne()
+ {
+ int i;
+ for (i = 1; i < blocks; i++)
+ {
+ value[i] = 0x00;
+ }
+ value[0] = 0x01;
+ }
+
+ /**
+ * Sets Bit 1 to 1 and all other to 0, assigning 'x' to this GF2Polynomial.
+ */
+ public void assignX()
+ {
+ int i;
+ for (i = 1; i < blocks; i++)
+ {
+ value[i] = 0x00;
+ }
+ value[0] = 0x02;
+ }
+
+ /**
+ * Sets all Bits to 1.
+ */
+ public void assignAll()
+ {
+ int i;
+ for (i = 0; i < blocks; i++)
+ {
+ value[i] = 0xffffffff;
+ }
+ zeroUnusedBits();
+ }
+
+ /**
+ * Resets all bits to zero.
+ */
+ public void assignZero()
+ {
+ int i;
+ for (i = 0; i < blocks; i++)
+ {
+ value[i] = 0x00;
+ }
+ }
+
+ /**
+ * Fills all len bits of this GF2Polynomial with random values.
+ */
+ public void randomize()
+ {
+ int i;
+ for (i = 0; i < blocks; i++)
+ {
+ value[i] = rand.nextInt();
+ }
+ zeroUnusedBits();
+ }
+
+ /**
+ * Fills all len bits of this GF2Polynomial with random values using the
+ * specified source of randomness.
+ *
+ * @param rand the source of randomness
+ */
+ public void randomize(Random rand)
+ {
+ int i;
+ for (i = 0; i < blocks; i++)
+ {
+ value[i] = rand.nextInt();
+ }
+ zeroUnusedBits();
+ }
+
+ /**
+ * Returns true if two GF2Polynomials have the same size and value and thus
+ * are equal.
+ *
+ * @param other the other GF2Polynomial
+ * @return true if this GF2Polynomial equals <i>b</i> (<i>this</i> ==
+ * <i>b</i>)
+ */
+ public boolean equals(Object other)
+ {
+ if (other == null || !(other instanceof GF2Polynomial))
+ {
+ return false;
+ }
+
+ GF2Polynomial otherPol = (GF2Polynomial)other;
+
+ if (len != otherPol.len)
+ {
+ return false;
+ }
+ for (int i = 0; i < blocks; i++)
+ {
+ if (value[i] != otherPol.value[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @return the hash code of this polynomial
+ */
+ public int hashCode()
+ {
+ return len + value.hashCode();
+ }
+
+ /**
+ * Tests if all bits equal zero.
+ *
+ * @return true if this GF2Polynomial equals 'zero' (<i>this</i> == 0)
+ */
+ public boolean isZero()
+ {
+ int i;
+ if (len == 0)
+ {
+ return true;
+ }
+ for (i = 0; i < blocks; i++)
+ {
+ if (value[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Tests if all bits are reset to 0 and LSB is set to 1.
+ *
+ * @return true if this GF2Polynomial equals 'one' (<i>this</i> == 1)
+ */
+ public boolean isOne()
+ {
+ int i;
+ for (i = 1; i < blocks; i++)
+ {
+ if (value[i] != 0)
+ {
+ return false;
+ }
+ }
+ if (value[0] != 0x01)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Adds <i>b</i> to this GF2Polynomial and assigns the result to this
+ * GF2Polynomial. <i>b</i> can be of different size.
+ *
+ * @param b GF2Polynomial to add to this GF2Polynomial
+ */
+ public void addToThis(GF2Polynomial b)
+ {
+ expandN(b.len);
+ xorThisBy(b);
+ }
+
+ /**
+ * Adds two GF2Polynomials, <i>this</i> and <i>b</i>, and returns the
+ * result. <i>this</i> and <i>b</i> can be of different size.
+ *
+ * @param b a GF2Polynomial
+ * @return a new GF2Polynomial (<i>this</i> + <i>b</i>)
+ */
+ public GF2Polynomial add(GF2Polynomial b)
+ {
+ return xor(b);
+ }
+
+ /**
+ * Subtracts <i>b</i> from this GF2Polynomial and assigns the result to
+ * this GF2Polynomial. <i>b</i> can be of different size.
+ *
+ * @param b a GF2Polynomial
+ */
+ public void subtractFromThis(GF2Polynomial b)
+ {
+ expandN(b.len);
+ xorThisBy(b);
+ }
+
+ /**
+ * Subtracts two GF2Polynomials, <i>this</i> and <i>b</i>, and returns the
+ * result in a new GF2Polynomial. <i>this</i> and <i>b</i> can be of
+ * different size.
+ *
+ * @param b a GF2Polynomial
+ * @return a new GF2Polynomial (<i>this</i> - <i>b</i>)
+ */
+ public GF2Polynomial subtract(GF2Polynomial b)
+ {
+ return xor(b);
+ }
+
+ /**
+ * Toggles the LSB of this GF2Polynomial, increasing its value by 'one'.
+ */
+ public void increaseThis()
+ {
+ xorBit(0);
+ }
+
+ /**
+ * Toggles the LSB of this GF2Polynomial, increasing the value by 'one' and
+ * returns the result in a new GF2Polynomial.
+ *
+ * @return <tt>this + 1</tt>
+ */
+ public GF2Polynomial increase()
+ {
+ GF2Polynomial result = new GF2Polynomial(this);
+ result.increaseThis();
+ return result;
+ }
+
+ /**
+ * Multiplies this GF2Polynomial with <i>b</i> and returns the result in a
+ * new GF2Polynomial. This method does not reduce the result in GF(2^N).
+ * This method uses classic multiplication (schoolbook).
+ *
+ * @param b a GF2Polynomial
+ * @return a new GF2Polynomial (<i>this</i> * <i>b</i>)
+ */
+ public GF2Polynomial multiplyClassic(GF2Polynomial b)
+ {
+ GF2Polynomial result = new GF2Polynomial(Math.max(len, b.len) << 1);
+ GF2Polynomial[] m = new GF2Polynomial[32];
+ int i, j;
+ m[0] = new GF2Polynomial(this);
+ for (i = 1; i <= 31; i++)
+ {
+ m[i] = m[i - 1].shiftLeft();
+ }
+ for (i = 0; i < b.blocks; i++)
+ {
+ for (j = 0; j <= 31; j++)
+ {
+ if ((b.value[i] & bitMask[j]) != 0)
+ {
+ result.xorThisBy(m[j]);
+ }
+ }
+ for (j = 0; j <= 31; j++)
+ {
+ m[j].shiftBlocksLeft();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Multiplies this GF2Polynomial with <i>b</i> and returns the result in a
+ * new GF2Polynomial. This method does not reduce the result in GF(2^N).
+ * This method uses Karatzuba multiplication.
+ *
+ * @param b a GF2Polynomial
+ * @return a new GF2Polynomial (<i>this</i> * <i>b</i>)
+ */
+ public GF2Polynomial multiply(GF2Polynomial b)
+ {
+ int n = Math.max(len, b.len);
+ expandN(n);
+ b.expandN(n);
+ return karaMult(b);
+ }
+
+ /**
+ * Does the recursion for Karatzuba multiplication.
+ */
+ private GF2Polynomial karaMult(GF2Polynomial b)
+ {
+ GF2Polynomial result = new GF2Polynomial(len << 1);
+ if (len <= 32)
+ {
+ result.value = mult32(value[0], b.value[0]);
+ return result;
+ }
+ if (len <= 64)
+ {
+ result.value = mult64(value, b.value);
+ return result;
+ }
+ if (len <= 128)
+ {
+ result.value = mult128(value, b.value);
+ return result;
+ }
+ if (len <= 256)
+ {
+ result.value = mult256(value, b.value);
+ return result;
+ }
+ if (len <= 512)
+ {
+ result.value = mult512(value, b.value);
+ return result;
+ }
+
+ int n = IntegerFunctions.floorLog(len - 1);
+ n = bitMask[n];
+
+ GF2Polynomial a0 = lower(((n - 1) >> 5) + 1);
+ GF2Polynomial a1 = upper(((n - 1) >> 5) + 1);
+ GF2Polynomial b0 = b.lower(((n - 1) >> 5) + 1);
+ GF2Polynomial b1 = b.upper(((n - 1) >> 5) + 1);
+
+ GF2Polynomial c = a1.karaMult(b1); // c = a1*b1
+ GF2Polynomial e = a0.karaMult(b0); // e = a0*b0
+ a0.addToThis(a1); // a0 = a0 + a1
+ b0.addToThis(b1); // b0 = b0 + b1
+ GF2Polynomial d = a0.karaMult(b0); // d = (a0+a1)*(b0+b1)
+
+ result.shiftLeftAddThis(c, n << 1);
+ result.shiftLeftAddThis(c, n);
+ result.shiftLeftAddThis(d, n);
+ result.shiftLeftAddThis(e, n);
+ result.addToThis(e);
+ return result;
+ }
+
+ /**
+ * 16-Integer Version of Karatzuba multiplication.
+ */
+ private static int[] mult512(int[] a, int[] b)
+ {
+ int[] result = new int[32];
+ int[] a0 = new int[8];
+ System.arraycopy(a, 0, a0, 0, Math.min(8, a.length));
+ int[] a1 = new int[8];
+ if (a.length > 8)
+ {
+ System.arraycopy(a, 8, a1, 0, Math.min(8, a.length - 8));
+ }
+ int[] b0 = new int[8];
+ System.arraycopy(b, 0, b0, 0, Math.min(8, b.length));
+ int[] b1 = new int[8];
+ if (b.length > 8)
+ {
+ System.arraycopy(b, 8, b1, 0, Math.min(8, b.length - 8));
+ }
+ int[] c = mult256(a1, b1);
+ result[31] ^= c[15];
+ result[30] ^= c[14];
+ result[29] ^= c[13];
+ result[28] ^= c[12];
+ result[27] ^= c[11];
+ result[26] ^= c[10];
+ result[25] ^= c[9];
+ result[24] ^= c[8];
+ result[23] ^= c[7] ^ c[15];
+ result[22] ^= c[6] ^ c[14];
+ result[21] ^= c[5] ^ c[13];
+ result[20] ^= c[4] ^ c[12];
+ result[19] ^= c[3] ^ c[11];
+ result[18] ^= c[2] ^ c[10];
+ result[17] ^= c[1] ^ c[9];
+ result[16] ^= c[0] ^ c[8];
+ result[15] ^= c[7];
+ result[14] ^= c[6];
+ result[13] ^= c[5];
+ result[12] ^= c[4];
+ result[11] ^= c[3];
+ result[10] ^= c[2];
+ result[9] ^= c[1];
+ result[8] ^= c[0];
+ a1[0] ^= a0[0];
+ a1[1] ^= a0[1];
+ a1[2] ^= a0[2];
+ a1[3] ^= a0[3];
+ a1[4] ^= a0[4];
+ a1[5] ^= a0[5];
+ a1[6] ^= a0[6];
+ a1[7] ^= a0[7];
+ b1[0] ^= b0[0];
+ b1[1] ^= b0[1];
+ b1[2] ^= b0[2];
+ b1[3] ^= b0[3];
+ b1[4] ^= b0[4];
+ b1[5] ^= b0[5];
+ b1[6] ^= b0[6];
+ b1[7] ^= b0[7];
+ int[] d = mult256(a1, b1);
+ result[23] ^= d[15];
+ result[22] ^= d[14];
+ result[21] ^= d[13];
+ result[20] ^= d[12];
+ result[19] ^= d[11];
+ result[18] ^= d[10];
+ result[17] ^= d[9];
+ result[16] ^= d[8];
+ result[15] ^= d[7];
+ result[14] ^= d[6];
+ result[13] ^= d[5];
+ result[12] ^= d[4];
+ result[11] ^= d[3];
+ result[10] ^= d[2];
+ result[9] ^= d[1];
+ result[8] ^= d[0];
+ int[] e = mult256(a0, b0);
+ result[23] ^= e[15];
+ result[22] ^= e[14];
+ result[21] ^= e[13];
+ result[20] ^= e[12];
+ result[19] ^= e[11];
+ result[18] ^= e[10];
+ result[17] ^= e[9];
+ result[16] ^= e[8];
+ result[15] ^= e[7] ^ e[15];
+ result[14] ^= e[6] ^ e[14];
+ result[13] ^= e[5] ^ e[13];
+ result[12] ^= e[4] ^ e[12];
+ result[11] ^= e[3] ^ e[11];
+ result[10] ^= e[2] ^ e[10];
+ result[9] ^= e[1] ^ e[9];
+ result[8] ^= e[0] ^ e[8];
+ result[7] ^= e[7];
+ result[6] ^= e[6];
+ result[5] ^= e[5];
+ result[4] ^= e[4];
+ result[3] ^= e[3];
+ result[2] ^= e[2];
+ result[1] ^= e[1];
+ result[0] ^= e[0];
+ return result;
+ }
+
+ /**
+ * 8-Integer Version of Karatzuba multiplication.
+ */
+ private static int[] mult256(int[] a, int[] b)
+ {
+ int[] result = new int[16];
+ int[] a0 = new int[4];
+ System.arraycopy(a, 0, a0, 0, Math.min(4, a.length));
+ int[] a1 = new int[4];
+ if (a.length > 4)
+ {
+ System.arraycopy(a, 4, a1, 0, Math.min(4, a.length - 4));
+ }
+ int[] b0 = new int[4];
+ System.arraycopy(b, 0, b0, 0, Math.min(4, b.length));
+ int[] b1 = new int[4];
+ if (b.length > 4)
+ {
+ System.arraycopy(b, 4, b1, 0, Math.min(4, b.length - 4));
+ }
+ if (a1[3] == 0 && a1[2] == 0 && b1[3] == 0 && b1[2] == 0)
+ {
+ if (a1[1] == 0 && b1[1] == 0)
+ {
+ if (a1[0] != 0 || b1[0] != 0)
+ { // [3]=[2]=[1]=0, [0]!=0
+ int[] c = mult32(a1[0], b1[0]);
+ result[9] ^= c[1];
+ result[8] ^= c[0];
+ result[5] ^= c[1];
+ result[4] ^= c[0];
+ }
+ }
+ else
+ { // [3]=[2]=0 [1]!=0, [0]!=0
+ int[] c = mult64(a1, b1);
+ result[11] ^= c[3];
+ result[10] ^= c[2];
+ result[9] ^= c[1];
+ result[8] ^= c[0];
+ result[7] ^= c[3];
+ result[6] ^= c[2];
+ result[5] ^= c[1];
+ result[4] ^= c[0];
+ }
+ }
+ else
+ { // [3]!=0 [2]!=0 [1]!=0, [0]!=0
+ int[] c = mult128(a1, b1);
+ result[15] ^= c[7];
+ result[14] ^= c[6];
+ result[13] ^= c[5];
+ result[12] ^= c[4];
+ result[11] ^= c[3] ^ c[7];
+ result[10] ^= c[2] ^ c[6];
+ result[9] ^= c[1] ^ c[5];
+ result[8] ^= c[0] ^ c[4];
+ result[7] ^= c[3];
+ result[6] ^= c[2];
+ result[5] ^= c[1];
+ result[4] ^= c[0];
+ }
+ a1[0] ^= a0[0];
+ a1[1] ^= a0[1];
+ a1[2] ^= a0[2];
+ a1[3] ^= a0[3];
+ b1[0] ^= b0[0];
+ b1[1] ^= b0[1];
+ b1[2] ^= b0[2];
+ b1[3] ^= b0[3];
+ int[] d = mult128(a1, b1);
+ result[11] ^= d[7];
+ result[10] ^= d[6];
+ result[9] ^= d[5];
+ result[8] ^= d[4];
+ result[7] ^= d[3];
+ result[6] ^= d[2];
+ result[5] ^= d[1];
+ result[4] ^= d[0];
+ int[] e = mult128(a0, b0);
+ result[11] ^= e[7];
+ result[10] ^= e[6];
+ result[9] ^= e[5];
+ result[8] ^= e[4];
+ result[7] ^= e[3] ^ e[7];
+ result[6] ^= e[2] ^ e[6];
+ result[5] ^= e[1] ^ e[5];
+ result[4] ^= e[0] ^ e[4];
+ result[3] ^= e[3];
+ result[2] ^= e[2];
+ result[1] ^= e[1];
+ result[0] ^= e[0];
+ return result;
+ }
+
+ /**
+ * 4-Integer Version of Karatzuba multiplication.
+ */
+ private static int[] mult128(int[] a, int[] b)
+ {
+ int[] result = new int[8];
+ int[] a0 = new int[2];
+ System.arraycopy(a, 0, a0, 0, Math.min(2, a.length));
+ int[] a1 = new int[2];
+ if (a.length > 2)
+ {
+ System.arraycopy(a, 2, a1, 0, Math.min(2, a.length - 2));
+ }
+ int[] b0 = new int[2];
+ System.arraycopy(b, 0, b0, 0, Math.min(2, b.length));
+ int[] b1 = new int[2];
+ if (b.length > 2)
+ {
+ System.arraycopy(b, 2, b1, 0, Math.min(2, b.length - 2));
+ }
+ if (a1[1] == 0 && b1[1] == 0)
+ {
+ if (a1[0] != 0 || b1[0] != 0)
+ {
+ int[] c = mult32(a1[0], b1[0]);
+ result[5] ^= c[1];
+ result[4] ^= c[0];
+ result[3] ^= c[1];
+ result[2] ^= c[0];
+ }
+ }
+ else
+ {
+ int[] c = mult64(a1, b1);
+ result[7] ^= c[3];
+ result[6] ^= c[2];
+ result[5] ^= c[1] ^ c[3];
+ result[4] ^= c[0] ^ c[2];
+ result[3] ^= c[1];
+ result[2] ^= c[0];
+ }
+ a1[0] ^= a0[0];
+ a1[1] ^= a0[1];
+ b1[0] ^= b0[0];
+ b1[1] ^= b0[1];
+ if (a1[1] == 0 && b1[1] == 0)
+ {
+ int[] d = mult32(a1[0], b1[0]);
+ result[3] ^= d[1];
+ result[2] ^= d[0];
+ }
+ else
+ {
+ int[] d = mult64(a1, b1);
+ result[5] ^= d[3];
+ result[4] ^= d[2];
+ result[3] ^= d[1];
+ result[2] ^= d[0];
+ }
+ if (a0[1] == 0 && b0[1] == 0)
+ {
+ int[] e = mult32(a0[0], b0[0]);
+ result[3] ^= e[1];
+ result[2] ^= e[0];
+ result[1] ^= e[1];
+ result[0] ^= e[0];
+ }
+ else
+ {
+ int[] e = mult64(a0, b0);
+ result[5] ^= e[3];
+ result[4] ^= e[2];
+ result[3] ^= e[1] ^ e[3];
+ result[2] ^= e[0] ^ e[2];
+ result[1] ^= e[1];
+ result[0] ^= e[0];
+ }
+ return result;
+ }
+
+ /**
+ * 2-Integer Version of Karatzuba multiplication.
+ */
+ private static int[] mult64(int[] a, int[] b)
+ {
+ int[] result = new int[4];
+ int a0 = a[0];
+ int a1 = 0;
+ if (a.length > 1)
+ {
+ a1 = a[1];
+ }
+ int b0 = b[0];
+ int b1 = 0;
+ if (b.length > 1)
+ {
+ b1 = b[1];
+ }
+ if (a1 != 0 || b1 != 0)
+ {
+ int[] c = mult32(a1, b1);
+ result[3] ^= c[1];
+ result[2] ^= c[0] ^ c[1];
+ result[1] ^= c[0];
+ }
+ int[] d = mult32(a0 ^ a1, b0 ^ b1);
+ result[2] ^= d[1];
+ result[1] ^= d[0];
+ int[] e = mult32(a0, b0);
+ result[2] ^= e[1];
+ result[1] ^= e[0] ^ e[1];
+ result[0] ^= e[0];
+ return result;
+ }
+
+ /**
+ * 4-Byte Version of Karatzuba multiplication. Here the actual work is done.
+ */
+ private static int[] mult32(int a, int b)
+ {
+ int[] result = new int[2];
+ if (a == 0 || b == 0)
+ {
+ return result;
+ }
+ long b2 = b;
+ b2 &= 0x00000000ffffffffL;
+ int i;
+ long h = 0;
+ for (i = 1; i <= 32; i++)
+ {
+ if ((a & bitMask[i - 1]) != 0)
+ {
+ h ^= b2;
+ }
+ b2 <<= 1;
+ }
+ result[1] = (int)(h >>> 32);
+ result[0] = (int)(h & 0x00000000ffffffffL);
+ return result;
+ }
+
+ /**
+ * Returns a new GF2Polynomial containing the upper <i>k</i> bytes of this
+ * GF2Polynomial.
+ *
+ * @param k
+ * @return a new GF2Polynomial containing the upper <i>k</i> bytes of this
+ * GF2Polynomial
+ * @see GF2Polynomial#karaMult
+ */
+ private GF2Polynomial upper(int k)
+ {
+ int j = Math.min(k, blocks - k);
+ GF2Polynomial result = new GF2Polynomial(j << 5);
+ if (blocks >= k)
+ {
+ System.arraycopy(value, k, result.value, 0, j);
+ }
+ return result;
+ }
+
+ /**
+ * Returns a new GF2Polynomial containing the lower <i>k</i> bytes of this
+ * GF2Polynomial.
+ *
+ * @param k
+ * @return a new GF2Polynomial containing the lower <i>k</i> bytes of this
+ * GF2Polynomial
+ * @see GF2Polynomial#karaMult
+ */
+ private GF2Polynomial lower(int k)
+ {
+ GF2Polynomial result = new GF2Polynomial(k << 5);
+ System.arraycopy(value, 0, result.value, 0, Math.min(k, blocks));
+ return result;
+ }
+
+ /**
+ * Returns the remainder of <i>this</i> divided by <i>g</i> in a new
+ * GF2Polynomial.
+ *
+ * @param g GF2Polynomial != 0
+ * @return a new GF2Polynomial (<i>this</i> % <i>g</i>)
+ * @throws PolynomialIsZeroException if <i>g</i> equals zero
+ */
+ public GF2Polynomial remainder(GF2Polynomial g)
+ throws RuntimeException
+ {
+ /* a div b = q / r */
+ GF2Polynomial a = new GF2Polynomial(this);
+ GF2Polynomial b = new GF2Polynomial(g);
+ GF2Polynomial j;
+ int i;
+ if (b.isZero())
+ {
+ throw new RuntimeException();
+ }
+ a.reduceN();
+ b.reduceN();
+ if (a.len < b.len)
+ {
+ return a;
+ }
+ i = a.len - b.len;
+ while (i >= 0)
+ {
+ j = b.shiftLeft(i);
+ a.subtractFromThis(j);
+ a.reduceN();
+ i = a.len - b.len;
+ }
+ return a;
+ }
+
+ /**
+ * Returns the absolute quotient of <i>this</i> divided by <i>g</i> in a
+ * new GF2Polynomial.
+ *
+ * @param g GF2Polynomial != 0
+ * @return a new GF2Polynomial |_ <i>this</i> / <i>g</i> _|
+ * @throws PolynomialIsZeroException if <i>g</i> equals zero
+ */
+ public GF2Polynomial quotient(GF2Polynomial g)
+ throws RuntimeException
+ {
+ /* a div b = q / r */
+ GF2Polynomial q = new GF2Polynomial(len);
+ GF2Polynomial a = new GF2Polynomial(this);
+ GF2Polynomial b = new GF2Polynomial(g);
+ GF2Polynomial j;
+ int i;
+ if (b.isZero())
+ {
+ throw new RuntimeException();
+ }
+ a.reduceN();
+ b.reduceN();
+ if (a.len < b.len)
+ {
+ return new GF2Polynomial(0);
+ }
+ i = a.len - b.len;
+ q.expandN(i + 1);
+
+ while (i >= 0)
+ {
+ j = b.shiftLeft(i);
+ a.subtractFromThis(j);
+ a.reduceN();
+ q.xorBit(i);
+ i = a.len - b.len;
+ }
+
+ return q;
+ }
+
+ /**
+ * Divides <i>this</i> by <i>g</i> and returns the quotient and remainder
+ * in a new GF2Polynomial[2], quotient in [0], remainder in [1].
+ *
+ * @param g GF2Polynomial != 0
+ * @return a new GF2Polynomial[2] containing quotient and remainder
+ * @throws PolynomialIsZeroException if <i>g</i> equals zero
+ */
+ public GF2Polynomial[] divide(GF2Polynomial g)
+ throws RuntimeException
+ {
+ /* a div b = q / r */
+ GF2Polynomial[] result = new GF2Polynomial[2];
+ GF2Polynomial q = new GF2Polynomial(len);
+ GF2Polynomial a = new GF2Polynomial(this);
+ GF2Polynomial b = new GF2Polynomial(g);
+ GF2Polynomial j;
+ int i;
+ if (b.isZero())
+ {
+ throw new RuntimeException();
+ }
+ a.reduceN();
+ b.reduceN();
+ if (a.len < b.len)
+ {
+ result[0] = new GF2Polynomial(0);
+ result[1] = a;
+ return result;
+ }
+ i = a.len - b.len;
+ q.expandN(i + 1);
+
+ while (i >= 0)
+ {
+ j = b.shiftLeft(i);
+ a.subtractFromThis(j);
+ a.reduceN();
+ q.xorBit(i);
+ i = a.len - b.len;
+ }
+
+ result[0] = q;
+ result[1] = a;
+ return result;
+ }
+
+ /**
+ * Returns the greatest common divisor of <i>this</i> and <i>g</i> in a
+ * new GF2Polynomial.
+ *
+ * @param g GF2Polynomial != 0
+ * @return a new GF2Polynomial gcd(<i>this</i>,<i>g</i>)
+ * @throws ArithmeticException if <i>this</i> and <i>g</i> both are equal to zero
+ * @throws PolynomialIsZeroException to be API-compliant (should never be thrown).
+ */
+ public GF2Polynomial gcd(GF2Polynomial g)
+ throws RuntimeException
+ {
+ if (isZero() && g.isZero())
+ {
+ throw new ArithmeticException("Both operands of gcd equal zero.");
+ }
+ if (isZero())
+ {
+ return new GF2Polynomial(g);
+ }
+ if (g.isZero())
+ {
+ return new GF2Polynomial(this);
+ }
+ GF2Polynomial a = new GF2Polynomial(this);
+ GF2Polynomial b = new GF2Polynomial(g);
+ GF2Polynomial c;
+
+ while (!b.isZero())
+ {
+ c = a.remainder(b);
+ a = b;
+ b = c;
+ }
+
+ return a;
+ }
+
+ /**
+ * Checks if <i>this</i> is irreducible, according to IEEE P1363, A.5.5,
+ * p103. <br />
+ * Note: The algorithm from IEEE P1363, A5.5 can be used to check a
+ * polynomial with coefficients in GF(2^r) for irreducibility. As this class
+ * only represents polynomials with coefficients in GF(2), the algorithm is
+ * adapted to the case r=1.
+ *
+ * @return true if <i>this</i> is irreducible
+ * @see "P1363, A.5.5, p103"
+ */
+ public boolean isIrreducible()
+ {
+ if (isZero())
+ {
+ return false;
+ }
+ GF2Polynomial f = new GF2Polynomial(this);
+ int d, i;
+ GF2Polynomial u, g;
+ GF2Polynomial dummy;
+ f.reduceN();
+ d = f.len - 1;
+ u = new GF2Polynomial(f.len, "X");
+
+ for (i = 1; i <= (d >> 1); i++)
+ {
+ u.squareThisPreCalc();
+ u = u.remainder(f);
+ dummy = u.add(new GF2Polynomial(32, "X"));
+ if (!dummy.isZero())
+ {
+ g = f.gcd(dummy);
+ if (!g.isOne())
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Reduces this GF2Polynomial using the trinomial x^<i>m</i> + x^<i>tc</i> +
+ * 1.
+ *
+ * @param m the degree of the used field
+ * @param tc degree of the middle x in the trinomial
+ */
+ void reduceTrinomial(int m, int tc)
+ {
+ int i;
+ int p0, p1;
+ int q0, q1;
+ long t;
+ p0 = m >>> 5; // block which contains 2^m
+ q0 = 32 - (m & 0x1f); // (32-index) of 2^m within block p0
+ p1 = (m - tc) >>> 5; // block which contains 2^tc
+ q1 = 32 - ((m - tc) & 0x1f); // (32-index) of 2^tc within block q1
+ int max = ((m << 1) - 2) >>> 5; // block which contains 2^(2m-2)
+ int min = p0; // block which contains 2^m
+ for (i = max; i > min; i--)
+ { // for i = maxBlock to minBlock
+ // reduce coefficients contained in t
+ // t = block[i]
+ t = value[i] & 0x00000000ffffffffL;
+ // block[i-p0-1] ^= t << q0
+ value[i - p0 - 1] ^= (int)(t << q0);
+ // block[i-p0] ^= t >>> (32-q0)
+ value[i - p0] ^= t >>> (32 - q0);
+ // block[i-p1-1] ^= << q1
+ value[i - p1 - 1] ^= (int)(t << q1);
+ // block[i-p1] ^= t >>> (32-q1)
+ value[i - p1] ^= t >>> (32 - q1);
+ value[i] = 0x00;
+ }
+ // reduce last coefficients in block containing 2^m
+ t = value[min] & 0x00000000ffffffffL & (0xffffffffL << (m & 0x1f)); // t
+ // contains the last coefficients > m
+ value[0] ^= t >>> (32 - q0);
+ if (min - p1 - 1 >= 0)
+ {
+ value[min - p1 - 1] ^= (int)(t << q1);
+ }
+ value[min - p1] ^= t >>> (32 - q1);
+
+ value[min] &= reverseRightMask[m & 0x1f];
+ blocks = ((m - 1) >>> 5) + 1;
+ len = m;
+ }
+
+ /**
+ * Reduces this GF2Polynomial using the pentanomial x^<i>m</i> + x^<i>pc[2]</i> +
+ * x^<i>pc[1]</i> + x^<i>pc[0]</i> + 1.
+ *
+ * @param m the degree of the used field
+ * @param pc degrees of the middle x's in the pentanomial
+ */
+ void reducePentanomial(int m, int[] pc)
+ {
+ int i;
+ int p0, p1, p2, p3;
+ int q0, q1, q2, q3;
+ long t;
+ p0 = m >>> 5;
+ q0 = 32 - (m & 0x1f);
+ p1 = (m - pc[0]) >>> 5;
+ q1 = 32 - ((m - pc[0]) & 0x1f);
+ p2 = (m - pc[1]) >>> 5;
+ q2 = 32 - ((m - pc[1]) & 0x1f);
+ p3 = (m - pc[2]) >>> 5;
+ q3 = 32 - ((m - pc[2]) & 0x1f);
+ int max = ((m << 1) - 2) >>> 5;
+ int min = p0;
+ for (i = max; i > min; i--)
+ {
+ t = value[i] & 0x00000000ffffffffL;
+ value[i - p0 - 1] ^= (int)(t << q0);
+ value[i - p0] ^= t >>> (32 - q0);
+ value[i - p1 - 1] ^= (int)(t << q1);
+ value[i - p1] ^= t >>> (32 - q1);
+ value[i - p2 - 1] ^= (int)(t << q2);
+ value[i - p2] ^= t >>> (32 - q2);
+ value[i - p3 - 1] ^= (int)(t << q3);
+ value[i - p3] ^= t >>> (32 - q3);
+ value[i] = 0;
+ }
+ t = value[min] & 0x00000000ffffffffL & (0xffffffffL << (m & 0x1f));
+ value[0] ^= t >>> (32 - q0);
+ if (min - p1 - 1 >= 0)
+ {
+ value[min - p1 - 1] ^= (int)(t << q1);
+ }
+ value[min - p1] ^= t >>> (32 - q1);
+ if (min - p2 - 1 >= 0)
+ {
+ value[min - p2 - 1] ^= (int)(t << q2);
+ }
+ value[min - p2] ^= t >>> (32 - q2);
+ if (min - p3 - 1 >= 0)
+ {
+ value[min - p3 - 1] ^= (int)(t << q3);
+ }
+ value[min - p3] ^= t >>> (32 - q3);
+ value[min] &= reverseRightMask[m & 0x1f];
+
+ blocks = ((m - 1) >>> 5) + 1;
+ len = m;
+ }
+
+ /**
+ * Reduces len by finding the most significant bit set to one and reducing
+ * len and blocks.
+ */
+ public void reduceN()
+ {
+ int i, j, h;
+ i = blocks - 1;
+ while ((value[i] == 0) && (i > 0))
+ {
+ i--;
+ }
+ h = value[i];
+ j = 0;
+ while (h != 0)
+ {
+ h >>>= 1;
+ j++;
+ }
+ len = (i << 5) + j;
+ blocks = i + 1;
+ }
+
+ /**
+ * Expands len and int[] value to <i>i</i>. This is useful before adding
+ * two GF2Polynomials of different size.
+ *
+ * @param i the intended length
+ */
+ public void expandN(int i)
+ {
+ int k;
+ int[] bs;
+ if (len >= i)
+ {
+ return;
+ }
+ len = i;
+ k = ((i - 1) >>> 5) + 1;
+ if (blocks >= k)
+ {
+ return;
+ }
+ if (value.length >= k)
+ {
+ int j;
+ for (j = blocks; j < k; j++)
+ {
+ value[j] = 0;
+ }
+ blocks = k;
+ return;
+ }
+ bs = new int[k];
+ System.arraycopy(value, 0, bs, 0, blocks);
+ blocks = k;
+ value = null;
+ value = bs;
+ }
+
+ /**
+ * Squares this GF2Polynomial and expands it accordingly. This method does
+ * not reduce the result in GF(2^N). There exists a faster method for
+ * squaring in GF(2^N).
+ *
+ * @see GF2nPolynomialElement#square
+ */
+ public void squareThisBitwise()
+ {
+ int i, h, j, k;
+ if (isZero())
+ {
+ return;
+ }
+ int[] result = new int[blocks << 1];
+ for (i = blocks - 1; i >= 0; i--)
+ {
+ h = value[i];
+ j = 0x00000001;
+ for (k = 0; k < 16; k++)
+ {
+ if ((h & 0x01) != 0)
+ {
+ result[i << 1] |= j;
+ }
+ if ((h & 0x00010000) != 0)
+ {
+ result[(i << 1) + 1] |= j;
+ }
+ j <<= 2;
+ h >>>= 1;
+ }
+ }
+ value = null;
+ value = result;
+ blocks = result.length;
+ len = (len << 1) - 1;
+ }
+
+ /**
+ * Squares this GF2Polynomial by using precomputed values of squaringTable.
+ * This method does not reduce the result in GF(2^N).
+ */
+ public void squareThisPreCalc()
+ {
+ int i;
+ if (isZero())
+ {
+ return;
+ }
+ if (value.length >= (blocks << 1))
+ {
+ for (i = blocks - 1; i >= 0; i--)
+ {
+ value[(i << 1) + 1] = GF2Polynomial.squaringTable[(value[i] & 0x00ff0000) >>> 16]
+ | (GF2Polynomial.squaringTable[(value[i] & 0xff000000) >>> 24] << 16);
+ value[i << 1] = GF2Polynomial.squaringTable[value[i] & 0x000000ff]
+ | (GF2Polynomial.squaringTable[(value[i] & 0x0000ff00) >>> 8] << 16);
+ }
+ blocks <<= 1;
+ len = (len << 1) - 1;
+ }
+ else
+ {
+ int[] result = new int[blocks << 1];
+ for (i = 0; i < blocks; i++)
+ {
+ result[i << 1] = GF2Polynomial.squaringTable[value[i] & 0x000000ff]
+ | (GF2Polynomial.squaringTable[(value[i] & 0x0000ff00) >>> 8] << 16);
+ result[(i << 1) + 1] = GF2Polynomial.squaringTable[(value[i] & 0x00ff0000) >>> 16]
+ | (GF2Polynomial.squaringTable[(value[i] & 0xff000000) >>> 24] << 16);
+ }
+ value = null;
+ value = result;
+ blocks <<= 1;
+ len = (len << 1) - 1;
+ }
+ }
+
+ /**
+ * Does a vector-multiplication modulo 2 and returns the result as boolean.
+ *
+ * @param b GF2Polynomial
+ * @return this x <i>b</i> as boolean (1->true, 0->false)
+ * @throws PolynomialsHaveDifferentLengthException if <i>this</i> and <i>b</i> have a different length and
+ * thus cannot be vector-multiplied
+ */
+ public boolean vectorMult(GF2Polynomial b)
+ throws RuntimeException
+ {
+ int i;
+ int h;
+ boolean result = false;
+ if (len != b.len)
+ {
+ throw new RuntimeException();
+ }
+ for (i = 0; i < blocks; i++)
+ {
+ h = value[i] & b.value[i];
+ result ^= parity[h & 0x000000ff];
+ result ^= parity[(h >>> 8) & 0x000000ff];
+ result ^= parity[(h >>> 16) & 0x000000ff];
+ result ^= parity[(h >>> 24) & 0x000000ff];
+ }
+ return result;
+ }
+
+ /**
+ * Returns the bitwise exclusive-or of <i>this</i> and <i>b</i> in a new
+ * GF2Polynomial. <i>this</i> and <i>b</i> can be of different size.
+ *
+ * @param b GF2Polynomial
+ * @return a new GF2Polynomial (<i>this</i> ^ <i>b</i>)
+ */
+ public GF2Polynomial xor(GF2Polynomial b)
+ {
+ int i;
+ GF2Polynomial result;
+ int k = Math.min(blocks, b.blocks);
+ if (len >= b.len)
+ {
+ result = new GF2Polynomial(this);
+ for (i = 0; i < k; i++)
+ {
+ result.value[i] ^= b.value[i];
+ }
+ }
+ else
+ {
+ result = new GF2Polynomial(b);
+ for (i = 0; i < k; i++)
+ {
+ result.value[i] ^= value[i];
+ }
+ }
+ // If we xor'ed some bits too many by proceeding blockwise,
+ // restore them to zero:
+ result.zeroUnusedBits();
+ return result;
+ }
+
+ /**
+ * Computes the bitwise exclusive-or of this GF2Polynomial and <i>b</i> and
+ * stores the result in this GF2Polynomial. <i>b</i> can be of different
+ * size.
+ *
+ * @param b GF2Polynomial
+ */
+ public void xorThisBy(GF2Polynomial b)
+ {
+ int i;
+ for (i = 0; i < Math.min(blocks, b.blocks); i++)
+ {
+ value[i] ^= b.value[i];
+ }
+ // If we xor'ed some bits too many by proceeding blockwise,
+ // restore them to zero:
+ zeroUnusedBits();
+ }
+
+ /**
+ * If {@link #len} is not a multiple of the block size (32), some extra bits
+ * of the last block might have been modified during a blockwise operation.
+ * This method compensates for that by restoring these "extra" bits to zero.
+ */
+ private void zeroUnusedBits()
+ {
+ if ((len & 0x1f) != 0)
+ {
+ value[blocks - 1] &= reverseRightMask[len & 0x1f];
+ }
+ }
+
+ /**
+ * Sets the bit at position <i>i</i>.
+ *
+ * @param i int
+ * @throws BitDoesNotExistException if (<i>i</i> < 0) || (<i>i</i> > (len - 1))
+ */
+ public void setBit(int i)
+ throws RuntimeException
+ {
+ if (i < 0 || i > (len - 1))
+ {
+ throw new RuntimeException();
+ }
+ if (i > (len - 1))
+ {
+ return;
+ }
+ value[i >>> 5] |= bitMask[i & 0x1f];
+ return;
+ }
+
+ /**
+ * Returns the bit at position <i>i</i>.
+ *
+ * @param i int
+ * @return the bit at position <i>i</i> if <i>i</i> is a valid position, 0
+ * otherwise.
+ */
+ public int getBit(int i)
+ {
+ if (i < 0 || i > (len - 1))
+ {
+ return 0;
+ }
+ return ((value[i >>> 5] & bitMask[i & 0x1f]) != 0) ? 1 : 0;
+ }
+
+ /**
+ * Resets the bit at position <i>i</i>.
+ *
+ * @param i int
+ * @throws BitDoesNotExistException if (<i>i</i> < 0) || (<i>i</i> > (len - 1))
+ */
+ public void resetBit(int i)
+ throws RuntimeException
+ {
+ if (i < 0 || i > (len - 1))
+ {
+ throw new RuntimeException();
+ }
+ if (i > (len - 1))
+ {
+ return;
+ }
+ value[i >>> 5] &= ~bitMask[i & 0x1f];
+ }
+
+ /**
+ * Xors the bit at position <i>i</i>.
+ *
+ * @param i int
+ * @throws BitDoesNotExistException if (<i>i</i> < 0) || (<i>i</i> > (len - 1))
+ */
+ public void xorBit(int i)
+ throws RuntimeException
+ {
+ if (i < 0 || i > (len - 1))
+ {
+ throw new RuntimeException();
+ }
+ if (i > (len - 1))
+ {
+ return;
+ }
+ value[i >>> 5] ^= bitMask[i & 0x1f];
+ }
+
+ /**
+ * Tests the bit at position <i>i</i>.
+ *
+ * @param i the position of the bit to be tested
+ * @return true if the bit at position <i>i</i> is set (a(<i>i</i>) ==
+ * 1). False if (<i>i</i> < 0) || (<i>i</i> > (len - 1))
+ */
+ public boolean testBit(int i)
+ {
+ if (i < 0 || i > (len - 1))
+ {
+ return false;
+ }
+ return (value[i >>> 5] & bitMask[i & 0x1f]) != 0;
+ }
+
+ /**
+ * Returns this GF2Polynomial shift-left by 1 in a new GF2Polynomial.
+ *
+ * @return a new GF2Polynomial (this << 1)
+ */
+ public GF2Polynomial shiftLeft()
+ {
+ GF2Polynomial result = new GF2Polynomial(len + 1, value);
+ int i;
+ for (i = result.blocks - 1; i >= 1; i--)
+ {
+ result.value[i] <<= 1;
+ result.value[i] |= result.value[i - 1] >>> 31;
+ }
+ result.value[0] <<= 1;
+ return result;
+ }
+
+ /**
+ * Shifts-left this by one and enlarges the size of value if necesary.
+ */
+ public void shiftLeftThis()
+ {
+ /** @todo This is untested. */
+ int i;
+ if ((len & 0x1f) == 0)
+ { // check if blocks increases
+ len += 1;
+ blocks += 1;
+ if (blocks > value.length)
+ { // enlarge value
+ int[] bs = new int[blocks];
+ System.arraycopy(value, 0, bs, 0, value.length);
+ value = null;
+ value = bs;
+ }
+ for (i = blocks - 1; i >= 1; i--)
+ {
+ value[i] |= value[i - 1] >>> 31;
+ value[i - 1] <<= 1;
+ }
+ }
+ else
+ {
+ len += 1;
+ for (i = blocks - 1; i >= 1; i--)
+ {
+ value[i] <<= 1;
+ value[i] |= value[i - 1] >>> 31;
+ }
+ value[0] <<= 1;
+ }
+ }
+
+ /**
+ * Returns this GF2Polynomial shift-left by <i>k</i> in a new
+ * GF2Polynomial.
+ *
+ * @param k int
+ * @return a new GF2Polynomial (this << <i>k</i>)
+ */
+ public GF2Polynomial shiftLeft(int k)
+ {
+ // Variant 2, requiring a modified shiftBlocksLeft(k)
+ // In case of modification, consider a rename to doShiftBlocksLeft()
+ // with an explicit note that this method assumes that the polynomial
+ // has already been resized. Or consider doing things inline.
+ // Construct the resulting polynomial of appropriate length:
+ GF2Polynomial result = new GF2Polynomial(len + k, value);
+ // Shift left as many multiples of the block size as possible:
+ if (k >= 32)
+ {
+ result.doShiftBlocksLeft(k >>> 5);
+ }
+ // Shift left by the remaining (<32) amount:
+ final int remaining = k & 0x1f;
+ if (remaining != 0)
+ {
+ for (int i = result.blocks - 1; i >= 1; i--)
+ {
+ result.value[i] <<= remaining;
+ result.value[i] |= result.value[i - 1] >>> (32 - remaining);
+ }
+ result.value[0] <<= remaining;
+ }
+ return result;
+ }
+
+ /**
+ * Shifts left b and adds the result to Its a fast version of
+ * <tt>this = add(b.shl(k));</tt>
+ *
+ * @param b GF2Polynomial to shift and add to this
+ * @param k the amount to shift
+ * @see GF2nPolynomialElement#invertEEA
+ */
+ public void shiftLeftAddThis(GF2Polynomial b, int k)
+ {
+ if (k == 0)
+ {
+ addToThis(b);
+ return;
+ }
+ int i;
+ expandN(b.len + k);
+ int d = k >>> 5;
+ for (i = b.blocks - 1; i >= 0; i--)
+ {
+ if ((i + d + 1 < blocks) && ((k & 0x1f) != 0))
+ {
+ value[i + d + 1] ^= b.value[i] >>> (32 - (k & 0x1f));
+ }
+ value[i + d] ^= b.value[i] << (k & 0x1f);
+ }
+ }
+
+ /**
+ * Shifts-left this GF2Polynomial's value blockwise 1 block resulting in a
+ * shift-left by 32.
+ *
+ * @see GF2Polynomial#multiply
+ */
+ void shiftBlocksLeft()
+ {
+ blocks += 1;
+ len += 32;
+ if (blocks <= value.length)
+ {
+ int i;
+ for (i = blocks - 1; i >= 1; i--)
+ {
+ value[i] = value[i - 1];
+ }
+ value[0] = 0x00;
+ }
+ else
+ {
+ int[] result = new int[blocks];
+ System.arraycopy(value, 0, result, 1, blocks - 1);
+ value = null;
+ value = result;
+ }
+ }
+
+ /**
+ * Shifts left this GF2Polynomial's value blockwise <i>b</i> blocks
+ * resulting in a shift-left by b*32. This method assumes that {@link #len}
+ * and {@link #blocks} have already been updated to reflect the final state.
+ *
+ * @param b shift amount (in blocks)
+ */
+ private void doShiftBlocksLeft(int b)
+ {
+ if (blocks <= value.length)
+ {
+ int i;
+ for (i = blocks - 1; i >= b; i--)
+ {
+ value[i] = value[i - b];
+ }
+ for (i = 0; i < b; i++)
+ {
+ value[i] = 0x00;
+ }
+ }
+ else
+ {
+ int[] result = new int[blocks];
+ System.arraycopy(value, 0, result, b, blocks - b);
+ value = null;
+ value = result;
+ }
+ }
+
+ /**
+ * Returns this GF2Polynomial shift-right by 1 in a new GF2Polynomial.
+ *
+ * @return a new GF2Polynomial (this << 1)
+ */
+ public GF2Polynomial shiftRight()
+ {
+ GF2Polynomial result = new GF2Polynomial(len - 1);
+ int i;
+ System.arraycopy(value, 0, result.value, 0, result.blocks);
+ for (i = 0; i <= result.blocks - 2; i++)
+ {
+ result.value[i] >>>= 1;
+ result.value[i] |= result.value[i + 1] << 31;
+ }
+ result.value[result.blocks - 1] >>>= 1;
+ if (result.blocks < blocks)
+ {
+ result.value[result.blocks - 1] |= value[result.blocks] << 31;
+ }
+ return result;
+ }
+
+ /**
+ * Shifts-right this GF2Polynomial by 1.
+ */
+ public void shiftRightThis()
+ {
+ int i;
+ len -= 1;
+ blocks = ((len - 1) >>> 5) + 1;
+ for (i = 0; i <= blocks - 2; i++)
+ {
+ value[i] >>>= 1;
+ value[i] |= value[i + 1] << 31;
+ }
+ value[blocks - 1] >>>= 1;
+ if ((len & 0x1f) == 0)
+ {
+ value[blocks - 1] |= value[blocks] << 31;
+ }
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Vector.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Vector.java
new file mode 100644
index 00000000..ec35b682
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Vector.java
@@ -0,0 +1,539 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+import java.security.SecureRandom;
+
+/**
+ * This class implements the abstract class <tt>Vector</tt> for the case of
+ * vectors over the finite field GF(2). <br>
+ * For the vector representation the array of type int[] is used, thus one
+ * element of the array holds 32 elements of the vector.
+ *
+ * @see Vector
+ */
+public class GF2Vector
+ extends Vector
+{
+
+ /**
+ * holds the elements of this vector
+ */
+ private int[] v;
+
+ /**
+ * Construct the zero vector of the given length.
+ *
+ * @param length the length of the vector
+ */
+ public GF2Vector(int length)
+ {
+ if (length < 0)
+ {
+ throw new ArithmeticException("Negative length.");
+ }
+ this.length = length;
+ v = new int[(length + 31) >> 5];
+ }
+
+ /**
+ * Construct a random GF2Vector of the given length.
+ *
+ * @param length the length of the vector
+ * @param sr the source of randomness
+ */
+ public GF2Vector(int length, SecureRandom sr)
+ {
+ this.length = length;
+
+ int size = (length + 31) >> 5;
+ v = new int[size];
+
+ // generate random elements
+ for (int i = size - 1; i >= 0; i--)
+ {
+ v[i] = sr.nextInt();
+ }
+
+ // erase unused bits
+ int r = length & 0x1f;
+ if (r != 0)
+ {
+ // erase unused bits
+ v[size - 1] &= (1 << r) - 1;
+ }
+ }
+
+ /**
+ * Construct a random GF2Vector of the given length with the specified
+ * number of non-zero coefficients.
+ *
+ * @param length the length of the vector
+ * @param t the number of non-zero coefficients
+ * @param sr the source of randomness
+ */
+ public GF2Vector(int length, int t, SecureRandom sr)
+ {
+ if (t > length)
+ {
+ throw new ArithmeticException(
+ "The hamming weight is greater than the length of vector.");
+ }
+ this.length = length;
+
+ int size = (length + 31) >> 5;
+ v = new int[size];
+
+ int[] help = new int[length];
+ for (int i = 0; i < length; i++)
+ {
+ help[i] = i;
+ }
+
+ int m = length;
+ for (int i = 0; i < t; i++)
+ {
+ int j = RandUtils.nextInt(sr, m);
+ setBit(help[j]);
+ m--;
+ help[j] = help[m];
+ }
+ }
+
+ /**
+ * Construct a GF2Vector of the given length and with elements from the
+ * given array. The array is copied and unused bits are masked out.
+ *
+ * @param length the length of the vector
+ * @param v the element array
+ */
+ public GF2Vector(int length, int[] v)
+ {
+ if (length < 0)
+ {
+ throw new ArithmeticException("negative length");
+ }
+ this.length = length;
+
+ int size = (length + 31) >> 5;
+
+ if (v.length != size)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ this.v = IntUtils.clone(v);
+
+ int r = length & 0x1f;
+ if (r != 0)
+ {
+ // erase unused bits
+ this.v[size - 1] &= (1 << r) - 1;
+ }
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other another {@link GF2Vector}
+ */
+ public GF2Vector(GF2Vector other)
+ {
+ this.length = other.length;
+ this.v = IntUtils.clone(other.v);
+ }
+
+ /**
+ * Construct a new {@link GF2Vector} of the given length and with the given
+ * element array. The array is not changed and only a reference to the array
+ * is stored. No length checking is performed either.
+ *
+ * @param v the element array
+ * @param length the length of the vector
+ */
+ protected GF2Vector(int[] v, int length)
+ {
+ this.v = v;
+ this.length = length;
+ }
+
+ /**
+ * Construct a new GF2Vector with the given length out of the encoded
+ * vector.
+ *
+ * @param length the length of the vector
+ * @param encVec the encoded vector
+ * @return the decoded vector
+ */
+ public static GF2Vector OS2VP(int length, byte[] encVec)
+ {
+ if (length < 0)
+ {
+ throw new ArithmeticException("negative length");
+ }
+
+ int byteLen = (length + 7) >> 3;
+
+ if (encVec.length > byteLen)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ return new GF2Vector(length, LittleEndianConversions.toIntArray(encVec));
+ }
+
+ /**
+ * Encode this vector as byte array.
+ *
+ * @return the encoded vector
+ */
+ public byte[] getEncoded()
+ {
+ int byteLen = (length + 7) >> 3;
+ return LittleEndianConversions.toByteArray(v, byteLen);
+ }
+
+ /**
+ * @return the int array representation of this vector
+ */
+ public int[] getVecArray()
+ {
+ return v;
+ }
+
+ /**
+ * Return the Hamming weight of this vector, i.e., compute the number of
+ * units of this vector.
+ *
+ * @return the Hamming weight of this vector
+ */
+ public int getHammingWeight()
+ {
+ int weight = 0;
+ for (int i = 0; i < v.length; i++)
+ {
+ int e = v[i];
+ for (int j = 0; j < 32; j++)
+ {
+ int b = e & 1;
+ if (b != 0)
+ {
+ weight++;
+ }
+ e >>>= 1;
+ }
+ }
+ return weight;
+ }
+
+ /**
+ * @return whether this is the zero vector (i.e., all elements are zero)
+ */
+ public boolean isZero()
+ {
+ for (int i = v.length - 1; i >= 0; i--)
+ {
+ if (v[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return the value of the bit of this vector at the specified index.
+ *
+ * @param index the index
+ * @return the value of the bit (0 or 1)
+ */
+ public int getBit(int index)
+ {
+ if (index >= length)
+ {
+ throw new IndexOutOfBoundsException();
+ }
+ int q = index >> 5;
+ int r = index & 0x1f;
+ return (v[q] & (1 << r)) >>> r;
+ }
+
+ /**
+ * Set the coefficient at the given index to 1. If the index is out of
+ * bounds, do nothing.
+ *
+ * @param index the index of the coefficient to set
+ */
+ public void setBit(int index)
+ {
+ if (index >= length)
+ {
+ throw new IndexOutOfBoundsException();
+ }
+ v[index >> 5] |= 1 << (index & 0x1f);
+ }
+
+ /**
+ * Adds another GF2Vector to this vector.
+ *
+ * @param other another GF2Vector
+ * @return <tt>this + other</tt>
+ * @throws ArithmeticException if the other vector is not a GF2Vector or has another
+ * length.
+ */
+ public Vector add(Vector other)
+ {
+ if (!(other instanceof GF2Vector))
+ {
+ throw new ArithmeticException("vector is not defined over GF(2)");
+ }
+
+ GF2Vector otherVec = (GF2Vector)other;
+ if (length != otherVec.length)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ int[] vec = IntUtils.clone(((GF2Vector)other).v);
+
+ for (int i = vec.length - 1; i >= 0; i--)
+ {
+ vec[i] ^= v[i];
+ }
+
+ return new GF2Vector(length, vec);
+ }
+
+ /**
+ * Multiply this vector with a permutation.
+ *
+ * @param p the permutation
+ * @return <tt>this*p = p*this</tt>
+ */
+ public Vector multiply(Permutation p)
+ {
+ int[] pVec = p.getVector();
+ if (length != pVec.length)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ GF2Vector result = new GF2Vector(length);
+
+ for (int i = 0; i < pVec.length; i++)
+ {
+ int e = v[pVec[i] >> 5] & (1 << (pVec[i] & 0x1f));
+ if (e != 0)
+ {
+ result.v[i >> 5] |= 1 << (i & 0x1f);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Return a new vector consisting of the elements of this vector with the
+ * indices given by the set <tt>setJ</tt>.
+ *
+ * @param setJ the set of indices of elements to extract
+ * @return the new {@link GF2Vector}
+ * <tt>[this_setJ[0], this_setJ[1], ..., this_setJ[#setJ-1]]</tt>
+ */
+ public GF2Vector extractVector(int[] setJ)
+ {
+ int k = setJ.length;
+ if (setJ[k - 1] > length)
+ {
+ throw new ArithmeticException("invalid index set");
+ }
+
+ GF2Vector result = new GF2Vector(k);
+
+ for (int i = 0; i < k; i++)
+ {
+ int e = v[setJ[i] >> 5] & (1 << (setJ[i] & 0x1f));
+ if (e != 0)
+ {
+ result.v[i >> 5] |= 1 << (i & 0x1f);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Return a new vector consisting of the first <tt>k</tt> elements of this
+ * vector.
+ *
+ * @param k the number of elements to extract
+ * @return a new {@link GF2Vector} consisting of the first <tt>k</tt>
+ * elements of this vector
+ */
+ public GF2Vector extractLeftVector(int k)
+ {
+ if (k > length)
+ {
+ throw new ArithmeticException("invalid length");
+ }
+
+ if (k == length)
+ {
+ return new GF2Vector(this);
+ }
+
+ GF2Vector result = new GF2Vector(k);
+
+ int q = k >> 5;
+ int r = k & 0x1f;
+
+ System.arraycopy(v, 0, result.v, 0, q);
+ if (r != 0)
+ {
+ result.v[q] = v[q] & ((1 << r) - 1);
+ }
+
+ return result;
+ }
+
+ /**
+ * Return a new vector consisting of the last <tt>k</tt> elements of this
+ * vector.
+ *
+ * @param k the number of elements to extract
+ * @return a new {@link GF2Vector} consisting of the last <tt>k</tt>
+ * elements of this vector
+ */
+ public GF2Vector extractRightVector(int k)
+ {
+ if (k > length)
+ {
+ throw new ArithmeticException("invalid length");
+ }
+
+ if (k == length)
+ {
+ return new GF2Vector(this);
+ }
+
+ GF2Vector result = new GF2Vector(k);
+
+ int q = (length - k) >> 5;
+ int r = (length - k) & 0x1f;
+ int length = (k + 31) >> 5;
+
+ int ind = q;
+ // if words have to be shifted
+ if (r != 0)
+ {
+ // process all but last word
+ for (int i = 0; i < length - 1; i++)
+ {
+ result.v[i] = (v[ind++] >>> r) | (v[ind] << (32 - r));
+ }
+ // process last word
+ result.v[length - 1] = v[ind++] >>> r;
+ if (ind < v.length)
+ {
+ result.v[length - 1] |= v[ind] << (32 - r);
+ }
+ }
+ else
+ {
+ // no shift necessary
+ System.arraycopy(v, q, result.v, 0, length);
+ }
+
+ return result;
+ }
+
+ /**
+ * Rewrite this vector as a vector over <tt>GF(2<sup>m</sup>)</tt> with
+ * <tt>t</tt> elements.
+ *
+ * @param field the finite field <tt>GF(2<sup>m</sup>)</tt>
+ * @return the converted vector over <tt>GF(2<sup>m</sup>)</tt>
+ */
+ public GF2mVector toExtensionFieldVector(GF2mField field)
+ {
+ int m = field.getDegree();
+ if ((length % m) != 0)
+ {
+ throw new ArithmeticException("conversion is impossible");
+ }
+
+ int t = length / m;
+ int[] result = new int[t];
+ int count = 0;
+ for (int i = t - 1; i >= 0; i--)
+ {
+ for (int j = field.getDegree() - 1; j >= 0; j--)
+ {
+ int q = count >>> 5;
+ int r = count & 0x1f;
+
+ int e = (v[q] >>> r) & 1;
+ if (e == 1)
+ {
+ result[i] ^= 1 << j;
+ }
+ count++;
+ }
+ }
+ return new GF2mVector(field, result);
+ }
+
+ /**
+ * Check if the given object is equal to this vector.
+ *
+ * @param other vector
+ * @return the result of the comparison
+ */
+ public boolean equals(Object other)
+ {
+
+ if (!(other instanceof GF2Vector))
+ {
+ return false;
+ }
+ GF2Vector otherVec = (GF2Vector)other;
+
+ return (length == otherVec.length) && IntUtils.equals(v, otherVec.v);
+ }
+
+ /**
+ * @return the hash code of this vector
+ */
+ public int hashCode()
+ {
+ int hash = length;
+ hash = hash * 31 + v.hashCode();
+ return hash;
+ }
+
+ /**
+ * @return a human readable form of this vector
+ */
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < length; i++)
+ {
+ if ((i != 0) && ((i & 0x1f) == 0))
+ {
+ buf.append(' ');
+ }
+ int q = i >> 5;
+ int r = i & 0x1f;
+ int bit = v[q] & (1 << r);
+ if (bit == 0)
+ {
+ buf.append('0');
+ }
+ else
+ {
+ buf.append('1');
+ }
+ }
+ return buf.toString();
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mField.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mField.java
new file mode 100644
index 00000000..e74d20b2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mField.java
@@ -0,0 +1,366 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+import java.security.SecureRandom;
+
+/**
+ * This class describes operations with elements from the finite field F =
+ * GF(2^m). ( GF(2^m)= GF(2)[A] where A is a root of irreducible polynomial with
+ * degree m, each field element B has a polynomial basis representation, i.e. it
+ * is represented by a different binary polynomial of degree less than m, B =
+ * poly(A) ) All operations are defined only for field with 1< m <32. For the
+ * representation of field elements the map f: F->Z, poly(A)->poly(2) is used,
+ * where integers have the binary representation. For example: A^7+A^3+A+1 ->
+ * (00...0010001011)=139 Also for elements type Integer is used.
+ *
+ * @see PolynomialRingGF2
+ */
+public class GF2mField
+{
+
+ /*
+ * degree - degree of the field polynomial - the field polynomial ring -
+ * polynomial ring over the finite field GF(2)
+ */
+
+ private int degree = 0;
+
+ private int polynomial;
+
+ /**
+ * create a finite field GF(2^m)
+ *
+ * @param degree the degree of the field
+ */
+ public GF2mField(int degree)
+ {
+ if (degree >= 32)
+ {
+ throw new IllegalArgumentException(
+ " Error: the degree of field is too large ");
+ }
+ if (degree < 1)
+ {
+ throw new IllegalArgumentException(
+ " Error: the degree of field is non-positive ");
+ }
+ this.degree = degree;
+ polynomial = PolynomialRingGF2.getIrreduciblePolynomial(degree);
+ }
+
+ /**
+ * create a finite field GF(2^m) with the fixed field polynomial
+ *
+ * @param degree the degree of the field
+ * @param poly the field polynomial
+ */
+ public GF2mField(int degree, int poly)
+ {
+ if (degree != PolynomialRingGF2.degree(poly))
+ {
+ throw new IllegalArgumentException(
+ " Error: the degree is not correct");
+ }
+ if (!PolynomialRingGF2.isIrreducible(poly))
+ {
+ throw new IllegalArgumentException(
+ " Error: given polynomial is reducible");
+ }
+ this.degree = degree;
+ polynomial = poly;
+
+ }
+
+ public GF2mField(byte[] enc)
+ {
+ if (enc.length != 4)
+ {
+ throw new IllegalArgumentException(
+ "byte array is not an encoded finite field");
+ }
+ polynomial = LittleEndianConversions.OS2IP(enc);
+ if (!PolynomialRingGF2.isIrreducible(polynomial))
+ {
+ throw new IllegalArgumentException(
+ "byte array is not an encoded finite field");
+ }
+
+ degree = PolynomialRingGF2.degree(polynomial);
+ }
+
+ public GF2mField(GF2mField field)
+ {
+ degree = field.degree;
+ polynomial = field.polynomial;
+ }
+
+ /**
+ * return degree of the field
+ *
+ * @return degree of the field
+ */
+ public int getDegree()
+ {
+ return degree;
+ }
+
+ /**
+ * return the field polynomial
+ *
+ * @return the field polynomial
+ */
+ public int getPolynomial()
+ {
+ return polynomial;
+ }
+
+ /**
+ * return the encoded form of this field
+ *
+ * @return the field in byte array form
+ */
+ public byte[] getEncoded()
+ {
+ return LittleEndianConversions.I2OSP(polynomial);
+ }
+
+ /**
+ * Return sum of two elements
+ *
+ * @param a
+ * @param b
+ * @return a+b
+ */
+ public int add(int a, int b)
+ {
+ return a ^ b;
+ }
+
+ /**
+ * Return product of two elements
+ *
+ * @param a
+ * @param b
+ * @return a*b
+ */
+ public int mult(int a, int b)
+ {
+ return PolynomialRingGF2.modMultiply(a, b, polynomial);
+ }
+
+ /**
+ * compute exponentiation a^k
+ *
+ * @param a a field element a
+ * @param k k degree
+ * @return a^k
+ */
+ public int exp(int a, int k)
+ {
+ if (a == 0)
+ {
+ return 0;
+ }
+ if (a == 1)
+ {
+ return 1;
+ }
+ int result = 1;
+ if (k < 0)
+ {
+ a = inverse(a);
+ k = -k;
+ }
+ while (k != 0)
+ {
+ if ((k & 1) == 1)
+ {
+ result = mult(result, a);
+ }
+ a = mult(a, a);
+ k >>>= 1;
+ }
+ return result;
+ }
+
+ /**
+ * compute the multiplicative inverse of a
+ *
+ * @param a a field element a
+ * @return a<sup>-1</sup>
+ */
+ public int inverse(int a)
+ {
+ int d = (1 << degree) - 2;
+
+ return exp(a, d);
+ }
+
+ /**
+ * compute the square root of an integer
+ *
+ * @param a a field element a
+ * @return a<sup>1/2</sup>
+ */
+ public int sqRoot(int a)
+ {
+ for (int i = 1; i < degree; i++)
+ {
+ a = mult(a, a);
+ }
+ return a;
+ }
+
+ /**
+ * create a random field element using PRNG sr
+ *
+ * @param sr SecureRandom
+ * @return a random element
+ */
+ public int getRandomElement(SecureRandom sr)
+ {
+ int result = RandUtils.nextInt(sr, 1 << degree);
+ return result;
+ }
+
+ /**
+ * create a random non-zero field element
+ *
+ * @return a random element
+ */
+ public int getRandomNonZeroElement()
+ {
+ return getRandomNonZeroElement(new SecureRandom());
+ }
+
+ /**
+ * create a random non-zero field element using PRNG sr
+ *
+ * @param sr SecureRandom
+ * @return a random non-zero element
+ */
+ public int getRandomNonZeroElement(SecureRandom sr)
+ {
+ int controltime = 1 << 20;
+ int count = 0;
+ int result = RandUtils.nextInt(sr, 1 << degree);
+ while ((result == 0) && (count < controltime))
+ {
+ result = RandUtils.nextInt(sr, 1 << degree);
+ count++;
+ }
+ if (count == controltime)
+ {
+ result = 1;
+ }
+ return result;
+ }
+
+ /**
+ * @return true if e is encoded element of this field and false otherwise
+ */
+ public boolean isElementOfThisField(int e)
+ {
+ // e is encoded element of this field iff 0<= e < |2^m|
+ if (degree == 31)
+ {
+ return e >= 0;
+ }
+ return e >= 0 && e < (1 << degree);
+ }
+
+ /*
+ * help method for visual control
+ */
+ public String elementToStr(int a)
+ {
+ String s = "";
+ for (int i = 0; i < degree; i++)
+ {
+ if (((byte)a & 0x01) == 0)
+ {
+ s = "0" + s;
+ }
+ else
+ {
+ s = "1" + s;
+ }
+ a >>>= 1;
+ }
+ return s;
+ }
+
+ /**
+ * checks if given object is equal to this field.
+ * <p/>
+ * The method returns false whenever the given object is not GF2m.
+ *
+ * @param other object
+ * @return true or false
+ */
+ public boolean equals(Object other)
+ {
+ if ((other == null) || !(other instanceof GF2mField))
+ {
+ return false;
+ }
+
+ GF2mField otherField = (GF2mField)other;
+
+ if ((degree == otherField.degree)
+ && (polynomial == otherField.polynomial))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ public int hashCode()
+ {
+ return polynomial;
+ }
+
+ /**
+ * Returns a human readable form of this field.
+ * <p/>
+ *
+ * @return a human readable form of this field.
+ */
+ public String toString()
+ {
+ String str = "Finite Field GF(2^" + degree + ") = " + "GF(2)[X]/<"
+ + polyToString(polynomial) + "> ";
+ return str;
+ }
+
+ private static String polyToString(int p)
+ {
+ String str = "";
+ if (p == 0)
+ {
+ str = "0";
+ }
+ else
+ {
+ byte b = (byte)(p & 0x01);
+ if (b == 1)
+ {
+ str = "1";
+ }
+ p >>>= 1;
+ int i = 1;
+ while (p != 0)
+ {
+ b = (byte)(p & 0x01);
+ if (b == 1)
+ {
+ str = str + "+x^" + i;
+ }
+ p >>>= 1;
+ i++;
+ }
+ }
+ return str;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mMatrix.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mMatrix.java
new file mode 100644
index 00000000..5c985a18
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mMatrix.java
@@ -0,0 +1,377 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+/**
+ * This class describes some operations with matrices over finite field <i>GF(2<sup>m</sup>)</i>
+ * with small <i>m</i> (1< m <32).
+ *
+ * @see Matrix
+ */
+public class GF2mMatrix
+ extends Matrix
+{
+
+ /**
+ * finite field GF(2^m)
+ */
+ protected GF2mField field;
+
+ /**
+ * For the matrix representation the array of type int[][] is used, thus
+ * every element of the array keeps one element of the matrix (element from
+ * finite field GF(2^m))
+ */
+ protected int[][] matrix;
+
+ /**
+ * Constructor.
+ *
+ * @param field a finite field GF(2^m)
+ * @param enc byte[] matrix in byte array form
+ */
+ public GF2mMatrix(GF2mField field, byte[] enc)
+ {
+
+ this.field = field;
+
+ // decode matrix
+ int d = 8;
+ int count = 1;
+ while (field.getDegree() > d)
+ {
+ count++;
+ d += 8;
+ }
+
+ if (enc.length < 5)
+ {
+ throw new IllegalArgumentException(
+ " Error: given array is not encoded matrix over GF(2^m)");
+ }
+
+ this.numRows = ((enc[3] & 0xff) << 24) ^ ((enc[2] & 0xff) << 16)
+ ^ ((enc[1] & 0xff) << 8) ^ (enc[0] & 0xff);
+
+ int n = count * this.numRows;
+
+ if ((this.numRows <= 0) || (((enc.length - 4) % n) != 0))
+ {
+ throw new IllegalArgumentException(
+ " Error: given array is not encoded matrix over GF(2^m)");
+ }
+
+ this.numColumns = (enc.length - 4) / n;
+
+ matrix = new int[this.numRows][this.numColumns];
+ count = 4;
+ for (int i = 0; i < this.numRows; i++)
+ {
+ for (int j = 0; j < this.numColumns; j++)
+ {
+ for (int jj = 0; jj < d; jj += 8)
+ {
+ matrix[i][j] ^= (enc[count++] & 0x000000ff) << jj;
+ }
+ if (!this.field.isElementOfThisField(matrix[i][j]))
+ {
+ throw new IllegalArgumentException(
+ " Error: given array is not encoded matrix over GF(2^m)");
+ }
+ }
+ }
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other another {@link GF2mMatrix}
+ */
+ public GF2mMatrix(GF2mMatrix other)
+ {
+ numRows = other.numRows;
+ numColumns = other.numColumns;
+ field = other.field;
+ matrix = new int[numRows][];
+ for (int i = 0; i < numRows; i++)
+ {
+ matrix[i] = IntUtils.clone(other.matrix[i]);
+ }
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param field a finite field GF(2^m)
+ * @param matrix the matrix as int array. Only the reference is copied.
+ */
+ protected GF2mMatrix(GF2mField field, int[][] matrix)
+ {
+ this.field = field;
+ this.matrix = matrix;
+ numRows = matrix.length;
+ numColumns = matrix[0].length;
+ }
+
+ /**
+ * @return a byte array encoding of this matrix
+ */
+ public byte[] getEncoded()
+ {
+ int d = 8;
+ int count = 1;
+ while (field.getDegree() > d)
+ {
+ count++;
+ d += 8;
+ }
+
+ byte[] bf = new byte[this.numRows * this.numColumns * count + 4];
+ bf[0] = (byte)(this.numRows & 0xff);
+ bf[1] = (byte)((this.numRows >>> 8) & 0xff);
+ bf[2] = (byte)((this.numRows >>> 16) & 0xff);
+ bf[3] = (byte)((this.numRows >>> 24) & 0xff);
+
+ count = 4;
+ for (int i = 0; i < this.numRows; i++)
+ {
+ for (int j = 0; j < this.numColumns; j++)
+ {
+ for (int jj = 0; jj < d; jj += 8)
+ {
+ bf[count++] = (byte)(matrix[i][j] >>> jj);
+ }
+ }
+ }
+
+ return bf;
+ }
+
+ /**
+ * Check if this is the zero matrix (i.e., all entries are zero).
+ *
+ * @return <tt>true</tt> if this is the zero matrix
+ */
+ public boolean isZero()
+ {
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < numColumns; j++)
+ {
+ if (matrix[i][j] != 0)
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Compute the inverse of this matrix.
+ *
+ * @return the inverse of this matrix (newly created).
+ */
+ public Matrix computeInverse()
+ {
+ if (numRows != numColumns)
+ {
+ throw new ArithmeticException("Matrix is not invertible.");
+ }
+
+ // clone this matrix
+ int[][] tmpMatrix = new int[numRows][numRows];
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ tmpMatrix[i] = IntUtils.clone(matrix[i]);
+ }
+
+ // initialize inverse matrix as unit matrix
+ int[][] invMatrix = new int[numRows][numRows];
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ invMatrix[i][i] = 1;
+ }
+
+ // simultaneously compute Gaussian reduction of tmpMatrix and unit
+ // matrix
+ for (int i = 0; i < numRows; i++)
+ {
+ // if diagonal element is zero
+ if (tmpMatrix[i][i] == 0)
+ {
+ boolean foundNonZero = false;
+ // find a non-zero element in the same column
+ for (int j = i + 1; j < numRows; j++)
+ {
+ if (tmpMatrix[j][i] != 0)
+ {
+ // found it, swap rows ...
+ foundNonZero = true;
+ swapColumns(tmpMatrix, i, j);
+ swapColumns(invMatrix, i, j);
+ // ... and quit searching
+ j = numRows;
+ continue;
+ }
+ }
+ // if no non-zero element was found
+ if (!foundNonZero)
+ {
+ // the matrix is not invertible
+ throw new ArithmeticException("Matrix is not invertible.");
+ }
+ }
+
+ // normalize i-th row
+ int coef = tmpMatrix[i][i];
+ int invCoef = field.inverse(coef);
+ multRowWithElementThis(tmpMatrix[i], invCoef);
+ multRowWithElementThis(invMatrix[i], invCoef);
+
+ // normalize all other rows
+ for (int j = 0; j < numRows; j++)
+ {
+ if (j != i)
+ {
+ coef = tmpMatrix[j][i];
+ if (coef != 0)
+ {
+ int[] tmpRow = multRowWithElement(tmpMatrix[i], coef);
+ int[] tmpInvRow = multRowWithElement(invMatrix[i], coef);
+ addToRow(tmpRow, tmpMatrix[j]);
+ addToRow(tmpInvRow, invMatrix[j]);
+ }
+ }
+ }
+ }
+
+ return new GF2mMatrix(field, invMatrix);
+ }
+
+ private static void swapColumns(int[][] matrix, int first, int second)
+ {
+ int[] tmp = matrix[first];
+ matrix[first] = matrix[second];
+ matrix[second] = tmp;
+ }
+
+ private void multRowWithElementThis(int[] row, int element)
+ {
+ for (int i = row.length - 1; i >= 0; i--)
+ {
+ row[i] = field.mult(row[i], element);
+ }
+ }
+
+ private int[] multRowWithElement(int[] row, int element)
+ {
+ int[] result = new int[row.length];
+ for (int i = row.length - 1; i >= 0; i--)
+ {
+ result[i] = field.mult(row[i], element);
+ }
+ return result;
+ }
+
+ /**
+ * Add one row to another.
+ *
+ * @param fromRow the addend
+ * @param toRow the row to add to
+ */
+ private void addToRow(int[] fromRow, int[] toRow)
+ {
+ for (int i = toRow.length - 1; i >= 0; i--)
+ {
+ toRow[i] = field.add(fromRow[i], toRow[i]);
+ }
+ }
+
+ public Matrix rightMultiply(Matrix a)
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ public Matrix rightMultiply(Permutation perm)
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ public Vector leftMultiply(Vector vector)
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ public Vector rightMultiply(Vector vector)
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ /**
+ * Checks if given object is equal to this matrix. The method returns false
+ * whenever the given object is not a matrix over GF(2^m).
+ *
+ * @param other object
+ * @return true or false
+ */
+ public boolean equals(Object other)
+ {
+
+ if (other == null || !(other instanceof GF2mMatrix))
+ {
+ return false;
+ }
+
+ GF2mMatrix otherMatrix = (GF2mMatrix)other;
+
+ if ((!this.field.equals(otherMatrix.field))
+ || (otherMatrix.numRows != this.numColumns)
+ || (otherMatrix.numColumns != this.numColumns))
+ {
+ return false;
+ }
+
+ for (int i = 0; i < this.numRows; i++)
+ {
+ for (int j = 0; j < this.numColumns; j++)
+ {
+ if (this.matrix[i][j] != otherMatrix.matrix[i][j])
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public int hashCode()
+ {
+ int hash = (this.field.hashCode() * 31 + numRows) * 31 + numColumns;
+ for (int i = 0; i < this.numRows; i++)
+ {
+ for (int j = 0; j < this.numColumns; j++)
+ {
+ hash = hash * 31 + matrix[i][j];
+ }
+ }
+ return hash;
+ }
+
+ public String toString()
+ {
+ String str = this.numRows + " x " + this.numColumns + " Matrix over "
+ + this.field.toString() + ": \n";
+
+ for (int i = 0; i < this.numRows; i++)
+ {
+ for (int j = 0; j < this.numColumns; j++)
+ {
+ str = str + this.field.elementToStr(matrix[i][j]) + " : ";
+ }
+ str = str + "\n";
+ }
+
+ return str;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mVector.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mVector.java
new file mode 100644
index 00000000..1f2f5953
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mVector.java
@@ -0,0 +1,256 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+
+/**
+ * This class implements vectors over the finite field
+ * <tt>GF(2<sup>m</sup>)</tt> for small <tt>m</tt> (i.e.,
+ * <tt>1&lt;m&lt;32</tt>). It extends the abstract class {@link Vector}.
+ */
+public class GF2mVector
+ extends Vector
+{
+
+ /**
+ * the finite field this vector is defined over
+ */
+ private GF2mField field;
+
+ /**
+ * the element array
+ */
+ private int[] vector;
+
+ /**
+ * creates the vector over GF(2^m) of given length and with elements from
+ * array v (beginning at the first bit)
+ *
+ * @param field finite field
+ * @param v array with elements of vector
+ */
+ public GF2mVector(GF2mField field, byte[] v)
+ {
+ this.field = new GF2mField(field);
+
+ // decode vector
+ int d = 8;
+ int count = 1;
+ while (field.getDegree() > d)
+ {
+ count++;
+ d += 8;
+ }
+
+ if ((v.length % count) != 0)
+ {
+ throw new IllegalArgumentException(
+ "Byte array is not an encoded vector over the given finite field.");
+ }
+
+ length = v.length / count;
+ vector = new int[length];
+ count = 0;
+ for (int i = 0; i < vector.length; i++)
+ {
+ for (int j = 0; j < d; j += 8)
+ {
+ vector[i] |= (v[count++] & 0xff) << j;
+ }
+ if (!field.isElementOfThisField(vector[i]))
+ {
+ throw new IllegalArgumentException(
+ "Byte array is not an encoded vector over the given finite field.");
+ }
+ }
+ }
+
+ /**
+ * Create a new vector over <tt>GF(2<sup>m</sup>)</tt> of the given
+ * length and element array.
+ *
+ * @param field the finite field <tt>GF(2<sup>m</sup>)</tt>
+ * @param vector the element array
+ */
+ public GF2mVector(GF2mField field, int[] vector)
+ {
+ this.field = field;
+ length = vector.length;
+ for (int i = vector.length - 1; i >= 0; i--)
+ {
+ if (!field.isElementOfThisField(vector[i]))
+ {
+ throw new ArithmeticException(
+ "Element array is not specified over the given finite field.");
+ }
+ }
+ this.vector = IntUtils.clone(vector);
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other another {@link GF2mVector}
+ */
+ public GF2mVector(GF2mVector other)
+ {
+ field = new GF2mField(other.field);
+ length = other.length;
+ vector = IntUtils.clone(other.vector);
+ }
+
+ /**
+ * @return the finite field this vector is defined over
+ */
+ public GF2mField getField()
+ {
+ return field;
+ }
+
+ /**
+ * @return int[] form of this vector
+ */
+ public int[] getIntArrayForm()
+ {
+ return IntUtils.clone(vector);
+ }
+
+ /**
+ * @return a byte array encoding of this vector
+ */
+ public byte[] getEncoded()
+ {
+ int d = 8;
+ int count = 1;
+ while (field.getDegree() > d)
+ {
+ count++;
+ d += 8;
+ }
+
+ byte[] res = new byte[vector.length * count];
+ count = 0;
+ for (int i = 0; i < vector.length; i++)
+ {
+ for (int j = 0; j < d; j += 8)
+ {
+ res[count++] = (byte)(vector[i] >>> j);
+ }
+ }
+
+ return res;
+ }
+
+ /**
+ * @return whether this is the zero vector (i.e., all elements are zero)
+ */
+ public boolean isZero()
+ {
+ for (int i = vector.length - 1; i >= 0; i--)
+ {
+ if (vector[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Add another vector to this vector. Method is not yet implemented.
+ *
+ * @param addend the other vector
+ * @return <tt>this + addend</tt>
+ * @throws ArithmeticException if the other vector is not defined over the same field as
+ * this vector.
+ * <p/>
+ * TODO: implement this method
+ */
+ public Vector add(Vector addend)
+ {
+ throw new RuntimeException("not implemented");
+ }
+
+ /**
+ * Multiply this vector with a permutation.
+ *
+ * @param p the permutation
+ * @return <tt>this*p = p*this</tt>
+ */
+ public Vector multiply(Permutation p)
+ {
+ int[] pVec = p.getVector();
+ if (length != pVec.length)
+ {
+ throw new ArithmeticException(
+ "permutation size and vector size mismatch");
+ }
+
+ int[] result = new int[length];
+ for (int i = 0; i < pVec.length; i++)
+ {
+ result[i] = vector[pVec[i]];
+ }
+
+ return new GF2mVector(field, result);
+ }
+
+ /**
+ * Compare this vector with another object.
+ *
+ * @param other the other object
+ * @return the result of the comparison
+ */
+ public boolean equals(Object other)
+ {
+
+ if (!(other instanceof GF2mVector))
+ {
+ return false;
+ }
+ GF2mVector otherVec = (GF2mVector)other;
+
+ if (!field.equals(otherVec.field))
+ {
+ return false;
+ }
+
+ return IntUtils.equals(vector, otherVec.vector);
+ }
+
+ /**
+ * @return the hash code of this vector
+ */
+ public int hashCode()
+ {
+ int hash = this.field.hashCode();
+ hash = hash * 31 + vector.hashCode();
+ return hash;
+ }
+
+ /**
+ * @return a human readable form of this vector
+ */
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < vector.length; i++)
+ {
+ for (int j = 0; j < field.getDegree(); j++)
+ {
+ int r = j & 0x1f;
+ int bitMask = 1 << r;
+ int coeff = vector[i] & bitMask;
+ if (coeff != 0)
+ {
+ buf.append('1');
+ }
+ else
+ {
+ buf.append('0');
+ }
+ }
+ buf.append(' ');
+ }
+ return buf.toString();
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nElement.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nElement.java
new file mode 100644
index 00000000..faa99dcb
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nElement.java
@@ -0,0 +1,186 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+
+/**
+ * This abstract class implements an element of the finite field <i>GF(2)<sup>n
+ * </sup></i> in either <i>optimal normal basis</i> representation (<i>ONB</i>)
+ * or in <i>polynomial</i> representation. It is extended by the classes <a
+ * href = GF2nONBElement.html><tt> GF2nONBElement</tt></a> and <a href =
+ * GF2nPolynomialElement.html> <tt>GF2nPolynomialElement</tt> </a>.
+ *
+ * @see GF2nPolynomialElement
+ * @see GF2nONBElement
+ * @see GF2nONBField
+ */
+public abstract class GF2nElement
+ implements GFElement
+{
+
+ // /////////////////////////////////////////////////////////////////////
+ // member variables
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * holds a pointer to this element's corresponding field.
+ */
+ protected GF2nField mField;
+
+ /**
+ * holds the extension degree <i>n</i> of this element's corresponding
+ * field.
+ */
+ protected int mDegree;
+
+ // /////////////////////////////////////////////////////////////////////
+ // pseudo-constructors
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * @return a copy of this GF2nElement
+ */
+ public abstract Object clone();
+
+ // /////////////////////////////////////////////////////////////////////
+ // assignments
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Assign the value 0 to this element.
+ */
+ abstract void assignZero();
+
+ /**
+ * Assigns the value 1 to this element.
+ */
+ abstract void assignOne();
+
+ // /////////////////////////////////////////////////////////////////////
+ // access
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns whether the rightmost bit of the bit representation is set. This
+ * is needed for data conversion according to 1363.
+ *
+ * @return true if the rightmost bit of this element is set
+ */
+ public abstract boolean testRightmostBit();
+
+ /**
+ * Checks whether the indexed bit of the bit representation is set
+ *
+ * @param index the index of the bit to test
+ * @return <tt>true</tt> if the indexed bit is set
+ */
+ abstract boolean testBit(int index);
+
+ /**
+ * Returns the field of this element.
+ *
+ * @return the field of this element
+ */
+ public final GF2nField getField()
+ {
+ return mField;
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // arithmetic
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns <tt>this</tt> element + 1.
+ *
+ * @return <tt>this</tt> + 1
+ */
+ public abstract GF2nElement increase();
+
+ /**
+ * Increases this element by one.
+ */
+ public abstract void increaseThis();
+
+ /**
+ * Compute the difference of this element and <tt>minuend</tt>.
+ *
+ * @param minuend the minuend
+ * @return <tt>this - minuend</tt> (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public final GFElement subtract(GFElement minuend)
+ throws RuntimeException
+ {
+ return add(minuend);
+ }
+
+ /**
+ * Compute the difference of this element and <tt>minuend</tt>,
+ * overwriting this element.
+ *
+ * @param minuend the minuend
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public final void subtractFromThis(GFElement minuend)
+ {
+ addToThis(minuend);
+ }
+
+ /**
+ * Returns <tt>this</tt> element to the power of 2.
+ *
+ * @return <tt>this</tt><sup>2</sup>
+ */
+ public abstract GF2nElement square();
+
+ /**
+ * Squares <tt>this</tt> element.
+ */
+ public abstract void squareThis();
+
+ /**
+ * Compute the square root of this element and return the result in a new
+ * {@link GF2nElement}.
+ *
+ * @return <tt>this<sup>1/2</sup></tt> (newly created)
+ */
+ public abstract GF2nElement squareRoot();
+
+ /**
+ * Compute the square root of this element.
+ */
+ public abstract void squareRootThis();
+
+ /**
+ * Performs a basis transformation of this element to the given GF2nField
+ * <tt>basis</tt>.
+ *
+ * @param basis the GF2nField representation to transform this element to
+ * @return this element in the representation of <tt>basis</tt>
+ * @throws DifferentFieldsException if <tt>this</tt> cannot be converted according to
+ * <tt>basis</tt>.
+ */
+ public final GF2nElement convert(GF2nField basis)
+ throws RuntimeException
+ {
+ return mField.convert(this, basis);
+ }
+
+ /**
+ * Returns the trace of this element.
+ *
+ * @return the trace of this element
+ */
+ public abstract int trace();
+
+ /**
+ * Solves a quadratic equation.<br>
+ * Let z<sup>2</sup> + z = <tt>this</tt>. Then this method returns z.
+ *
+ * @return z with z<sup>2</sup> + z = <tt>this</tt>
+ * @throws NoSolutionException if z<sup>2</sup> + z = <tt>this</tt> does not have a
+ * solution
+ */
+ public abstract GF2nElement solveQuadraticEquation()
+ throws RuntimeException;
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nField.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nField.java
new file mode 100644
index 00000000..907afd76
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nField.java
@@ -0,0 +1,292 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+
+import java.util.Vector;
+
+
+/**
+ * This abstract class defines the finite field <i>GF(2<sup>n</sup>)</i>. It
+ * holds the extension degree <i>n</i>, the characteristic, the irreducible
+ * fieldpolynomial and conversion matrices. GF2nField is implemented by the
+ * classes GF2nPolynomialField and GF2nONBField.
+ *
+ * @see GF2nONBField
+ * @see GF2nPolynomialField
+ */
+public abstract class GF2nField
+{
+
+ /**
+ * the degree of this field
+ */
+ protected int mDegree;
+
+ /**
+ * the irreducible fieldPolynomial stored in normal order (also for ONB)
+ */
+ protected GF2Polynomial fieldPolynomial;
+
+ /**
+ * holds a list of GF2nFields to which elements have been converted and thus
+ * a COB-Matrix exists
+ */
+ protected Vector fields;
+
+ /**
+ * the COB matrices
+ */
+ protected Vector matrices;
+
+ /**
+ * Returns the degree <i>n</i> of this field.
+ *
+ * @return the degree <i>n</i> of this field
+ */
+ public final int getDegree()
+ {
+ return mDegree;
+ }
+
+ /**
+ * Returns the fieldpolynomial as a new Bitstring.
+ *
+ * @return a copy of the fieldpolynomial as a new Bitstring
+ */
+ public final GF2Polynomial getFieldPolynomial()
+ {
+ if (fieldPolynomial == null)
+ {
+ computeFieldPolynomial();
+ }
+ return new GF2Polynomial(fieldPolynomial);
+ }
+
+ /**
+ * Decides whether the given object <tt>other</tt> is the same as this
+ * field.
+ *
+ * @param other another object
+ * @return (this == other)
+ */
+ public final boolean equals(Object other)
+ {
+ if (other == null || !(other instanceof GF2nField))
+ {
+ return false;
+ }
+
+ GF2nField otherField = (GF2nField)other;
+
+ if (otherField.mDegree != mDegree)
+ {
+ return false;
+ }
+ if (!fieldPolynomial.equals(otherField.fieldPolynomial))
+ {
+ return false;
+ }
+ if ((this instanceof GF2nPolynomialField)
+ && !(otherField instanceof GF2nPolynomialField))
+ {
+ return false;
+ }
+ if ((this instanceof GF2nONBField)
+ && !(otherField instanceof GF2nONBField))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @return the hash code of this field
+ */
+ public int hashCode()
+ {
+ return mDegree + fieldPolynomial.hashCode();
+ }
+
+ /**
+ * Computes a random root from the given irreducible fieldpolynomial
+ * according to IEEE 1363 algorithm A.5.6. This cal take very long for big
+ * degrees.
+ *
+ * @param B0FieldPolynomial the fieldpolynomial if the other basis as a Bitstring
+ * @return a random root of BOFieldPolynomial in representation according to
+ * this field
+ * @see "P1363 A.5.6, p103f"
+ */
+ protected abstract GF2nElement getRandomRoot(GF2Polynomial B0FieldPolynomial);
+
+ /**
+ * Computes the change-of-basis matrix for basis conversion according to
+ * 1363. The result is stored in the lists fields and matrices.
+ *
+ * @param B1 the GF2nField to convert to
+ * @see "P1363 A.7.3, p111ff"
+ */
+ protected abstract void computeCOBMatrix(GF2nField B1);
+
+ /**
+ * Computes the fieldpolynomial. This can take a long time for big degrees.
+ */
+ protected abstract void computeFieldPolynomial();
+
+ /**
+ * Inverts the given matrix represented as bitstrings.
+ *
+ * @param matrix the matrix to invert as a Bitstring[]
+ * @return matrix^(-1)
+ */
+ protected final GF2Polynomial[] invertMatrix(GF2Polynomial[] matrix)
+ {
+ GF2Polynomial[] a = new GF2Polynomial[matrix.length];
+ GF2Polynomial[] inv = new GF2Polynomial[matrix.length];
+ GF2Polynomial dummy;
+ int i, j;
+ // initialize a as a copy of matrix and inv as E(inheitsmatrix)
+ for (i = 0; i < mDegree; i++)
+ {
+ try
+ {
+ a[i] = new GF2Polynomial(matrix[i]);
+ inv[i] = new GF2Polynomial(mDegree);
+ inv[i].setBit(mDegree - 1 - i);
+ }
+ catch (RuntimeException BDNEExc)
+ {
+ BDNEExc.printStackTrace();
+ }
+ }
+ // construct triangle matrix so that for each a[i] the first i bits are
+ // zero
+ for (i = 0; i < mDegree - 1; i++)
+ {
+ // find column where bit i is set
+ j = i;
+ while ((j < mDegree) && !a[j].testBit(mDegree - 1 - i))
+ {
+ j++;
+ }
+ if (j >= mDegree)
+ {
+ throw new RuntimeException(
+ "GF2nField.invertMatrix: Matrix cannot be inverted!");
+ }
+ if (i != j)
+ { // swap a[i]/a[j] and inv[i]/inv[j]
+ dummy = a[i];
+ a[i] = a[j];
+ a[j] = dummy;
+ dummy = inv[i];
+ inv[i] = inv[j];
+ inv[j] = dummy;
+ }
+ for (j = i + 1; j < mDegree; j++)
+ { // add column i to all columns>i
+ // having their i-th bit set
+ if (a[j].testBit(mDegree - 1 - i))
+ {
+ a[j].addToThis(a[i]);
+ inv[j].addToThis(inv[i]);
+ }
+ }
+ }
+ // construct Einheitsmatrix from a
+ for (i = mDegree - 1; i > 0; i--)
+ {
+ for (j = i - 1; j >= 0; j--)
+ { // eliminate the i-th bit in all
+ // columns < i
+ if (a[j].testBit(mDegree - 1 - i))
+ {
+ a[j].addToThis(a[i]);
+ inv[j].addToThis(inv[i]);
+ }
+ }
+ }
+ return inv;
+ }
+
+ /**
+ * Converts the given element in representation according to this field to a
+ * new element in representation according to B1 using the change-of-basis
+ * matrix calculated by computeCOBMatrix.
+ *
+ * @param elem the GF2nElement to convert
+ * @param basis the basis to convert <tt>elem</tt> to
+ * @return <tt>elem</tt> converted to a new element representation
+ * according to <tt>basis</tt>
+ * @throws DifferentFieldsException if <tt>elem</tt> cannot be converted according to
+ * <tt>basis</tt>.
+ * @see GF2nField#computeCOBMatrix
+ * @see GF2nField#getRandomRoot
+ * @see GF2nPolynomial
+ * @see "P1363 A.7 p109ff"
+ */
+ public final GF2nElement convert(GF2nElement elem, GF2nField basis)
+ throws RuntimeException
+ {
+ if (basis == this)
+ {
+ return (GF2nElement)elem.clone();
+ }
+ if (fieldPolynomial.equals(basis.fieldPolynomial))
+ {
+ return (GF2nElement)elem.clone();
+ }
+ if (mDegree != basis.mDegree)
+ {
+ throw new RuntimeException("GF2nField.convert: B1 has a"
+ + " different degree and thus cannot be coverted to!");
+ }
+
+ int i;
+ GF2Polynomial[] COBMatrix;
+ i = fields.indexOf(basis);
+ if (i == -1)
+ {
+ computeCOBMatrix(basis);
+ i = fields.indexOf(basis);
+ }
+ COBMatrix = (GF2Polynomial[])matrices.elementAt(i);
+
+ GF2nElement elemCopy = (GF2nElement)elem.clone();
+ if (elemCopy instanceof GF2nONBElement)
+ {
+ // remember: ONB treats its bits in reverse order
+ ((GF2nONBElement)elemCopy).reverseOrder();
+ }
+ GF2Polynomial bs = new GF2Polynomial(mDegree, elemCopy.toFlexiBigInt());
+ bs.expandN(mDegree);
+ GF2Polynomial result = new GF2Polynomial(mDegree);
+ for (i = 0; i < mDegree; i++)
+ {
+ if (bs.vectorMult(COBMatrix[i]))
+ {
+ result.setBit(mDegree - 1 - i);
+ }
+ }
+ if (basis instanceof GF2nPolynomialField)
+ {
+ return new GF2nPolynomialElement((GF2nPolynomialField)basis,
+ result);
+ }
+ else if (basis instanceof GF2nONBField)
+ {
+ GF2nONBElement res = new GF2nONBElement((GF2nONBField)basis,
+ result.toFlexiBigInt());
+ // TODO Remember: ONB treats its Bits in reverse order !!!
+ res.reverseOrder();
+ return res;
+ }
+ else
+ {
+ throw new RuntimeException(
+ "GF2nField.convert: B1 must be an instance of "
+ + "GF2nPolynomialField or GF2nONBField!");
+ }
+
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nONBElement.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nONBElement.java
new file mode 100644
index 00000000..d8ae6c7a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nONBElement.java
@@ -0,0 +1,1154 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+
+import java.math.BigInteger;
+import java.util.Random;
+
+/**
+ * This class implements an element of the finite field <i>GF(2<sup>n </sup>)</i>.
+ * It is represented in an optimal normal basis representation and holds the
+ * pointer <tt>mField</tt> to its corresponding field.
+ *
+ * @see GF2nField
+ * @see GF2nElement
+ */
+public class GF2nONBElement
+ extends GF2nElement
+{
+
+ // /////////////////////////////////////////////////////////////////////
+ // member variables
+ // /////////////////////////////////////////////////////////////////////
+
+ private static final long[] mBitmask = new long[]{0x0000000000000001L,
+ 0x0000000000000002L, 0x0000000000000004L, 0x0000000000000008L,
+ 0x0000000000000010L, 0x0000000000000020L, 0x0000000000000040L,
+ 0x0000000000000080L, 0x0000000000000100L, 0x0000000000000200L,
+ 0x0000000000000400L, 0x0000000000000800L, 0x0000000000001000L,
+ 0x0000000000002000L, 0x0000000000004000L, 0x0000000000008000L,
+ 0x0000000000010000L, 0x0000000000020000L, 0x0000000000040000L,
+ 0x0000000000080000L, 0x0000000000100000L, 0x0000000000200000L,
+ 0x0000000000400000L, 0x0000000000800000L, 0x0000000001000000L,
+ 0x0000000002000000L, 0x0000000004000000L, 0x0000000008000000L,
+ 0x0000000010000000L, 0x0000000020000000L, 0x0000000040000000L,
+ 0x0000000080000000L, 0x0000000100000000L, 0x0000000200000000L,
+ 0x0000000400000000L, 0x0000000800000000L, 0x0000001000000000L,
+ 0x0000002000000000L, 0x0000004000000000L, 0x0000008000000000L,
+ 0x0000010000000000L, 0x0000020000000000L, 0x0000040000000000L,
+ 0x0000080000000000L, 0x0000100000000000L, 0x0000200000000000L,
+ 0x0000400000000000L, 0x0000800000000000L, 0x0001000000000000L,
+ 0x0002000000000000L, 0x0004000000000000L, 0x0008000000000000L,
+ 0x0010000000000000L, 0x0020000000000000L, 0x0040000000000000L,
+ 0x0080000000000000L, 0x0100000000000000L, 0x0200000000000000L,
+ 0x0400000000000000L, 0x0800000000000000L, 0x1000000000000000L,
+ 0x2000000000000000L, 0x4000000000000000L, 0x8000000000000000L};
+
+ private static final long[] mMaxmask = new long[]{0x0000000000000001L,
+ 0x0000000000000003L, 0x0000000000000007L, 0x000000000000000FL,
+ 0x000000000000001FL, 0x000000000000003FL, 0x000000000000007FL,
+ 0x00000000000000FFL, 0x00000000000001FFL, 0x00000000000003FFL,
+ 0x00000000000007FFL, 0x0000000000000FFFL, 0x0000000000001FFFL,
+ 0x0000000000003FFFL, 0x0000000000007FFFL, 0x000000000000FFFFL,
+ 0x000000000001FFFFL, 0x000000000003FFFFL, 0x000000000007FFFFL,
+ 0x00000000000FFFFFL, 0x00000000001FFFFFL, 0x00000000003FFFFFL,
+ 0x00000000007FFFFFL, 0x0000000000FFFFFFL, 0x0000000001FFFFFFL,
+ 0x0000000003FFFFFFL, 0x0000000007FFFFFFL, 0x000000000FFFFFFFL,
+ 0x000000001FFFFFFFL, 0x000000003FFFFFFFL, 0x000000007FFFFFFFL,
+ 0x00000000FFFFFFFFL, 0x00000001FFFFFFFFL, 0x00000003FFFFFFFFL,
+ 0x00000007FFFFFFFFL, 0x0000000FFFFFFFFFL, 0x0000001FFFFFFFFFL,
+ 0x0000003FFFFFFFFFL, 0x0000007FFFFFFFFFL, 0x000000FFFFFFFFFFL,
+ 0x000001FFFFFFFFFFL, 0x000003FFFFFFFFFFL, 0x000007FFFFFFFFFFL,
+ 0x00000FFFFFFFFFFFL, 0x00001FFFFFFFFFFFL, 0x00003FFFFFFFFFFFL,
+ 0x00007FFFFFFFFFFFL, 0x0000FFFFFFFFFFFFL, 0x0001FFFFFFFFFFFFL,
+ 0x0003FFFFFFFFFFFFL, 0x0007FFFFFFFFFFFFL, 0x000FFFFFFFFFFFFFL,
+ 0x001FFFFFFFFFFFFFL, 0x003FFFFFFFFFFFFFL, 0x007FFFFFFFFFFFFFL,
+ 0x00FFFFFFFFFFFFFFL, 0x01FFFFFFFFFFFFFFL, 0x03FFFFFFFFFFFFFFL,
+ 0x07FFFFFFFFFFFFFFL, 0x0FFFFFFFFFFFFFFFL, 0x1FFFFFFFFFFFFFFFL,
+ 0x3FFFFFFFFFFFFFFFL, 0x7FFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL};
+
+ // mIBy64[j * 16 + i] = (j * 16 + i)/64
+ // i =
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ //
+ private static final int[] mIBY64 = new int[]{
+ // j =
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 8
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 9
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 10
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 11
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 12
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 13
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 14
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 15
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 16
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 17
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 18
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 19
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 20
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 21
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 22
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 // 23
+ };
+
+ private static final int MAXLONG = 64;
+
+ /**
+ * holds the lenght of the polynomial with 64 bit sized fields.
+ */
+ private int mLength;
+
+ /**
+ * holds the value of mDeg % MAXLONG.
+ */
+ private int mBit;
+
+ /**
+ * holds this element in ONB representation.
+ */
+ private long[] mPol;
+
+ // /////////////////////////////////////////////////////////////////////
+ // constructors
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Construct a random element over the field <tt>gf2n</tt>, using the
+ * specified source of randomness.
+ *
+ * @param gf2n the field
+ * @param rand the source of randomness
+ */
+ public GF2nONBElement(GF2nONBField gf2n, Random rand)
+ {
+ mField = gf2n;
+ mDegree = mField.getDegree();
+ mLength = gf2n.getONBLength();
+ mBit = gf2n.getONBBit();
+ mPol = new long[mLength];
+ if (mLength > 1)
+ {
+ for (int j = 0; j < mLength - 1; j++)
+ {
+ mPol[j] = rand.nextLong();
+ }
+ long last = rand.nextLong();
+ mPol[mLength - 1] = last >>> (MAXLONG - mBit);
+ }
+ else
+ {
+ mPol[0] = rand.nextLong();
+ mPol[0] = mPol[0] >>> (MAXLONG - mBit);
+ }
+ }
+
+ /**
+ * Construct a new GF2nONBElement from its encoding.
+ *
+ * @param gf2n the field
+ * @param e the encoded element
+ */
+ public GF2nONBElement(GF2nONBField gf2n, byte[] e)
+ {
+ mField = gf2n;
+ mDegree = mField.getDegree();
+ mLength = gf2n.getONBLength();
+ mBit = gf2n.getONBBit();
+ mPol = new long[mLength];
+ assign(e);
+ }
+
+ /**
+ * Construct the element of the field <tt>gf2n</tt> with the specified
+ * value <tt>val</tt>.
+ *
+ * @param gf2n the field
+ * @param val the value represented by a BigInteger
+ */
+ public GF2nONBElement(GF2nONBField gf2n, BigInteger val)
+ {
+ mField = gf2n;
+ mDegree = mField.getDegree();
+ mLength = gf2n.getONBLength();
+ mBit = gf2n.getONBBit();
+ mPol = new long[mLength];
+ assign(val);
+ }
+
+ /**
+ * Construct the element of the field <tt>gf2n</tt> with the specified
+ * value <tt>val</tt>.
+ *
+ * @param gf2n the field
+ * @param val the value in ONB representation
+ */
+ private GF2nONBElement(GF2nONBField gf2n, long[] val)
+ {
+ mField = gf2n;
+ mDegree = mField.getDegree();
+ mLength = gf2n.getONBLength();
+ mBit = gf2n.getONBBit();
+ mPol = val;
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // pseudo-constructors
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Copy constructor.
+ *
+ * @param gf2n the field
+ */
+ public GF2nONBElement(GF2nONBElement gf2n)
+ {
+
+ mField = gf2n.mField;
+ mDegree = mField.getDegree();
+ mLength = ((GF2nONBField)mField).getONBLength();
+ mBit = ((GF2nONBField)mField).getONBBit();
+ mPol = new long[mLength];
+ assign(gf2n.getElement());
+ }
+
+ /**
+ * Create a new GF2nONBElement by cloning this GF2nPolynomialElement.
+ *
+ * @return a copy of this element
+ */
+ public Object clone()
+ {
+ return new GF2nONBElement(this);
+ }
+
+ /**
+ * Create the zero element.
+ *
+ * @param gf2n the finite field
+ * @return the zero element in the given finite field
+ */
+ public static GF2nONBElement ZERO(GF2nONBField gf2n)
+ {
+ long[] polynomial = new long[gf2n.getONBLength()];
+ return new GF2nONBElement(gf2n, polynomial);
+ }
+
+ /**
+ * Create the one element.
+ *
+ * @param gf2n the finite field
+ * @return the one element in the given finite field
+ */
+ public static GF2nONBElement ONE(GF2nONBField gf2n)
+ {
+ int mLength = gf2n.getONBLength();
+ long[] polynomial = new long[mLength];
+
+ // fill mDegree coefficients with one's
+ for (int i = 0; i < mLength - 1; i++)
+ {
+ polynomial[i] = 0xffffffffffffffffL;
+ }
+ polynomial[mLength - 1] = mMaxmask[gf2n.getONBBit() - 1];
+
+ return new GF2nONBElement(gf2n, polynomial);
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // assignments
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * assigns to this element the zero element
+ */
+ void assignZero()
+ {
+ mPol = new long[mLength];
+ }
+
+ /**
+ * assigns to this element the one element
+ */
+ void assignOne()
+ {
+ // fill mDegree coefficients with one's
+ for (int i = 0; i < mLength - 1; i++)
+ {
+ mPol[i] = 0xffffffffffffffffL;
+ }
+ mPol[mLength - 1] = mMaxmask[mBit - 1];
+ }
+
+ /**
+ * assigns to this element the value <tt>val</tt>.
+ *
+ * @param val the value represented by a BigInteger
+ */
+ private void assign(BigInteger val)
+ {
+ assign(val.toByteArray());
+ }
+
+ /**
+ * assigns to this element the value <tt>val</tt>.
+ *
+ * @param val the value in ONB representation
+ */
+ private void assign(long[] val)
+ {
+ System.arraycopy(val, 0, mPol, 0, mLength);
+ }
+
+ /**
+ * assigns to this element the value <tt>val</tt>. First: inverting the
+ * order of val into reversed[]. That means: reversed[0] = val[length - 1],
+ * ..., reversed[reversed.length - 1] = val[0]. Second: mPol[0] = sum{i = 0,
+ * ... 7} (val[i]<<(i*8)) .... mPol[1] = sum{i = 8, ... 15} (val[i]<<(i*8))
+ *
+ * @param val the value in ONB representation
+ */
+ private void assign(byte[] val)
+ {
+ int j;
+ mPol = new long[mLength];
+ for (j = 0; j < val.length; j++)
+ {
+ mPol[j >>> 3] |= (val[val.length - 1 - j] & 0x00000000000000ffL) << ((j & 0x07) << 3);
+ }
+ }
+
+ // /////////////////////////////////////////////////////////////////
+ // comparison
+ // /////////////////////////////////////////////////////////////////
+
+ /**
+ * Checks whether this element is zero.
+ *
+ * @return <tt>true</tt> if <tt>this</tt> is the zero element
+ */
+ public boolean isZero()
+ {
+
+ boolean result = true;
+
+ for (int i = 0; i < mLength && result; i++)
+ {
+ result = result && ((mPol[i] & 0xFFFFFFFFFFFFFFFFL) == 0);
+ }
+
+ return result;
+ }
+
+ /**
+ * Checks whether this element is one.
+ *
+ * @return <tt>true</tt> if <tt>this</tt> is the one element
+ */
+ public boolean isOne()
+ {
+
+ boolean result = true;
+
+ for (int i = 0; i < mLength - 1 && result; i++)
+ {
+ result = result
+ && ((mPol[i] & 0xFFFFFFFFFFFFFFFFL) == 0xFFFFFFFFFFFFFFFFL);
+ }
+
+ if (result)
+ {
+ result = result
+ && ((mPol[mLength - 1] & mMaxmask[mBit - 1]) == mMaxmask[mBit - 1]);
+ }
+
+ return result;
+ }
+
+ /**
+ * Compare this element with another object.
+ *
+ * @param other the other object
+ * @return <tt>true</tt> if the two objects are equal, <tt>false</tt>
+ * otherwise
+ */
+ public boolean equals(Object other)
+ {
+ if (other == null || !(other instanceof GF2nONBElement))
+ {
+ return false;
+ }
+
+ GF2nONBElement otherElem = (GF2nONBElement)other;
+
+ for (int i = 0; i < mLength; i++)
+ {
+ if (mPol[i] != otherElem.mPol[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @return the hash code of this element
+ */
+ public int hashCode()
+ {
+ return mPol.hashCode();
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // access
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns whether the highest bit of the bit representation is set
+ *
+ * @return true, if the highest bit of mPol is set, false, otherwise
+ */
+ public boolean testRightmostBit()
+ {
+ // due to the reverse bit order (compared to 1363) this method returns
+ // the value of the leftmost bit
+ return (mPol[mLength - 1] & mBitmask[mBit - 1]) != 0L;
+ }
+
+ /**
+ * Checks whether the indexed bit of the bit representation is set. Warning:
+ * GF2nONBElement currently stores its bits in reverse order (compared to
+ * 1363) !!!
+ *
+ * @param index the index of the bit to test
+ * @return <tt>true</tt> if the indexed bit of mPol is set, <tt>false</tt>
+ * otherwise.
+ */
+ boolean testBit(int index)
+ {
+ if (index < 0 || index > mDegree)
+ {
+ return false;
+ }
+ long test = mPol[index >>> 6] & mBitmask[index & 0x3f];
+ return test != 0x0L;
+ }
+
+ /**
+ * @return this element in its ONB representation
+ */
+ private long[] getElement()
+ {
+
+ long[] result = new long[mPol.length];
+ System.arraycopy(mPol, 0, result, 0, mPol.length);
+
+ return result;
+ }
+
+ /**
+ * Returns the ONB representation of this element. The Bit-Order is
+ * exchanged (according to 1363)!
+ *
+ * @return this element in its representation and reverse bit-order
+ */
+ private long[] getElementReverseOrder()
+ {
+ long[] result = new long[mPol.length];
+ for (int i = 0; i < mDegree; i++)
+ {
+ if (testBit(mDegree - i - 1))
+ {
+ result[i >>> 6] |= mBitmask[i & 0x3f];
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Reverses the bit-order in this element(according to 1363). This is a
+ * hack!
+ */
+ void reverseOrder()
+ {
+ mPol = getElementReverseOrder();
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // arithmetic
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Compute the sum of this element and <tt>addend</tt>.
+ *
+ * @param addend the addend
+ * @return <tt>this + other</tt> (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public GFElement add(GFElement addend)
+ throws RuntimeException
+ {
+ GF2nONBElement result = new GF2nONBElement(this);
+ result.addToThis(addend);
+ return result;
+ }
+
+ /**
+ * Compute <tt>this + addend</tt> (overwrite <tt>this</tt>).
+ *
+ * @param addend the addend
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public void addToThis(GFElement addend)
+ throws RuntimeException
+ {
+ if (!(addend instanceof GF2nONBElement))
+ {
+ throw new RuntimeException();
+ }
+ if (!mField.equals(((GF2nONBElement)addend).mField))
+ {
+ throw new RuntimeException();
+ }
+
+ for (int i = 0; i < mLength; i++)
+ {
+ mPol[i] ^= ((GF2nONBElement)addend).mPol[i];
+ }
+ }
+
+ /**
+ * returns <tt>this</tt> element + 1.
+ *
+ * @return <tt>this</tt> + 1
+ */
+ public GF2nElement increase()
+ {
+ GF2nONBElement result = new GF2nONBElement(this);
+ result.increaseThis();
+ return result;
+ }
+
+ /**
+ * increases <tt>this</tt> element.
+ */
+ public void increaseThis()
+ {
+ addToThis(ONE((GF2nONBField)mField));
+ }
+
+ /**
+ * Compute the product of this element and <tt>factor</tt>.
+ *
+ * @param factor the factor
+ * @return <tt>this * factor</tt> (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public GFElement multiply(GFElement factor)
+ throws RuntimeException
+ {
+ GF2nONBElement result = new GF2nONBElement(this);
+ result.multiplyThisBy(factor);
+ return result;
+ }
+
+ /**
+ * Compute <tt>this * factor</tt> (overwrite <tt>this</tt>).
+ *
+ * @param factor the factor
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public void multiplyThisBy(GFElement factor)
+ throws RuntimeException
+ {
+
+ if (!(factor instanceof GF2nONBElement))
+ {
+ throw new RuntimeException("The elements have different"
+ + " representation: not yet" + " implemented");
+ }
+ if (!mField.equals(((GF2nONBElement)factor).mField))
+ {
+ throw new RuntimeException();
+ }
+
+ if (equals(factor))
+ {
+ squareThis();
+ }
+ else
+ {
+
+ long[] a = mPol;
+ long[] b = ((GF2nONBElement)factor).mPol;
+ long[] c = new long[mLength];
+
+ int[][] m = ((GF2nONBField)mField).mMult;
+
+ int degf, degb, s, fielda, fieldb, bita, bitb;
+ degf = mLength - 1;
+ degb = mBit - 1;
+ s = 0;
+
+ long TWOTOMAXLONGM1 = mBitmask[MAXLONG - 1];
+ long TWOTODEGB = mBitmask[degb];
+
+ boolean old, now;
+
+ // the product c of a and b (a*b = c) is calculated in mDegree
+ // cicles
+ // in every cicle one coefficient of c is calculated and stored
+ // k indicates the coefficient
+ //
+ for (int k = 0; k < mDegree; k++)
+ {
+
+ s = 0;
+
+ for (int i = 0; i < mDegree; i++)
+ {
+
+ // fielda = i / MAXLONG
+ //
+ fielda = mIBY64[i];
+
+ // bita = i % MAXLONG
+ //
+ bita = i & (MAXLONG - 1);
+
+ // fieldb = m[i][0] / MAXLONG
+ //
+ fieldb = mIBY64[m[i][0]];
+
+ // bitb = m[i][0] % MAXLONG
+ //
+ bitb = m[i][0] & (MAXLONG - 1);
+
+ if ((a[fielda] & mBitmask[bita]) != 0)
+ {
+
+ if ((b[fieldb] & mBitmask[bitb]) != 0)
+ {
+ s ^= 1;
+ }
+
+ if (m[i][1] != -1)
+ {
+
+ // fieldb = m[i][1] / MAXLONG
+ //
+ fieldb = mIBY64[m[i][1]];
+
+ // bitb = m[i][1] % MAXLONG
+ //
+ bitb = m[i][1] & (MAXLONG - 1);
+
+ if ((b[fieldb] & mBitmask[bitb]) != 0)
+ {
+ s ^= 1;
+ }
+
+ }
+ }
+ }
+ fielda = mIBY64[k];
+ bita = k & (MAXLONG - 1);
+
+ if (s != 0)
+ {
+ c[fielda] ^= mBitmask[bita];
+ }
+
+ // Circular shift of x and y one bit to the right,
+ // respectively.
+
+ if (mLength > 1)
+ {
+
+ // Shift x.
+ //
+ old = (a[degf] & 1) == 1;
+
+ for (int i = degf - 1; i >= 0; i--)
+ {
+ now = (a[i] & 1) != 0;
+
+ a[i] = a[i] >>> 1;
+
+ if (old)
+ {
+ a[i] ^= TWOTOMAXLONGM1;
+ }
+
+ old = now;
+ }
+ a[degf] = a[degf] >>> 1;
+
+ if (old)
+ {
+ a[degf] ^= TWOTODEGB;
+ }
+
+ // Shift y.
+ //
+ old = (b[degf] & 1) == 1;
+
+ for (int i = degf - 1; i >= 0; i--)
+ {
+ now = (b[i] & 1) != 0;
+
+ b[i] = b[i] >>> 1;
+
+ if (old)
+ {
+ b[i] ^= TWOTOMAXLONGM1;
+ }
+
+ old = now;
+ }
+
+ b[degf] = b[degf] >>> 1;
+
+ if (old)
+ {
+ b[degf] ^= TWOTODEGB;
+ }
+ }
+ else
+ {
+ old = (a[0] & 1) == 1;
+ a[0] = a[0] >>> 1;
+
+ if (old)
+ {
+ a[0] ^= TWOTODEGB;
+ }
+
+ old = (b[0] & 1) == 1;
+ b[0] = b[0] >>> 1;
+
+ if (old)
+ {
+ b[0] ^= TWOTODEGB;
+ }
+ }
+ }
+ assign(c);
+ }
+ }
+
+ /**
+ * returns <tt>this</tt> element to the power of 2.
+ *
+ * @return <tt>this</tt><sup>2</sup>
+ */
+ public GF2nElement square()
+ {
+ GF2nONBElement result = new GF2nONBElement(this);
+ result.squareThis();
+ return result;
+ }
+
+ /**
+ * squares <tt>this</tt> element.
+ */
+ public void squareThis()
+ {
+
+ long[] pol = getElement();
+
+ int f = mLength - 1;
+ int b = mBit - 1;
+
+ // Shift the coefficients one bit to the left.
+ //
+ long TWOTOMAXLONGM1 = mBitmask[MAXLONG - 1];
+ boolean old, now;
+
+ old = (pol[f] & mBitmask[b]) != 0;
+
+ for (int i = 0; i < f; i++)
+ {
+
+ now = (pol[i] & TWOTOMAXLONGM1) != 0;
+
+ pol[i] = pol[i] << 1;
+
+ if (old)
+ {
+ pol[i] ^= 1;
+ }
+
+ old = now;
+ }
+ now = (pol[f] & mBitmask[b]) != 0;
+
+ pol[f] = pol[f] << 1;
+
+ if (old)
+ {
+ pol[f] ^= 1;
+ }
+
+ // Set the bit with index mDegree to zero.
+ //
+ if (now)
+ {
+ pol[f] ^= mBitmask[b + 1];
+ }
+
+ assign(pol);
+ }
+
+ /**
+ * Compute the multiplicative inverse of this element.
+ *
+ * @return <tt>this<sup>-1</sup></tt> (newly created)
+ * @throws ArithmeticException if <tt>this</tt> is the zero element.
+ */
+ public GFElement invert()
+ throws ArithmeticException
+ {
+ GF2nONBElement result = new GF2nONBElement(this);
+ result.invertThis();
+ return result;
+ }
+
+ /**
+ * Multiplicatively invert of this element (overwrite <tt>this</tt>).
+ *
+ * @throws ArithmeticException if <tt>this</tt> is the zero element.
+ */
+ public void invertThis()
+ throws ArithmeticException
+ {
+
+ if (isZero())
+ {
+ throw new ArithmeticException();
+ }
+ int r = 31; // mDegree kann nur 31 Bits lang sein!!!
+
+ // Bitlaenge von mDegree:
+ for (boolean found = false; !found && r >= 0; r--)
+ {
+
+ if (((mDegree - 1) & mBitmask[r]) != 0)
+ {
+ found = true;
+ }
+ }
+ r++;
+
+ GF2nElement m = ZERO((GF2nONBField)mField);
+ GF2nElement n = new GF2nONBElement(this);
+
+ int k = 1;
+
+ for (int i = r - 1; i >= 0; i--)
+ {
+ m = (GF2nElement)n.clone();
+ for (int j = 1; j <= k; j++)
+ {
+ m.squareThis();
+ }
+
+ n.multiplyThisBy(m);
+
+ k <<= 1;
+ if (((mDegree - 1) & mBitmask[i]) != 0)
+ {
+ n.squareThis();
+
+ n.multiplyThisBy(this);
+
+ k++;
+ }
+ }
+ n.squareThis();
+ }
+
+ /**
+ * returns the root of<tt>this</tt> element.
+ *
+ * @return <tt>this</tt><sup>1/2</sup>
+ */
+ public GF2nElement squareRoot()
+ {
+ GF2nONBElement result = new GF2nONBElement(this);
+ result.squareRootThis();
+ return result;
+ }
+
+ /**
+ * square roots <tt>this</tt> element.
+ */
+ public void squareRootThis()
+ {
+
+ long[] pol = getElement();
+
+ int f = mLength - 1;
+ int b = mBit - 1;
+
+ // Shift the coefficients one bit to the right.
+ //
+ long TWOTOMAXLONGM1 = mBitmask[MAXLONG - 1];
+ boolean old, now;
+
+ old = (pol[0] & 1) != 0;
+
+ for (int i = f; i >= 0; i--)
+ {
+ now = (pol[i] & 1) != 0;
+ pol[i] = pol[i] >>> 1;
+
+ if (old)
+ {
+ if (i == f)
+ {
+ pol[i] ^= mBitmask[b];
+ }
+ else
+ {
+ pol[i] ^= TWOTOMAXLONGM1;
+ }
+ }
+ old = now;
+ }
+ assign(pol);
+ }
+
+ /**
+ * Returns the trace of this element.
+ *
+ * @return the trace of this element
+ */
+ public int trace()
+ {
+
+ // trace = sum of coefficients
+ //
+
+ int result = 0;
+
+ int max = mLength - 1;
+
+ for (int i = 0; i < max; i++)
+ {
+
+ for (int j = 0; j < MAXLONG; j++)
+ {
+
+ if ((mPol[i] & mBitmask[j]) != 0)
+ {
+ result ^= 1;
+ }
+ }
+ }
+
+ int b = mBit;
+
+ for (int j = 0; j < b; j++)
+ {
+
+ if ((mPol[max] & mBitmask[j]) != 0)
+ {
+ result ^= 1;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Solves a quadratic equation.<br>
+ * Let z<sup>2</sup> + z = <tt>this</tt>. Then this method returns z.
+ *
+ * @return z with z<sup>2</sup> + z = <tt>this</tt>
+ * @throws NoSolutionException if z<sup>2</sup> + z = <tt>this</tt> does not have a
+ * solution
+ */
+ public GF2nElement solveQuadraticEquation()
+ throws RuntimeException
+ {
+
+ if (trace() == 1)
+ {
+ throw new RuntimeException();
+ }
+
+ long TWOTOMAXLONGM1 = mBitmask[MAXLONG - 1];
+ long ZERO = 0L;
+ long ONE = 1L;
+
+ long[] p = new long[mLength];
+ long z = 0L;
+ int j = 1;
+ for (int i = 0; i < mLength - 1; i++)
+ {
+
+ for (j = 1; j < MAXLONG; j++)
+ {
+
+ //
+ if (!((((mBitmask[j] & mPol[i]) != ZERO) && ((z & mBitmask[j - 1]) != ZERO)) || (((mPol[i] & mBitmask[j]) == ZERO) && ((z & mBitmask[j - 1]) == ZERO))))
+ {
+ z ^= mBitmask[j];
+ }
+ }
+ p[i] = z;
+
+ if (((TWOTOMAXLONGM1 & z) != ZERO && (ONE & mPol[i + 1]) == ONE)
+ || ((TWOTOMAXLONGM1 & z) == ZERO && (ONE & mPol[i + 1]) == ZERO))
+ {
+ z = ZERO;
+ }
+ else
+ {
+ z = ONE;
+ }
+ }
+
+ int b = mDegree & (MAXLONG - 1);
+
+ long LASTLONG = mPol[mLength - 1];
+
+ for (j = 1; j < b; j++)
+ {
+ if (!((((mBitmask[j] & LASTLONG) != ZERO) && ((mBitmask[j - 1] & z) != ZERO)) || (((mBitmask[j] & LASTLONG) == ZERO) && ((mBitmask[j - 1] & z) == ZERO))))
+ {
+ z ^= mBitmask[j];
+ }
+ }
+ p[mLength - 1] = z;
+ return new GF2nONBElement((GF2nONBField)mField, p);
+ }
+
+ // /////////////////////////////////////////////////////////////////
+ // conversion
+ // /////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns a String representation of this element.
+ *
+ * @return String representation of this element with the specified radix
+ */
+ public String toString()
+ {
+ return toString(16);
+ }
+
+ /**
+ * Returns a String representation of this element. <tt>radix</tt>
+ * specifies the radix of the String representation.<br>
+ * NOTE: ONLY <tt>radix = 2</tt> or <tt>radix = 16</tt> IS IMPLEMENTED>
+ *
+ * @param radix specifies the radix of the String representation
+ * @return String representation of this element with the specified radix
+ */
+ public String toString(int radix)
+ {
+ String s = "";
+
+ long[] a = getElement();
+ int b = mBit;
+
+ if (radix == 2)
+ {
+
+ for (int j = b - 1; j >= 0; j--)
+ {
+ if ((a[a.length - 1] & ((long)1 << j)) == 0)
+ {
+ s += "0";
+ }
+ else
+ {
+ s += "1";
+ }
+ }
+
+ for (int i = a.length - 2; i >= 0; i--)
+ {
+ for (int j = MAXLONG - 1; j >= 0; j--)
+ {
+ if ((a[i] & mBitmask[j]) == 0)
+ {
+ s += "0";
+ }
+ else
+ {
+ s += "1";
+ }
+ }
+ }
+ }
+ else if (radix == 16)
+ {
+ final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ for (int i = a.length - 1; i >= 0; i--)
+ {
+ s += HEX_CHARS[(int)(a[i] >>> 60) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 56) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 52) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 48) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 44) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 40) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 36) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 32) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 28) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 24) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 20) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 16) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 12) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 8) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 4) & 0x0f];
+ s += HEX_CHARS[(int)(a[i]) & 0x0f];
+ s += " ";
+ }
+ }
+ return s;
+ }
+
+ /**
+ * Returns this element as FlexiBigInt. The conversion is <a href =
+ * "http://grouper.ieee.org/groups/1363/">P1363</a>-conform.
+ *
+ * @return this element as BigInteger
+ */
+ public BigInteger toFlexiBigInt()
+ {
+ /** @todo this method does not reverse the bit-order as it should!!! */
+
+ return new BigInteger(1, toByteArray());
+ }
+
+ /**
+ * Returns this element as byte array. The conversion is <a href =
+ * "http://grouper.ieee.org/groups/1363/">P1363</a>-conform.
+ *
+ * @return this element as byte array
+ */
+ public byte[] toByteArray()
+ {
+ /** @todo this method does not reverse the bit-order as it should!!! */
+
+ int k = ((mDegree - 1) >> 3) + 1;
+ byte[] result = new byte[k];
+ int i;
+ for (i = 0; i < k; i++)
+ {
+ result[k - i - 1] = (byte)((mPol[i >>> 3] & (0x00000000000000ffL << ((i & 0x07) << 3))) >>> ((i & 0x07) << 3));
+ }
+ return result;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nONBField.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nONBField.java
new file mode 100644
index 00000000..1e4c8b26
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nONBField.java
@@ -0,0 +1,546 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+
+import java.util.Random;
+import java.util.Vector;
+
+
+/**
+ * This class implements the abstract class <tt>GF2nField</tt> for ONB
+ * representation. It computes the fieldpolynomial, multiplication matrix and
+ * one of its roots mONBRoot, (see for example <a
+ * href=http://www2.certicom.com/ecc/intro.htm>Certicoms Whitepapers</a>).
+ * GF2nField is used by GF2nONBElement which implements the elements of this
+ * field.
+ *
+ * @see GF2nField
+ * @see GF2nONBElement
+ */
+public class GF2nONBField
+ extends GF2nField
+{
+
+ // ///////////////////////////////////////////////////////////////////
+ // Hashtable for irreducible normal polynomials //
+ // ///////////////////////////////////////////////////////////////////
+
+ // i*5 + 0 i*5 + 1 i*5 + 2 i*5 + 3 i*5 + 4
+ /*
+ * private static int[][] mNB = {{0, 0, 0}, {0, 0, 0}, {1, 0, 0}, {1, 0, 0},
+ * {1, 0, 0}, // i = 0 {2, 0, 0}, {1, 0, 0}, {1, 0, 0}, {4, 3, 1}, {1, 0,
+ * 0}, // i = 1 {3, 0, 0}, {2, 0, 0}, {3, 0, 0}, {4, 3, 1}, {5, 0, 0}, // i =
+ * 2 {1, 0, 0}, {5, 3, 1}, {3, 0, 0}, {3, 0, 0}, {5, 2, 1}, // i = 3 {3, 0,
+ * 0}, {2, 0, 0}, {1, 0, 0}, {5, 0, 0}, {4, 3, 1}, // i = 4 {3, 0, 0}, {4,
+ * 3, 1}, {5, 2, 1}, {1, 0, 0}, {2, 0, 0}, // i = 5 {1, 0, 0}, {3, 0, 0},
+ * {7, 3, 2}, {10, 0, 0}, {7, 0, 0}, // i = 6 {2, 0, 0}, {9, 0, 0}, {6, 4,
+ * 1}, {6, 5, 1}, {4, 0, 0}, // i = 7 {5, 4, 3}, {3, 0, 0}, {7, 0, 0}, {6,
+ * 4, 3}, {5, 0, 0}, // i = 8 {4, 3, 1}, {1, 0, 0}, {5, 0, 0}, {5, 3, 2},
+ * {9, 0, 0}, // i = 9 {4, 3, 2}, {6, 3, 1}, {3, 0, 0}, {6, 2, 1}, {9, 0,
+ * 0}, // i = 10 {7, 0, 0}, {7, 4, 2}, {4, 0, 0}, {19, 0, 0}, {7, 4, 2}, //
+ * i = 11 {1, 0, 0}, {5, 2, 1}, {29, 0, 0}, {1, 0, 0}, {4, 3, 1}, // i = 12
+ * {18, 0, 0}, {3, 0, 0}, {5, 2, 1}, {9, 0, 0}, {6, 5, 2}, // i = 13 {5, 3,
+ * 1}, {6, 0, 0}, {10, 9, 3}, {25, 0, 0}, {35, 0, 0}, // i = 14 {6, 3, 1},
+ * {21, 0, 0}, {6, 5, 2}, {6, 5, 3}, {9, 0, 0}, // i = 15 {9, 4, 2}, {4, 0,
+ * 0}, {8, 3, 1}, {7, 4, 2}, {5, 0, 0}, // i = 16 {8, 2, 1}, {21, 0, 0},
+ * {13, 0, 0}, {7, 6, 2}, {38, 0, 0}, // i = 17 {27, 0, 0}, {8, 5, 1}, {21,
+ * 0, 0}, {2, 0, 0}, {21, 0, 0}, // i = 18 {11, 0, 0}, {10, 9, 6}, {6, 0,
+ * 0}, {11, 0, 0}, {6, 3, 1}, // i = 19 {15, 0, 0}, {7, 6, 1}, {29, 0, 0},
+ * {9, 0, 0}, {4, 3, 1}, // i = 20 {4, 0, 0}, {15, 0, 0}, {9, 7, 4}, {17, 0,
+ * 0}, {5, 4, 2}, // i = 21 {33, 0, 0}, {10, 0, 0}, {5, 4, 3}, {9, 0, 0},
+ * {5, 3, 2}, // i = 22 {8, 7, 5}, {4, 2, 1}, {5, 2, 1}, {33, 0, 0}, {8, 0,
+ * 0}, // i = 23 {4, 3, 1}, {18, 0, 0}, {6, 2, 1}, {2, 0, 0}, {19, 0, 0}, //
+ * i = 24 {7, 6, 5}, {21, 0, 0}, {1, 0, 0}, {7, 2, 1}, {5, 0, 0}, // i = 25
+ * {3, 0, 0}, {8, 3, 2}, {17, 0, 0}, {9, 8, 2}, {57, 0, 0}, // i = 26 {11,
+ * 0, 0}, {5, 3, 2}, {21, 0, 0}, {8, 7, 1}, {8, 5, 3}, // i = 27 {15, 0, 0},
+ * {10, 4, 1}, {21, 0, 0}, {5, 3, 2}, {7, 4, 2}, // i = 28 {52, 0, 0}, {71,
+ * 0, 0}, {14, 0, 0}, {27, 0, 0}, {10, 9, 7}, // i = 29 {53, 0, 0}, {3, 0,
+ * 0}, {6, 3, 2}, {1, 0, 0}, {15, 0, 0}, // i = 30 {62, 0, 0}, {9, 0, 0},
+ * {6, 5, 2}, {8, 6, 5}, {31, 0, 0}, // i = 31 {5, 3, 2}, {18, 0, 0 }, {27,
+ * 0, 0}, {7, 6, 3}, {10, 8, 7}, // i = 32 {9, 8, 3}, {37, 0, 0}, {6, 0, 0},
+ * {15, 3, 2}, {34, 0, 0}, // i = 33 {11, 0, 0}, {6, 5, 2}, {1, 0, 0}, {8,
+ * 5, 2}, {13, 0, 0}, // i = 34 {6, 0, 0}, {11, 3, 2}, {8, 0, 0}, {31, 0,
+ * 0}, {4, 2, 1}, // i = 35 {3, 0, 0}, {7, 6, 1}, {81, 0, 0}, {56, 0, 0},
+ * {9, 8, 7}, // i = 36 {24, 0, 0}, {11, 0, 0}, {7, 6, 5}, {6, 5, 2}, {6, 5,
+ * 2}, // i = 37 {8, 7, 6}, {9, 0, 0}, {7, 2, 1}, {15, 0, 0}, {87, 0, 0}, //
+ * i = 38 {8, 3, 2}, {3, 0, 0}, {9, 4, 2}, {9, 0, 0}, {34, 0, 0}, // i = 39
+ * {5, 3, 2}, {14, 0, 0}, {55, 0, 0}, {8, 7, 1}, {27, 0, 0}, // i = 40 {9,
+ * 5, 2}, {10, 9, 5}, {43, 0, 0}, {8, 6, 2}, {6, 0, 0}, // i = 41 {7, 0, 0},
+ * {11, 10, 8}, {105, 0, 0}, {6, 5, 2}, {73, 0, 0}}; // i = 42
+ */
+ // /////////////////////////////////////////////////////////////////////
+ // member variables
+ // /////////////////////////////////////////////////////////////////////
+ private static final int MAXLONG = 64;
+
+ /**
+ * holds the length of the array-representation of degree mDegree.
+ */
+ private int mLength;
+
+ /**
+ * holds the number of relevant bits in mONBPol[mLength-1].
+ */
+ private int mBit;
+
+ /**
+ * holds the type of mONB
+ */
+ private int mType;
+
+ /**
+ * holds the multiplication matrix
+ */
+ int[][] mMult;
+
+ // /////////////////////////////////////////////////////////////////////
+ // constructors
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * constructs an instance of the finite field with 2<sup>deg</sup>
+ * elements and characteristic 2.
+ *
+ * @param deg -
+ * the extention degree of this field
+ * @throws NoSuchBasisException if an ONB-implementation other than type 1 or type 2 is
+ * requested.
+ */
+ public GF2nONBField(int deg)
+ throws RuntimeException
+ {
+ if (deg < 3)
+ {
+ throw new IllegalArgumentException("k must be at least 3");
+ }
+
+ mDegree = deg;
+ mLength = mDegree / MAXLONG;
+ mBit = mDegree & (MAXLONG - 1);
+ if (mBit == 0)
+ {
+ mBit = MAXLONG;
+ }
+ else
+ {
+ mLength++;
+ }
+
+ computeType();
+
+ // only ONB-implementations for type 1 and type 2
+ //
+ if (mType < 3)
+ {
+ mMult = new int[mDegree][2];
+ for (int i = 0; i < mDegree; i++)
+ {
+ mMult[i][0] = -1;
+ mMult[i][1] = -1;
+ }
+ computeMultMatrix();
+ }
+ else
+ {
+ throw new RuntimeException("\nThe type of this field is "
+ + mType);
+ }
+ computeFieldPolynomial();
+ fields = new Vector();
+ matrices = new Vector();
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // access
+ // /////////////////////////////////////////////////////////////////////
+
+ int getONBLength()
+ {
+ return mLength;
+ }
+
+ int getONBBit()
+ {
+ return mBit;
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // arithmetic
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Computes a random root of the given polynomial.
+ *
+ * @param polynomial a polynomial
+ * @return a random root of the polynomial
+ * @see "P1363 A.5.6, p103f"
+ */
+ protected GF2nElement getRandomRoot(GF2Polynomial polynomial)
+ {
+ // We are in B1!!!
+ GF2nPolynomial c;
+ GF2nPolynomial ut;
+ GF2nElement u;
+ GF2nPolynomial h;
+ int hDegree;
+ // 1. Set g(t) <- f(t)
+ GF2nPolynomial g = new GF2nPolynomial(polynomial, this);
+ int gDegree = g.getDegree();
+ int i;
+
+ // 2. while deg(g) > 1
+ while (gDegree > 1)
+ {
+ do
+ {
+ // 2.1 choose random u (element of) GF(2^m)
+ u = new GF2nONBElement(this, new Random());
+ ut = new GF2nPolynomial(2, GF2nONBElement.ZERO(this));
+ // 2.2 Set c(t) <- ut
+ ut.set(1, u);
+ c = new GF2nPolynomial(ut);
+ // 2.3 For i from 1 to m-1 do
+ for (i = 1; i <= mDegree - 1; i++)
+ {
+ // 2.3.1 c(t) <- (c(t)^2 + ut) mod g(t)
+ c = c.multiplyAndReduce(c, g);
+ c = c.add(ut);
+ }
+ // 2.4 set h(t) <- GCD(c(t), g(t))
+ h = c.gcd(g);
+ // 2.5 if h(t) is constant or deg(g) = deg(h) then go to
+ // step 2.1
+ hDegree = h.getDegree();
+ gDegree = g.getDegree();
+ }
+ while ((hDegree == 0) || (hDegree == gDegree));
+ // 2.6 If 2deg(h) > deg(g) then set g(t) <- g(t)/h(t) ...
+ if ((hDegree << 1) > gDegree)
+ {
+ g = g.quotient(h);
+ }
+ else
+ {
+ // ... else g(t) <- h(t)
+ g = new GF2nPolynomial(h);
+ }
+ gDegree = g.getDegree();
+ }
+ // 3. Output g(0)
+ return g.at(0);
+
+ }
+
+ /**
+ * Computes the change-of-basis matrix for basis conversion according to
+ * 1363. The result is stored in the lists fields and matrices.
+ *
+ * @param B1 the GF2nField to convert to
+ * @see "P1363 A.7.3, p111ff"
+ */
+ protected void computeCOBMatrix(GF2nField B1)
+ {
+ // we are in B0 here!
+ if (mDegree != B1.mDegree)
+ {
+ throw new IllegalArgumentException(
+ "GF2nField.computeCOBMatrix: B1 has a "
+ + "different degree and thus cannot be coverted to!");
+ }
+ int i, j;
+ GF2nElement[] gamma;
+ GF2nElement u;
+ GF2Polynomial[] COBMatrix = new GF2Polynomial[mDegree];
+ for (i = 0; i < mDegree; i++)
+ {
+ COBMatrix[i] = new GF2Polynomial(mDegree);
+ }
+
+ // find Random Root
+ do
+ {
+ // u is in representation according to B1
+ u = B1.getRandomRoot(fieldPolynomial);
+ }
+ while (u.isZero());
+
+ gamma = new GF2nPolynomialElement[mDegree];
+ // build gamma matrix by squaring
+ gamma[0] = (GF2nElement)u.clone();
+ for (i = 1; i < mDegree; i++)
+ {
+ gamma[i] = gamma[i - 1].square();
+ }
+ // convert horizontal gamma matrix by vertical Bitstrings
+ for (i = 0; i < mDegree; i++)
+ {
+ for (j = 0; j < mDegree; j++)
+ {
+ if (gamma[i].testBit(j))
+ {
+ COBMatrix[mDegree - j - 1].setBit(mDegree - i - 1);
+ }
+ }
+ }
+
+ fields.addElement(B1);
+ matrices.addElement(COBMatrix);
+ B1.fields.addElement(this);
+ B1.matrices.addElement(invertMatrix(COBMatrix));
+ }
+
+ /**
+ * Computes the field polynomial for a ONB according to IEEE 1363 A.7.2
+ * (p110f).
+ *
+ * @see "P1363 A.7.2, p110f"
+ */
+ protected void computeFieldPolynomial()
+ {
+ if (mType == 1)
+ {
+ fieldPolynomial = new GF2Polynomial(mDegree + 1, "ALL");
+ }
+ else if (mType == 2)
+ {
+ // 1. q = 1
+ GF2Polynomial q = new GF2Polynomial(mDegree + 1, "ONE");
+ // 2. p = t+1
+ GF2Polynomial p = new GF2Polynomial(mDegree + 1, "X");
+ p.addToThis(q);
+ GF2Polynomial r;
+ int i;
+ // 3. for i = 1 to (m-1) do
+ for (i = 1; i < mDegree; i++)
+ {
+ // r <- q
+ r = q;
+ // q <- p
+ q = p;
+ // p = tq+r
+ p = q.shiftLeft();
+ p.addToThis(r);
+ }
+ fieldPolynomial = p;
+ }
+ }
+
+ /**
+ * Compute the inverse of a matrix <tt>a</tt>.
+ *
+ * @param a the matrix
+ * @return <tt>a<sup>-1</sup></tt>
+ */
+ int[][] invMatrix(int[][] a)
+ {
+
+ int[][] A = new int[mDegree][mDegree];
+ A = a;
+ int[][] inv = new int[mDegree][mDegree];
+
+ for (int i = 0; i < mDegree; i++)
+ {
+ inv[i][i] = 1;
+ }
+
+ for (int i = 0; i < mDegree; i++)
+ {
+ for (int j = i; j < mDegree; j++)
+ {
+ A[mDegree - 1 - i][j] = A[i][i];
+ }
+ }
+ return null;
+ }
+
+ private void computeType()
+ throws RuntimeException
+ {
+ if ((mDegree & 7) == 0)
+ {
+ throw new RuntimeException(
+ "The extension degree is divisible by 8!");
+ }
+ // checking for the type
+ int s = 0;
+ int k = 0;
+ mType = 1;
+ for (int d = 0; d != 1; mType++)
+ {
+ s = mType * mDegree + 1;
+ if (IntegerFunctions.isPrime(s))
+ {
+ k = IntegerFunctions.order(2, s);
+ d = IntegerFunctions.gcd(mType * mDegree / k, mDegree);
+ }
+ }
+ mType--;
+ if (mType == 1)
+ {
+ s = (mDegree << 1) + 1;
+ if (IntegerFunctions.isPrime(s))
+ {
+ k = IntegerFunctions.order(2, s);
+ int d = IntegerFunctions.gcd((mDegree << 1) / k, mDegree);
+ if (d == 1)
+ {
+ mType++;
+ }
+ }
+ }
+ }
+
+ private void computeMultMatrix()
+ {
+
+ if ((mType & 7) != 0)
+ {
+ int p = mType * mDegree + 1;
+
+ // compute sequence F[1] ... F[p-1] via A.3.7. of 1363.
+ // F[0] will not be filled!
+ //
+ int[] F = new int[p];
+
+ int u;
+ if (mType == 1)
+ {
+ u = 1;
+ }
+ else if (mType == 2)
+ {
+ u = p - 1;
+ }
+ else
+ {
+ u = elementOfOrder(mType, p);
+ }
+
+ int w = 1;
+ int n;
+ for (int j = 0; j < mType; j++)
+ {
+ n = w;
+
+ for (int i = 0; i < mDegree; i++)
+ {
+ F[n] = i;
+ n = (n << 1) % p;
+ if (n < 0)
+ {
+ n += p;
+ }
+ }
+ w = u * w % p;
+ if (w < 0)
+ {
+ w += p;
+ }
+ }
+
+ // building the matrix (mDegree * 2)
+ //
+ if (mType == 1)
+ {
+ for (int k = 1; k < p - 1; k++)
+ {
+ if (mMult[F[k + 1]][0] == -1)
+ {
+ mMult[F[k + 1]][0] = F[p - k];
+ }
+ else
+ {
+ mMult[F[k + 1]][1] = F[p - k];
+ }
+ }
+
+ int m_2 = mDegree >> 1;
+ for (int k = 1; k <= m_2; k++)
+ {
+
+ if (mMult[k - 1][0] == -1)
+ {
+ mMult[k - 1][0] = m_2 + k - 1;
+ }
+ else
+ {
+ mMult[k - 1][1] = m_2 + k - 1;
+ }
+
+ if (mMult[m_2 + k - 1][0] == -1)
+ {
+ mMult[m_2 + k - 1][0] = k - 1;
+ }
+ else
+ {
+ mMult[m_2 + k - 1][1] = k - 1;
+ }
+ }
+ }
+ else if (mType == 2)
+ {
+ for (int k = 1; k < p - 1; k++)
+ {
+ if (mMult[F[k + 1]][0] == -1)
+ {
+ mMult[F[k + 1]][0] = F[p - k];
+ }
+ else
+ {
+ mMult[F[k + 1]][1] = F[p - k];
+ }
+ }
+ }
+ else
+ {
+ throw new RuntimeException("only type 1 or type 2 implemented");
+ }
+ }
+ else
+ {
+ throw new RuntimeException("bisher nur fuer Gausssche Normalbasen"
+ + " implementiert");
+ }
+ }
+
+ private int elementOfOrder(int k, int p)
+ {
+ Random random = new Random();
+ int m = 0;
+ while (m == 0)
+ {
+ m = random.nextInt();
+ m %= p - 1;
+ if (m < 0)
+ {
+ m += p - 1;
+ }
+ }
+
+ int l = IntegerFunctions.order(m, p);
+
+ while (l % k != 0 || l == 0)
+ {
+ while (m == 0)
+ {
+ m = random.nextInt();
+ m %= p - 1;
+ if (m < 0)
+ {
+ m += p - 1;
+ }
+ }
+ l = IntegerFunctions.order(m, p);
+ }
+ int r = m;
+
+ l = k / l;
+
+ for (int i = 2; i <= l; i++)
+ {
+ r *= m;
+ }
+
+ return r;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomial.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomial.java
new file mode 100644
index 00000000..f122be03
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomial.java
@@ -0,0 +1,587 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+
+/**
+ * This class implements polynomials over GF2nElements.
+ *
+ * @see GF2nElement
+ */
+
+public class GF2nPolynomial
+{
+
+ private GF2nElement[] coeff; // keeps the coefficients of this polynomial
+
+ private int size; // the size of this polynomial
+
+ /**
+ * Creates a new PolynomialGF2n of size <i>deg</i> and elem as
+ * coefficients.
+ *
+ * @param deg -
+ * the maximum degree + 1
+ * @param elem -
+ * a GF2nElement
+ */
+ public GF2nPolynomial(int deg, GF2nElement elem)
+ {
+ size = deg;
+ coeff = new GF2nElement[size];
+ for (int i = 0; i < size; i++)
+ {
+ coeff[i] = (GF2nElement)elem.clone();
+ }
+ }
+
+ /**
+ * Creates a new PolynomialGF2n of size <i>deg</i>.
+ *
+ * @param deg the maximum degree + 1
+ */
+ private GF2nPolynomial(int deg)
+ {
+ size = deg;
+ coeff = new GF2nElement[size];
+ }
+
+ /**
+ * Creates a new PolynomialGF2n by cloning the given PolynomialGF2n <i>a</i>.
+ *
+ * @param a the PolynomialGF2n to clone
+ */
+ public GF2nPolynomial(GF2nPolynomial a)
+ {
+ int i;
+ coeff = new GF2nElement[a.size];
+ size = a.size;
+ for (i = 0; i < size; i++)
+ {
+ coeff[i] = (GF2nElement)a.coeff[i].clone();
+ }
+ }
+
+ /**
+ * Creates a new PolynomialGF2n from the given Bitstring <i>polynomial</i>
+ * over the GF2nField <i>B1</i>.
+ *
+ * @param polynomial the Bitstring to use
+ * @param B1 the field
+ */
+ public GF2nPolynomial(GF2Polynomial polynomial, GF2nField B1)
+ {
+ size = B1.getDegree() + 1;
+ coeff = new GF2nElement[size];
+ int i;
+ if (B1 instanceof GF2nONBField)
+ {
+ for (i = 0; i < size; i++)
+ {
+ if (polynomial.testBit(i))
+ {
+ coeff[i] = GF2nONBElement.ONE((GF2nONBField)B1);
+ }
+ else
+ {
+ coeff[i] = GF2nONBElement.ZERO((GF2nONBField)B1);
+ }
+ }
+ }
+ else if (B1 instanceof GF2nPolynomialField)
+ {
+ for (i = 0; i < size; i++)
+ {
+ if (polynomial.testBit(i))
+ {
+ coeff[i] = GF2nPolynomialElement
+ .ONE((GF2nPolynomialField)B1);
+ }
+ else
+ {
+ coeff[i] = GF2nPolynomialElement
+ .ZERO((GF2nPolynomialField)B1);
+ }
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "PolynomialGF2n(Bitstring, GF2nField): B1 must be "
+ + "an instance of GF2nONBField or GF2nPolynomialField!");
+ }
+ }
+
+ public final void assignZeroToElements()
+ {
+ int i;
+ for (i = 0; i < size; i++)
+ {
+ coeff[i].assignZero();
+ }
+ }
+
+ /**
+ * Returns the size (=maximum degree + 1) of this PolynomialGF2n. This is
+ * not the degree, use getDegree instead.
+ *
+ * @return the size (=maximum degree + 1) of this PolynomialGF2n.
+ */
+ public final int size()
+ {
+ return size;
+ }
+
+ /**
+ * Returns the degree of this PolynomialGF2n.
+ *
+ * @return the degree of this PolynomialGF2n.
+ */
+ public final int getDegree()
+ {
+ int i;
+ for (i = size - 1; i >= 0; i--)
+ {
+ if (!coeff[i].isZero())
+ {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Enlarges the size of this PolynomialGF2n to <i>k</i> + 1.
+ *
+ * @param k the new maximum degree
+ */
+ public final void enlarge(int k)
+ {
+ if (k <= size)
+ {
+ return;
+ }
+ int i;
+ GF2nElement[] res = new GF2nElement[k];
+ System.arraycopy(coeff, 0, res, 0, size);
+ GF2nField f = coeff[0].getField();
+ if (coeff[0] instanceof GF2nPolynomialElement)
+ {
+ for (i = size; i < k; i++)
+ {
+ res[i] = GF2nPolynomialElement.ZERO((GF2nPolynomialField)f);
+ }
+ }
+ else if (coeff[0] instanceof GF2nONBElement)
+ {
+ for (i = size; i < k; i++)
+ {
+ res[i] = GF2nONBElement.ZERO((GF2nONBField)f);
+ }
+ }
+ size = k;
+ coeff = res;
+ }
+
+ public final void shrink()
+ {
+ int i = size - 1;
+ while (coeff[i].isZero() && (i > 0))
+ {
+ i--;
+ }
+ i++;
+ if (i < size)
+ {
+ GF2nElement[] res = new GF2nElement[i];
+ System.arraycopy(coeff, 0, res, 0, i);
+ coeff = res;
+ size = i;
+ }
+ }
+
+ /**
+ * Sets the coefficient at <i>index</i> to <i>elem</i>.
+ *
+ * @param index the index
+ * @param elem the GF2nElement to store as coefficient <i>index</i>
+ */
+ public final void set(int index, GF2nElement elem)
+ {
+ if (!(elem instanceof GF2nPolynomialElement)
+ && !(elem instanceof GF2nONBElement))
+ {
+ throw new IllegalArgumentException(
+ "PolynomialGF2n.set f must be an "
+ + "instance of either GF2nPolynomialElement or GF2nONBElement!");
+ }
+ coeff[index] = (GF2nElement)elem.clone();
+ }
+
+ /**
+ * Returns the coefficient at <i>index</i>.
+ *
+ * @param index the index
+ * @return the GF2nElement stored as coefficient <i>index</i>
+ */
+ public final GF2nElement at(int index)
+ {
+ return coeff[index];
+ }
+
+ /**
+ * Returns true if all coefficients equal zero.
+ *
+ * @return true if all coefficients equal zero.
+ */
+ public final boolean isZero()
+ {
+ int i;
+ for (i = 0; i < size; i++)
+ {
+ if (coeff[i] != null)
+ {
+ if (!coeff[i].isZero())
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public final boolean equals(Object other)
+ {
+ if (other == null || !(other instanceof GF2nPolynomial))
+ {
+ return false;
+ }
+
+ GF2nPolynomial otherPol = (GF2nPolynomial)other;
+
+ if (getDegree() != otherPol.getDegree())
+ {
+ return false;
+ }
+ int i;
+ for (i = 0; i < size; i++)
+ {
+ if (!coeff[i].equals(otherPol.coeff[i]))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @return the hash code of this polynomial
+ */
+ public int hashCode()
+ {
+ return getDegree() + coeff.hashCode();
+ }
+
+ /**
+ * Adds the PolynomialGF2n <tt>b</tt> to <tt>this</tt> and returns the
+ * result in a new <tt>PolynomialGF2n</tt>.
+ *
+ * @param b -
+ * the <tt>PolynomialGF2n</tt> to add
+ * @return <tt>this + b</tt>
+ * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over
+ * the same field.
+ */
+ public final GF2nPolynomial add(GF2nPolynomial b)
+ throws RuntimeException
+ {
+ GF2nPolynomial result;
+ if (size() >= b.size())
+ {
+ result = new GF2nPolynomial(size());
+ int i;
+ for (i = 0; i < b.size(); i++)
+ {
+ result.coeff[i] = (GF2nElement)coeff[i].add(b.coeff[i]);
+ }
+ for (; i < size(); i++)
+ {
+ result.coeff[i] = coeff[i];
+ }
+ }
+ else
+ {
+ result = new GF2nPolynomial(b.size());
+ int i;
+ for (i = 0; i < size(); i++)
+ {
+ result.coeff[i] = (GF2nElement)coeff[i].add(b.coeff[i]);
+ }
+ for (; i < b.size(); i++)
+ {
+ result.coeff[i] = b.coeff[i];
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Multiplies the scalar <i>s</i> to each coefficient of this
+ * PolynomialGF2n and returns the result in a new PolynomialGF2n.
+ *
+ * @param s the scalar to multiply
+ * @return <i>this</i> x <i>s</i>
+ * @throws DifferentFieldsException if <tt>this</tt> and <tt>s</tt> are not defined over
+ * the same field.
+ */
+ public final GF2nPolynomial scalarMultiply(GF2nElement s)
+ throws RuntimeException
+ {
+ GF2nPolynomial result = new GF2nPolynomial(size());
+ int i;
+ for (i = 0; i < size(); i++)
+ {
+ result.coeff[i] = (GF2nElement)coeff[i].multiply(s); // result[i]
+ // =
+ // a[i]*s
+ }
+ return result;
+ }
+
+ /**
+ * Multiplies <i>this</i> by <i>b</i> and returns the result in a new
+ * PolynomialGF2n.
+ *
+ * @param b the PolynomialGF2n to multiply
+ * @return <i>this</i> * <i>b</i>
+ * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over
+ * the same field.
+ */
+ public final GF2nPolynomial multiply(GF2nPolynomial b)
+ throws RuntimeException
+ {
+ int i, j;
+ int aDegree = size();
+ int bDegree = b.size();
+ if (aDegree != bDegree)
+ {
+ throw new IllegalArgumentException(
+ "PolynomialGF2n.multiply: this and b must "
+ + "have the same size!");
+ }
+ GF2nPolynomial result = new GF2nPolynomial((aDegree << 1) - 1);
+ for (i = 0; i < size(); i++)
+ {
+ for (j = 0; j < b.size(); j++)
+ {
+ if (result.coeff[i + j] == null)
+ {
+ result.coeff[i + j] = (GF2nElement)coeff[i]
+ .multiply(b.coeff[j]);
+ }
+ else
+ {
+ result.coeff[i + j] = (GF2nElement)result.coeff[i + j]
+ .add(coeff[i].multiply(b.coeff[j]));
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Multiplies <i>this</i> by <i>b</i>, reduces the result by <i>g</i> and
+ * returns it in a new PolynomialGF2n.
+ *
+ * @param b the PolynomialGF2n to multiply
+ * @param g the modul
+ * @return <i>this</i> * <i>b</i> mod <i>g</i>
+ * @throws DifferentFieldsException if <tt>this</tt>, <tt>b</tt> and <tt>g</tt> are
+ * not all defined over the same field.
+ */
+ public final GF2nPolynomial multiplyAndReduce(GF2nPolynomial b,
+ GF2nPolynomial g)
+ throws RuntimeException,
+ ArithmeticException
+ {
+ return multiply(b).reduce(g);
+ }
+
+ /**
+ * Reduces <i>this</i> by <i>g</i> and returns the result in a new
+ * PolynomialGF2n.
+ *
+ * @param g -
+ * the modulus
+ * @return <i>this</i> % <i>g</i>
+ * @throws DifferentFieldsException if <tt>this</tt> and <tt>g</tt> are not defined over
+ * the same field.
+ */
+ public final GF2nPolynomial reduce(GF2nPolynomial g)
+ throws RuntimeException, ArithmeticException
+ {
+ return remainder(g); // return this % g
+ }
+
+ /**
+ * Shifts left <i>this</i> by <i>amount</i> and stores the result in
+ * <i>this</i> PolynomialGF2n.
+ *
+ * @param amount the amount to shift the coefficients
+ */
+ public final void shiftThisLeft(int amount)
+ {
+ if (amount > 0)
+ {
+ int i;
+ int oldSize = size;
+ GF2nField f = coeff[0].getField();
+ enlarge(size + amount);
+ for (i = oldSize - 1; i >= 0; i--)
+ {
+ coeff[i + amount] = coeff[i];
+ }
+ if (coeff[0] instanceof GF2nPolynomialElement)
+ {
+ for (i = amount - 1; i >= 0; i--)
+ {
+ coeff[i] = GF2nPolynomialElement
+ .ZERO((GF2nPolynomialField)f);
+ }
+ }
+ else if (coeff[0] instanceof GF2nONBElement)
+ {
+ for (i = amount - 1; i >= 0; i--)
+ {
+ coeff[i] = GF2nONBElement.ZERO((GF2nONBField)f);
+ }
+ }
+ }
+ }
+
+ public final GF2nPolynomial shiftLeft(int amount)
+ {
+ if (amount <= 0)
+ {
+ return new GF2nPolynomial(this);
+ }
+ GF2nPolynomial result = new GF2nPolynomial(size + amount, coeff[0]);
+ result.assignZeroToElements();
+ for (int i = 0; i < size; i++)
+ {
+ result.coeff[i + amount] = coeff[i];
+ }
+ return result;
+ }
+
+ /**
+ * Divides <i>this</i> by <i>b</i> and stores the result in a new
+ * PolynomialGF2n[2], quotient in result[0] and remainder in result[1].
+ *
+ * @param b the divisor
+ * @return the quotient and remainder of <i>this</i> / <i>b</i>
+ * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over
+ * the same field.
+ */
+ public final GF2nPolynomial[] divide(GF2nPolynomial b)
+ throws RuntimeException, ArithmeticException
+ {
+ GF2nPolynomial[] result = new GF2nPolynomial[2];
+ GF2nPolynomial a = new GF2nPolynomial(this);
+ a.shrink();
+ GF2nPolynomial shift;
+ GF2nElement factor;
+ int bDegree = b.getDegree();
+ GF2nElement inv = (GF2nElement)b.coeff[bDegree].invert();
+ if (a.getDegree() < bDegree)
+ {
+ result[0] = new GF2nPolynomial(this);
+ result[0].assignZeroToElements();
+ result[0].shrink();
+ result[1] = new GF2nPolynomial(this);
+ result[1].shrink();
+ return result;
+ }
+ result[0] = new GF2nPolynomial(this);
+ result[0].assignZeroToElements();
+ int i = a.getDegree() - bDegree;
+ while (i >= 0)
+ {
+ factor = (GF2nElement)a.coeff[a.getDegree()].multiply(inv);
+ shift = b.scalarMultiply(factor);
+ shift.shiftThisLeft(i);
+ a = a.add(shift);
+ a.shrink();
+ result[0].coeff[i] = (GF2nElement)factor.clone();
+ i = a.getDegree() - bDegree;
+ }
+ result[1] = a;
+ result[0].shrink();
+ return result;
+ }
+
+ /**
+ * Divides <i>this</i> by <i>b</i> and stores the remainder in a new
+ * PolynomialGF2n.
+ *
+ * @param b the divisor
+ * @return the remainder <i>this</i> % <i>b</i>
+ * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over
+ * the same field.
+ */
+ public final GF2nPolynomial remainder(GF2nPolynomial b)
+ throws RuntimeException, ArithmeticException
+ {
+ GF2nPolynomial[] result = new GF2nPolynomial[2];
+ result = divide(b);
+ return result[1];
+ }
+
+ /**
+ * Divides <i>this</i> by <i>b</i> and stores the quotient in a new
+ * PolynomialGF2n.
+ *
+ * @param b the divisor
+ * @return the quotient <i>this</i> / <i>b</i>
+ * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over
+ * the same field.
+ */
+ public final GF2nPolynomial quotient(GF2nPolynomial b)
+ throws RuntimeException, ArithmeticException
+ {
+ GF2nPolynomial[] result = new GF2nPolynomial[2];
+ result = divide(b);
+ return result[0];
+ }
+
+ /**
+ * Computes the greatest common divisor of <i>this</i> and <i>g</i> and
+ * returns the result in a new PolynomialGF2n.
+ *
+ * @param g -
+ * a GF2nPolynomial
+ * @return gcd(<i>this</i>, <i>g</i>)
+ * @throws DifferentFieldsException if the coefficients of <i>this</i> and <i>g</i> use
+ * different fields
+ * @throws ArithmeticException if coefficients are zero.
+ */
+ public final GF2nPolynomial gcd(GF2nPolynomial g)
+ throws RuntimeException, ArithmeticException
+ {
+ GF2nPolynomial a = new GF2nPolynomial(this);
+ GF2nPolynomial b = new GF2nPolynomial(g);
+ a.shrink();
+ b.shrink();
+ GF2nPolynomial c;
+ GF2nPolynomial result;
+ GF2nElement alpha;
+ while (!b.isZero())
+ {
+ c = a.remainder(b);
+ a = b;
+ b = c;
+ }
+ alpha = a.coeff[a.getDegree()];
+ result = a.scalarMultiply((GF2nElement)alpha.invert());
+ return result;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomialElement.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomialElement.java
new file mode 100644
index 00000000..f1753653
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomialElement.java
@@ -0,0 +1,1021 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+
+import java.math.BigInteger;
+import java.util.Random;
+
+
+/**
+ * This class implements elements of finite binary fields <i>GF(2<sup>n</sup>)</i>
+ * using polynomial representation. For more information on the arithmetic see
+ * for example IEEE Standard 1363 or <a
+ * href=http://www.certicom.com/research/online.html> Certicom online-tutorial</a>.
+ *
+ * @see "GF2nField"
+ * @see GF2nPolynomialField
+ * @see GF2nONBElement
+ * @see GF2Polynomial
+ */
+public class GF2nPolynomialElement
+ extends GF2nElement
+{
+
+ // pre-computed Bitmask for fast masking, bitMask[a]=0x1 << a
+ private static final int[] bitMask = {0x00000001, 0x00000002, 0x00000004,
+ 0x00000008, 0x00000010, 0x00000020, 0x00000040, 0x00000080,
+ 0x00000100, 0x00000200, 0x00000400, 0x00000800, 0x00001000,
+ 0x00002000, 0x00004000, 0x00008000, 0x00010000, 0x00020000,
+ 0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000,
+ 0x00800000, 0x01000000, 0x02000000, 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x00000000};
+
+ // the used GF2Polynomial which stores the coefficients
+ private GF2Polynomial polynomial;
+
+ /**
+ * Create a new random GF2nPolynomialElement using the given field and
+ * source of randomness.
+ *
+ * @param f the GF2nField to use
+ * @param rand the source of randomness
+ */
+ public GF2nPolynomialElement(GF2nPolynomialField f, Random rand)
+ {
+ mField = f;
+ mDegree = mField.getDegree();
+ polynomial = new GF2Polynomial(mDegree);
+ randomize(rand);
+ }
+
+ /**
+ * Creates a new GF2nPolynomialElement using the given field and Bitstring.
+ *
+ * @param f the GF2nPolynomialField to use
+ * @param bs the desired value as Bitstring
+ */
+ public GF2nPolynomialElement(GF2nPolynomialField f, GF2Polynomial bs)
+ {
+ mField = f;
+ mDegree = mField.getDegree();
+ polynomial = new GF2Polynomial(bs);
+ polynomial.expandN(mDegree);
+ }
+
+ /**
+ * Creates a new GF2nPolynomialElement using the given field <i>f</i> and
+ * byte[] <i>os</i> as value. The conversion is done according to 1363.
+ *
+ * @param f the GF2nField to use
+ * @param os the octet string to assign to this GF2nPolynomialElement
+ * @see "P1363 5.5.5 p23, OS2FEP/OS2BSP"
+ */
+ public GF2nPolynomialElement(GF2nPolynomialField f, byte[] os)
+ {
+ mField = f;
+ mDegree = mField.getDegree();
+ polynomial = new GF2Polynomial(mDegree, os);
+ polynomial.expandN(mDegree);
+ }
+
+ /**
+ * Creates a new GF2nPolynomialElement using the given field <i>f</i> and
+ * int[] <i>is</i> as value.
+ *
+ * @param f the GF2nField to use
+ * @param is the integer string to assign to this GF2nPolynomialElement
+ */
+ public GF2nPolynomialElement(GF2nPolynomialField f, int[] is)
+ {
+ mField = f;
+ mDegree = mField.getDegree();
+ polynomial = new GF2Polynomial(mDegree, is);
+ polynomial.expandN(f.mDegree);
+ }
+
+ /**
+ * Creates a new GF2nPolynomialElement by cloning the given
+ * GF2nPolynomialElement <i>b</i>.
+ *
+ * @param other the GF2nPolynomialElement to clone
+ */
+ public GF2nPolynomialElement(GF2nPolynomialElement other)
+ {
+ mField = other.mField;
+ mDegree = other.mDegree;
+ polynomial = new GF2Polynomial(other.polynomial);
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // pseudo-constructors
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Creates a new GF2nPolynomialElement by cloning this
+ * GF2nPolynomialElement.
+ *
+ * @return a copy of this element
+ */
+ public Object clone()
+ {
+ return new GF2nPolynomialElement(this);
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // assignments
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Assigns the value 'zero' to this Polynomial.
+ */
+ void assignZero()
+ {
+ polynomial.assignZero();
+ }
+
+ /**
+ * Create the zero element.
+ *
+ * @param f the finite field
+ * @return the zero element in the given finite field
+ */
+ public static GF2nPolynomialElement ZERO(GF2nPolynomialField f)
+ {
+ GF2Polynomial polynomial = new GF2Polynomial(f.getDegree());
+ return new GF2nPolynomialElement(f, polynomial);
+ }
+
+ /**
+ * Create the one element.
+ *
+ * @param f the finite field
+ * @return the one element in the given finite field
+ */
+ public static GF2nPolynomialElement ONE(GF2nPolynomialField f)
+ {
+ GF2Polynomial polynomial = new GF2Polynomial(f.getDegree(),
+ new int[]{1});
+ return new GF2nPolynomialElement(f, polynomial);
+ }
+
+ /**
+ * Assigns the value 'one' to this Polynomial.
+ */
+ void assignOne()
+ {
+ polynomial.assignOne();
+ }
+
+ /**
+ * Assign a random value to this GF2nPolynomialElement using the specified
+ * source of randomness.
+ *
+ * @param rand the source of randomness
+ */
+ private void randomize(Random rand)
+ {
+ polynomial.expandN(mDegree);
+ polynomial.randomize(rand);
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // comparison
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Checks whether this element is zero.
+ *
+ * @return <tt>true</tt> if <tt>this</tt> is the zero element
+ */
+ public boolean isZero()
+ {
+ return polynomial.isZero();
+ }
+
+ /**
+ * Tests if the GF2nPolynomialElement has 'one' as value.
+ *
+ * @return true if <i>this</i> equals one (this == 1)
+ */
+ public boolean isOne()
+ {
+ return polynomial.isOne();
+ }
+
+ /**
+ * Compare this element with another object.
+ *
+ * @param other the other object
+ * @return <tt>true</tt> if the two objects are equal, <tt>false</tt>
+ * otherwise
+ */
+ public boolean equals(Object other)
+ {
+ if (other == null || !(other instanceof GF2nPolynomialElement))
+ {
+ return false;
+ }
+ GF2nPolynomialElement otherElem = (GF2nPolynomialElement)other;
+
+ if (mField != otherElem.mField)
+ {
+ if (!mField.getFieldPolynomial().equals(
+ otherElem.mField.getFieldPolynomial()))
+ {
+ return false;
+ }
+ }
+
+ return polynomial.equals(otherElem.polynomial);
+ }
+
+ /**
+ * @return the hash code of this element
+ */
+ public int hashCode()
+ {
+ return mField.hashCode() + polynomial.hashCode();
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // access
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns the value of this GF2nPolynomialElement in a new Bitstring.
+ *
+ * @return the value of this GF2nPolynomialElement in a new Bitstring
+ */
+ private GF2Polynomial getGF2Polynomial()
+ {
+ return new GF2Polynomial(polynomial);
+ }
+
+ /**
+ * Checks whether the indexed bit of the bit representation is set.
+ *
+ * @param index the index of the bit to test
+ * @return <tt>true</tt> if the indexed bit is set
+ */
+ boolean testBit(int index)
+ {
+ return polynomial.testBit(index);
+ }
+
+ /**
+ * Returns whether the rightmost bit of the bit representation is set. This
+ * is needed for data conversion according to 1363.
+ *
+ * @return true if the rightmost bit of this element is set
+ */
+ public boolean testRightmostBit()
+ {
+ return polynomial.testBit(0);
+ }
+
+ /**
+ * Compute the sum of this element and <tt>addend</tt>.
+ *
+ * @param addend the addend
+ * @return <tt>this + other</tt> (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public GFElement add(GFElement addend)
+ throws RuntimeException
+ {
+ GF2nPolynomialElement result = new GF2nPolynomialElement(this);
+ result.addToThis(addend);
+ return result;
+ }
+
+ /**
+ * Compute <tt>this + addend</tt> (overwrite <tt>this</tt>).
+ *
+ * @param addend the addend
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public void addToThis(GFElement addend)
+ throws RuntimeException
+ {
+ if (!(addend instanceof GF2nPolynomialElement))
+ {
+ throw new RuntimeException();
+ }
+ if (!mField.equals(((GF2nPolynomialElement)addend).mField))
+ {
+ throw new RuntimeException();
+ }
+ polynomial.addToThis(((GF2nPolynomialElement)addend).polynomial);
+ }
+
+ /**
+ * Returns <tt>this</tt> element + 'one".
+ *
+ * @return <tt>this</tt> + 'one'
+ */
+ public GF2nElement increase()
+ {
+ GF2nPolynomialElement result = new GF2nPolynomialElement(this);
+ result.increaseThis();
+ return result;
+ }
+
+ /**
+ * Increases this element by 'one'.
+ */
+ public void increaseThis()
+ {
+ polynomial.increaseThis();
+ }
+
+ /**
+ * Compute the product of this element and <tt>factor</tt>.
+ *
+ * @param factor the factor
+ * @return <tt>this * factor</tt> (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public GFElement multiply(GFElement factor)
+ throws RuntimeException
+ {
+ GF2nPolynomialElement result = new GF2nPolynomialElement(this);
+ result.multiplyThisBy(factor);
+ return result;
+ }
+
+ /**
+ * Compute <tt>this * factor</tt> (overwrite <tt>this</tt>).
+ *
+ * @param factor the factor
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public void multiplyThisBy(GFElement factor)
+ throws RuntimeException
+ {
+ if (!(factor instanceof GF2nPolynomialElement))
+ {
+ throw new RuntimeException();
+ }
+ if (!mField.equals(((GF2nPolynomialElement)factor).mField))
+ {
+ throw new RuntimeException();
+ }
+ if (equals(factor))
+ {
+ squareThis();
+ return;
+ }
+ polynomial = polynomial
+ .multiply(((GF2nPolynomialElement)factor).polynomial);
+ reduceThis();
+ }
+
+ /**
+ * Compute the multiplicative inverse of this element.
+ *
+ * @return <tt>this<sup>-1</sup></tt> (newly created)
+ * @throws ArithmeticException if <tt>this</tt> is the zero element.
+ * @see GF2nPolynomialElement#invertMAIA
+ * @see GF2nPolynomialElement#invertEEA
+ * @see GF2nPolynomialElement#invertSquare
+ */
+ public GFElement invert()
+ throws ArithmeticException
+ {
+ return invertMAIA();
+ }
+
+ /**
+ * Calculates the multiplicative inverse of <i>this</i> and returns the
+ * result in a new GF2nPolynomialElement.
+ *
+ * @return <i>this</i>^(-1)
+ * @throws ArithmeticException if <i>this</i> equals zero
+ */
+ public GF2nPolynomialElement invertEEA()
+ throws ArithmeticException
+ {
+ if (isZero())
+ {
+ throw new ArithmeticException();
+ }
+ GF2Polynomial b = new GF2Polynomial(mDegree + 32, "ONE");
+ b.reduceN();
+ GF2Polynomial c = new GF2Polynomial(mDegree + 32);
+ c.reduceN();
+ GF2Polynomial u = getGF2Polynomial();
+ GF2Polynomial v = mField.getFieldPolynomial();
+ GF2Polynomial h;
+ int j;
+ u.reduceN();
+ while (!u.isOne())
+ {
+ u.reduceN();
+ v.reduceN();
+ j = u.getLength() - v.getLength();
+ if (j < 0)
+ {
+ h = u;
+ u = v;
+ v = h;
+ h = b;
+ b = c;
+ c = h;
+ j = -j;
+ c.reduceN(); // this increases the performance
+ }
+ u.shiftLeftAddThis(v, j);
+ b.shiftLeftAddThis(c, j);
+ }
+ b.reduceN();
+ return new GF2nPolynomialElement((GF2nPolynomialField)mField, b);
+ }
+
+ /**
+ * Calculates the multiplicative inverse of <i>this</i> and returns the
+ * result in a new GF2nPolynomialElement.
+ *
+ * @return <i>this</i>^(-1)
+ * @throws ArithmeticException if <i>this</i> equals zero
+ */
+ public GF2nPolynomialElement invertSquare()
+ throws ArithmeticException
+ {
+ GF2nPolynomialElement n;
+ GF2nPolynomialElement u;
+ int i, j, k, b;
+
+ if (isZero())
+ {
+ throw new ArithmeticException();
+ }
+ // b = (n-1)
+ b = mField.getDegree() - 1;
+ // n = a
+ n = new GF2nPolynomialElement(this);
+ n.polynomial.expandN((mDegree << 1) + 32); // increase performance
+ n.polynomial.reduceN();
+ // k = 1
+ k = 1;
+
+ // for i = (r-1) downto 0 do, r=bitlength(b)
+ for (i = IntegerFunctions.floorLog(b) - 1; i >= 0; i--)
+ {
+ // u = n
+ u = new GF2nPolynomialElement(n);
+ // for j = 1 to k do
+ for (j = 1; j <= k; j++)
+ {
+ // u = u^2
+ u.squareThisPreCalc();
+ }
+ // n = nu
+ n.multiplyThisBy(u);
+ // k = 2k
+ k <<= 1;
+ // if b(i)==1
+ if ((b & bitMask[i]) != 0)
+ {
+ // n = n^2 * b
+ n.squareThisPreCalc();
+ n.multiplyThisBy(this);
+ // k = k+1
+ k += 1;
+ }
+ }
+
+ // outpur n^2
+ n.squareThisPreCalc();
+ return n;
+ }
+
+ /**
+ * Calculates the multiplicative inverse of <i>this</i> using the modified
+ * almost inverse algorithm and returns the result in a new
+ * GF2nPolynomialElement.
+ *
+ * @return <i>this</i>^(-1)
+ * @throws ArithmeticException if <i>this</i> equals zero
+ */
+ public GF2nPolynomialElement invertMAIA()
+ throws ArithmeticException
+ {
+ if (isZero())
+ {
+ throw new ArithmeticException();
+ }
+ GF2Polynomial b = new GF2Polynomial(mDegree, "ONE");
+ GF2Polynomial c = new GF2Polynomial(mDegree);
+ GF2Polynomial u = getGF2Polynomial();
+ GF2Polynomial v = mField.getFieldPolynomial();
+ GF2Polynomial h;
+ while (true)
+ {
+ while (!u.testBit(0))
+ { // x|u (x divides u)
+ u.shiftRightThis(); // u = u / x
+ if (!b.testBit(0))
+ {
+ b.shiftRightThis();
+ }
+ else
+ {
+ b.addToThis(mField.getFieldPolynomial());
+ b.shiftRightThis();
+ }
+ }
+ if (u.isOne())
+ {
+ return new GF2nPolynomialElement((GF2nPolynomialField)mField,
+ b);
+ }
+ u.reduceN();
+ v.reduceN();
+ if (u.getLength() < v.getLength())
+ {
+ h = u;
+ u = v;
+ v = h;
+ h = b;
+ b = c;
+ c = h;
+ }
+ u.addToThis(v);
+ b.addToThis(c);
+ }
+ }
+
+ /**
+ * This method is used internally to map the square()-calls within
+ * GF2nPolynomialElement to one of the possible squaring methods.
+ *
+ * @return <tt>this<sup>2</sup></tt> (newly created)
+ * @see GF2nPolynomialElement#squarePreCalc
+ */
+ public GF2nElement square()
+ {
+ return squarePreCalc();
+ }
+
+ /**
+ * This method is used internally to map the square()-calls within
+ * GF2nPolynomialElement to one of the possible squaring methods.
+ */
+ public void squareThis()
+ {
+ squareThisPreCalc();
+ }
+
+ /**
+ * Squares this GF2nPolynomialElement using GF2nField's squaring matrix.
+ * This is supposed to be fast when using a polynomial (no tri- or
+ * pentanomial) as fieldpolynomial. Use squarePreCalc when using a tri- or
+ * pentanomial as fieldpolynomial instead.
+ *
+ * @return <tt>this<sup>2</sup></tt> (newly created)
+ * @see GF2Polynomial#vectorMult
+ * @see GF2nPolynomialElement#squarePreCalc
+ * @see GF2nPolynomialElement#squareBitwise
+ */
+ public GF2nPolynomialElement squareMatrix()
+ {
+ GF2nPolynomialElement result = new GF2nPolynomialElement(this);
+ result.squareThisMatrix();
+ result.reduceThis();
+ return result;
+ }
+
+ /**
+ * Squares this GF2nPolynomialElement using GF2nFields squaring matrix. This
+ * is supposed to be fast when using a polynomial (no tri- or pentanomial)
+ * as fieldpolynomial. Use squarePreCalc when using a tri- or pentanomial as
+ * fieldpolynomial instead.
+ *
+ * @see GF2Polynomial#vectorMult
+ * @see GF2nPolynomialElement#squarePreCalc
+ * @see GF2nPolynomialElement#squareBitwise
+ */
+ public void squareThisMatrix()
+ {
+ GF2Polynomial result = new GF2Polynomial(mDegree);
+ for (int i = 0; i < mDegree; i++)
+ {
+ if (polynomial
+ .vectorMult(((GF2nPolynomialField)mField).squaringMatrix[mDegree
+ - i - 1]))
+ {
+ result.setBit(i);
+
+ }
+ }
+ polynomial = result;
+ }
+
+ /**
+ * Squares this GF2nPolynomialElement by shifting left its Bitstring and
+ * reducing. This is supposed to be the slowest method. Use squarePreCalc or
+ * squareMatrix instead.
+ *
+ * @return <tt>this<sup>2</sup></tt> (newly created)
+ * @see GF2nPolynomialElement#squareMatrix
+ * @see GF2nPolynomialElement#squarePreCalc
+ * @see GF2Polynomial#squareThisBitwise
+ */
+ public GF2nPolynomialElement squareBitwise()
+ {
+ GF2nPolynomialElement result = new GF2nPolynomialElement(this);
+ result.squareThisBitwise();
+ result.reduceThis();
+ return result;
+ }
+
+ /**
+ * Squares this GF2nPolynomialElement by shifting left its Bitstring and
+ * reducing. This is supposed to be the slowest method. Use squarePreCalc or
+ * squareMatrix instead.
+ *
+ * @see GF2nPolynomialElement#squareMatrix
+ * @see GF2nPolynomialElement#squarePreCalc
+ * @see GF2Polynomial#squareThisBitwise
+ */
+ public void squareThisBitwise()
+ {
+ polynomial.squareThisBitwise();
+ reduceThis();
+ }
+
+ /**
+ * Squares this GF2nPolynomialElement by using precalculated values and
+ * reducing. This is supposed to de fastest when using a trinomial or
+ * pentanomial as field polynomial. Use squareMatrix when using a ordinary
+ * polynomial as field polynomial.
+ *
+ * @return <tt>this<sup>2</sup></tt> (newly created)
+ * @see GF2nPolynomialElement#squareMatrix
+ * @see GF2Polynomial#squareThisPreCalc
+ */
+ public GF2nPolynomialElement squarePreCalc()
+ {
+ GF2nPolynomialElement result = new GF2nPolynomialElement(this);
+ result.squareThisPreCalc();
+ result.reduceThis();
+ return result;
+ }
+
+ /**
+ * Squares this GF2nPolynomialElement by using precalculated values and
+ * reducing. This is supposed to de fastest when using a tri- or pentanomial
+ * as fieldpolynomial. Use squareMatrix when using a ordinary polynomial as
+ * fieldpolynomial.
+ *
+ * @see GF2nPolynomialElement#squareMatrix
+ * @see GF2Polynomial#squareThisPreCalc
+ */
+ public void squareThisPreCalc()
+ {
+ polynomial.squareThisPreCalc();
+ reduceThis();
+ }
+
+ /**
+ * Calculates <i>this</i> to the power of <i>k</i> and returns the result
+ * in a new GF2nPolynomialElement.
+ *
+ * @param k the power
+ * @return <i>this</i>^<i>k</i> in a new GF2nPolynomialElement
+ */
+ public GF2nPolynomialElement power(int k)
+ {
+ if (k == 1)
+ {
+ return new GF2nPolynomialElement(this);
+ }
+
+ GF2nPolynomialElement result = GF2nPolynomialElement
+ .ONE((GF2nPolynomialField)mField);
+ if (k == 0)
+ {
+ return result;
+ }
+
+ GF2nPolynomialElement x = new GF2nPolynomialElement(this);
+ x.polynomial.expandN((x.mDegree << 1) + 32); // increase performance
+ x.polynomial.reduceN();
+
+ for (int i = 0; i < mDegree; i++)
+ {
+ if ((k & (1 << i)) != 0)
+ {
+ result.multiplyThisBy(x);
+ }
+ x.square();
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute the square root of this element and return the result in a new
+ * {@link GF2nPolynomialElement}.
+ *
+ * @return <tt>this<sup>1/2</sup></tt> (newly created)
+ */
+ public GF2nElement squareRoot()
+ {
+ GF2nPolynomialElement result = new GF2nPolynomialElement(this);
+ result.squareRootThis();
+ return result;
+ }
+
+ /**
+ * Compute the square root of this element.
+ */
+ public void squareRootThis()
+ {
+ // increase performance
+ polynomial.expandN((mDegree << 1) + 32);
+ polynomial.reduceN();
+ for (int i = 0; i < mField.getDegree() - 1; i++)
+ {
+ squareThis();
+ }
+ }
+
+ /**
+ * Solves the quadratic equation <tt>z<sup>2</sup> + z = this</tt> if
+ * such a solution exists. This method returns one of the two possible
+ * solutions. The other solution is <tt>z + 1</tt>. Use z.increase() to
+ * compute this solution.
+ *
+ * @return a GF2nPolynomialElement representing one z satisfying the
+ * equation <tt>z<sup>2</sup> + z = this</tt>
+ * @throws NoSolutionException if no solution exists
+ * @see "IEEE 1363, Annex A.4.7"
+ */
+ public GF2nElement solveQuadraticEquation()
+ throws RuntimeException
+ {
+ if (isZero())
+ {
+ return ZERO((GF2nPolynomialField)mField);
+ }
+
+ if ((mDegree & 1) == 1)
+ {
+ return halfTrace();
+ }
+
+ // TODO this can be sped-up by precomputation of p and w's
+ GF2nPolynomialElement z, w;
+ do
+ {
+ // step 1.
+ GF2nPolynomialElement p = new GF2nPolynomialElement(
+ (GF2nPolynomialField)mField, new Random());
+ // step 2.
+ z = ZERO((GF2nPolynomialField)mField);
+ w = (GF2nPolynomialElement)p.clone();
+ // step 3.
+ for (int i = 1; i < mDegree; i++)
+ {
+ // compute z = z^2 + w^2 * this
+ // and w = w^2 + p
+ z.squareThis();
+ w.squareThis();
+ z.addToThis(w.multiply(this));
+ w.addToThis(p);
+ }
+ }
+ while (w.isZero()); // step 4.
+
+ if (!equals(z.square().add(z)))
+ {
+ throw new RuntimeException();
+ }
+
+ // step 5.
+ return z;
+ }
+
+ /**
+ * Returns the trace of this GF2nPolynomialElement.
+ *
+ * @return the trace of this GF2nPolynomialElement
+ */
+ public int trace()
+ {
+ GF2nPolynomialElement t = new GF2nPolynomialElement(this);
+ int i;
+
+ for (i = 1; i < mDegree; i++)
+ {
+ t.squareThis();
+ t.addToThis(this);
+ }
+
+ if (t.isOne())
+ {
+ return 1;
+ }
+ return 0;
+ }
+
+ /**
+ * Returns the half-trace of this GF2nPolynomialElement.
+ *
+ * @return a GF2nPolynomialElement representing the half-trace of this
+ * GF2nPolynomialElement.
+ * @throws DegreeIsEvenException if the degree of this GF2nPolynomialElement is even.
+ */
+ private GF2nPolynomialElement halfTrace()
+ throws RuntimeException
+ {
+ if ((mDegree & 0x01) == 0)
+ {
+ throw new RuntimeException();
+ }
+ int i;
+ GF2nPolynomialElement h = new GF2nPolynomialElement(this);
+
+ for (i = 1; i <= ((mDegree - 1) >> 1); i++)
+ {
+ h.squareThis();
+ h.squareThis();
+ h.addToThis(this);
+ }
+
+ return h;
+ }
+
+ /**
+ * Reduces this GF2nPolynomialElement modulo the field-polynomial.
+ *
+ * @see GF2Polynomial#reduceTrinomial
+ * @see GF2Polynomial#reducePentanomial
+ */
+ private void reduceThis()
+ {
+ if (polynomial.getLength() > mDegree)
+ { // really reduce ?
+ if (((GF2nPolynomialField)mField).isTrinomial())
+ { // fieldpolonomial
+ // is trinomial
+ int tc;
+ try
+ {
+ tc = ((GF2nPolynomialField)mField).getTc();
+ }
+ catch (RuntimeException NATExc)
+ {
+ throw new RuntimeException(
+ "GF2nPolynomialElement.reduce: the field"
+ + " polynomial is not a trinomial");
+ }
+ if (((mDegree - tc) <= 32) // do we have to use slow
+ // bitwise reduction ?
+ || (polynomial.getLength() > (mDegree << 1)))
+ {
+ reduceTrinomialBitwise(tc);
+ return;
+ }
+ polynomial.reduceTrinomial(mDegree, tc);
+ return;
+ }
+ else if (((GF2nPolynomialField)mField).isPentanomial())
+ { // fieldpolynomial
+ // is
+ // pentanomial
+ int[] pc;
+ try
+ {
+ pc = ((GF2nPolynomialField)mField).getPc();
+ }
+ catch (RuntimeException NATExc)
+ {
+ throw new RuntimeException(
+ "GF2nPolynomialElement.reduce: the field"
+ + " polynomial is not a pentanomial");
+ }
+ if (((mDegree - pc[2]) <= 32) // do we have to use slow
+ // bitwise reduction ?
+ || (polynomial.getLength() > (mDegree << 1)))
+ {
+ reducePentanomialBitwise(pc);
+ return;
+ }
+ polynomial.reducePentanomial(mDegree, pc);
+ return;
+ }
+ else
+ { // fieldpolynomial is something else
+ polynomial = polynomial.remainder(mField.getFieldPolynomial());
+ polynomial.expandN(mDegree);
+ return;
+ }
+ }
+ if (polynomial.getLength() < mDegree)
+ {
+ polynomial.expandN(mDegree);
+ }
+ }
+
+ /**
+ * Reduce this GF2nPolynomialElement using the trinomial x^n + x^tc + 1 as
+ * fieldpolynomial. The coefficients are reduced bit by bit.
+ */
+ private void reduceTrinomialBitwise(int tc)
+ {
+ int i;
+ int k = mDegree - tc;
+ for (i = polynomial.getLength() - 1; i >= mDegree; i--)
+ {
+ if (polynomial.testBit(i))
+ {
+
+ polynomial.xorBit(i);
+ polynomial.xorBit(i - k);
+ polynomial.xorBit(i - mDegree);
+
+ }
+ }
+ polynomial.reduceN();
+ polynomial.expandN(mDegree);
+ }
+
+ /**
+ * Reduce this GF2nPolynomialElement using the pentanomial x^n + x^pc[2] +
+ * x^pc[1] + x^pc[0] + 1 as fieldpolynomial. The coefficients are reduced
+ * bit by bit.
+ */
+ private void reducePentanomialBitwise(int[] pc)
+ {
+ int i;
+ int k = mDegree - pc[2];
+ int l = mDegree - pc[1];
+ int m = mDegree - pc[0];
+ for (i = polynomial.getLength() - 1; i >= mDegree; i--)
+ {
+ if (polynomial.testBit(i))
+ {
+ polynomial.xorBit(i);
+ polynomial.xorBit(i - k);
+ polynomial.xorBit(i - l);
+ polynomial.xorBit(i - m);
+ polynomial.xorBit(i - mDegree);
+
+ }
+ }
+ polynomial.reduceN();
+ polynomial.expandN(mDegree);
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // conversion
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns a string representing this Bitstrings value using hexadecimal
+ * radix in MSB-first order.
+ *
+ * @return a String representing this Bitstrings value.
+ */
+ public String toString()
+ {
+ return polynomial.toString(16);
+ }
+
+ /**
+ * Returns a string representing this Bitstrings value using hexadecimal or
+ * binary radix in MSB-first order.
+ *
+ * @param radix the radix to use (2 or 16, otherwise 2 is used)
+ * @return a String representing this Bitstrings value.
+ */
+ public String toString(int radix)
+ {
+ return polynomial.toString(radix);
+ }
+
+ /**
+ * Converts this GF2nPolynomialElement to a byte[] according to 1363.
+ *
+ * @return a byte[] representing the value of this GF2nPolynomialElement
+ * @see "P1363 5.5.2 p22f BS2OSP, FE2OSP"
+ */
+ public byte[] toByteArray()
+ {
+ return polynomial.toByteArray();
+ }
+
+ /**
+ * Converts this GF2nPolynomialElement to an integer according to 1363.
+ *
+ * @return a BigInteger representing the value of this
+ * GF2nPolynomialElement
+ * @see "P1363 5.5.1 p22 BS2IP"
+ */
+ public BigInteger toFlexiBigInt()
+ {
+ return polynomial.toFlexiBigInt();
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomialField.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomialField.java
new file mode 100644
index 00000000..f66ec20a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomialField.java
@@ -0,0 +1,553 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+
+import java.util.Random;
+import java.util.Vector;
+
+
+/**
+ * This class implements the abstract class <tt>GF2nField</tt> for polynomial
+ * representation. It computes the field polynomial and the squaring matrix.
+ * GF2nField is used by GF2nPolynomialElement which implements the elements of
+ * this field.
+ *
+ * @see GF2nField
+ * @see GF2nPolynomialElement
+ */
+public class GF2nPolynomialField
+ extends GF2nField
+{
+
+ /**
+ * Matrix used for fast squaring
+ */
+ GF2Polynomial[] squaringMatrix;
+
+ // field polynomial is a trinomial
+ private boolean isTrinomial = false;
+
+ // field polynomial is a pentanomial
+ private boolean isPentanomial = false;
+
+ // middle coefficient of the field polynomial in case it is a trinomial
+ private int tc;
+
+ // middle 3 coefficients of the field polynomial in case it is a pentanomial
+ private int[] pc = new int[3];
+
+ /**
+ * constructs an instance of the finite field with 2<sup>deg</sup>
+ * elements and characteristic 2.
+ *
+ * @param deg the extention degree of this field
+ */
+ public GF2nPolynomialField(int deg)
+ {
+ if (deg < 3)
+ {
+ throw new IllegalArgumentException("k must be at least 3");
+ }
+ mDegree = deg;
+ computeFieldPolynomial();
+ computeSquaringMatrix();
+ fields = new Vector();
+ matrices = new Vector();
+ }
+
+ /**
+ * constructs an instance of the finite field with 2<sup>deg</sup>
+ * elements and characteristic 2.
+ *
+ * @param deg the degree of this field
+ * @param file true if you want to read the field polynomial from the
+ * file false if you want to use a random fielpolynomial
+ * (this can take very long for huge degrees)
+ */
+ public GF2nPolynomialField(int deg, boolean file)
+ {
+ if (deg < 3)
+ {
+ throw new IllegalArgumentException("k must be at least 3");
+ }
+ mDegree = deg;
+ if (file)
+ {
+ computeFieldPolynomial();
+ }
+ else
+ {
+ computeFieldPolynomial2();
+ }
+ computeSquaringMatrix();
+ fields = new Vector();
+ matrices = new Vector();
+ }
+
+ /**
+ * Creates a new GF2nField of degree <i>i</i> and uses the given
+ * <i>polynomial</i> as field polynomial. The <i>polynomial</i> is checked
+ * whether it is irreducible. This can take some time if <i>i</i> is huge!
+ *
+ * @param deg degree of the GF2nField
+ * @param polynomial the field polynomial to use
+ * @throws PolynomialIsNotIrreducibleException if the given polynomial is not irreducible in GF(2^<i>i</i>)
+ */
+ public GF2nPolynomialField(int deg, GF2Polynomial polynomial)
+ throws RuntimeException
+ {
+ if (deg < 3)
+ {
+ throw new IllegalArgumentException("degree must be at least 3");
+ }
+ if (polynomial.getLength() != deg + 1)
+ {
+ throw new RuntimeException();
+ }
+ if (!polynomial.isIrreducible())
+ {
+ throw new RuntimeException();
+ }
+ mDegree = deg;
+ // fieldPolynomial = new Bitstring(polynomial);
+ fieldPolynomial = polynomial;
+ computeSquaringMatrix();
+ int k = 2; // check if the polynomial is a trinomial or pentanomial
+ for (int j = 1; j < fieldPolynomial.getLength() - 1; j++)
+ {
+ if (fieldPolynomial.testBit(j))
+ {
+ k++;
+ if (k == 3)
+ {
+ tc = j;
+ }
+ if (k <= 5)
+ {
+ pc[k - 3] = j;
+ }
+ }
+ }
+ if (k == 3)
+ {
+ isTrinomial = true;
+ }
+ if (k == 5)
+ {
+ isPentanomial = true;
+ }
+ fields = new Vector();
+ matrices = new Vector();
+ }
+
+ /**
+ * Returns true if the field polynomial is a trinomial. The coefficient can
+ * be retrieved using getTc().
+ *
+ * @return true if the field polynomial is a trinomial
+ */
+ public boolean isTrinomial()
+ {
+ return isTrinomial;
+ }
+
+ /**
+ * Returns true if the field polynomial is a pentanomial. The coefficients
+ * can be retrieved using getPc().
+ *
+ * @return true if the field polynomial is a pentanomial
+ */
+ public boolean isPentanomial()
+ {
+ return isPentanomial;
+ }
+
+ /**
+ * Returns the degree of the middle coefficient of the used field trinomial
+ * (x^n + x^(getTc()) + 1).
+ *
+ * @return the middle coefficient of the used field trinomial
+ * @throws GFException if the field polynomial is not a trinomial
+ */
+ public int getTc()
+ throws RuntimeException
+ {
+ if (!isTrinomial)
+ {
+ throw new RuntimeException();
+ }
+ return tc;
+ }
+
+ /**
+ * Returns the degree of the middle coefficients of the used field
+ * pentanomial (x^n + x^(getPc()[2]) + x^(getPc()[1]) + x^(getPc()[0]) + 1).
+ *
+ * @return the middle coefficients of the used field pentanomial
+ * @throws GFException if the field polynomial is not a pentanomial
+ */
+ public int[] getPc()
+ throws RuntimeException
+ {
+ if (!isPentanomial)
+ {
+ throw new RuntimeException();
+ }
+ int[] result = new int[3];
+ System.arraycopy(pc, 0, result, 0, 3);
+ return result;
+ }
+
+ /**
+ * Return row vector i of the squaring matrix.
+ *
+ * @param i the index of the row vector to return
+ * @return a copy of squaringMatrix[i]
+ * @see GF2nPolynomialElement#squareMatrix
+ */
+ public GF2Polynomial getSquaringVector(int i)
+ {
+ return new GF2Polynomial(squaringMatrix[i]);
+ }
+
+ /**
+ * Compute a random root of the given GF2Polynomial.
+ *
+ * @param polynomial the polynomial
+ * @return a random root of <tt>polynomial</tt>
+ */
+ protected GF2nElement getRandomRoot(GF2Polynomial polynomial)
+ {
+ // We are in B1!!!
+ GF2nPolynomial c;
+ GF2nPolynomial ut;
+ GF2nElement u;
+ GF2nPolynomial h;
+ int hDegree;
+ // 1. Set g(t) <- f(t)
+ GF2nPolynomial g = new GF2nPolynomial(polynomial, this);
+ int gDegree = g.getDegree();
+ int i;
+
+ // 2. while deg(g) > 1
+ while (gDegree > 1)
+ {
+ do
+ {
+ // 2.1 choose random u (element of) GF(2^m)
+ u = new GF2nPolynomialElement(this, new Random());
+ ut = new GF2nPolynomial(2, GF2nPolynomialElement.ZERO(this));
+ // 2.2 Set c(t) <- ut
+ ut.set(1, u);
+ c = new GF2nPolynomial(ut);
+ // 2.3 For i from 1 to m-1 do
+ for (i = 1; i <= mDegree - 1; i++)
+ {
+ // 2.3.1 c(t) <- (c(t)^2 + ut) mod g(t)
+ c = c.multiplyAndReduce(c, g);
+ c = c.add(ut);
+ }
+ // 2.4 set h(t) <- GCD(c(t), g(t))
+ h = c.gcd(g);
+ // 2.5 if h(t) is constant or deg(g) = deg(h) then go to
+ // step 2.1
+ hDegree = h.getDegree();
+ gDegree = g.getDegree();
+ }
+ while ((hDegree == 0) || (hDegree == gDegree));
+ // 2.6 If 2deg(h) > deg(g) then set g(t) <- g(t)/h(t) ...
+ if ((hDegree << 1) > gDegree)
+ {
+ g = g.quotient(h);
+ }
+ else
+ {
+ // ... else g(t) <- h(t)
+ g = new GF2nPolynomial(h);
+ }
+ gDegree = g.getDegree();
+ }
+ // 3. Output g(0)
+ return g.at(0);
+
+ }
+
+ /**
+ * Computes the change-of-basis matrix for basis conversion according to
+ * 1363. The result is stored in the lists fields and matrices.
+ *
+ * @param B1 the GF2nField to convert to
+ * @see "P1363 A.7.3, p111ff"
+ */
+ protected void computeCOBMatrix(GF2nField B1)
+ {
+ // we are in B0 here!
+ if (mDegree != B1.mDegree)
+ {
+ throw new IllegalArgumentException(
+ "GF2nPolynomialField.computeCOBMatrix: B1 has a different "
+ + "degree and thus cannot be coverted to!");
+ }
+ if (B1 instanceof GF2nONBField)
+ {
+ // speedup (calculation is done in PolynomialElements instead of
+ // ONB)
+ B1.computeCOBMatrix(this);
+ return;
+ }
+ int i, j;
+ GF2nElement[] gamma;
+ GF2nElement u;
+ GF2Polynomial[] COBMatrix = new GF2Polynomial[mDegree];
+ for (i = 0; i < mDegree; i++)
+ {
+ COBMatrix[i] = new GF2Polynomial(mDegree);
+ }
+
+ // find Random Root
+ do
+ {
+ // u is in representation according to B1
+ u = B1.getRandomRoot(fieldPolynomial);
+ }
+ while (u.isZero());
+
+ // build gamma matrix by multiplying by u
+ if (u instanceof GF2nONBElement)
+ {
+ gamma = new GF2nONBElement[mDegree];
+ gamma[mDegree - 1] = GF2nONBElement.ONE((GF2nONBField)B1);
+ }
+ else
+ {
+ gamma = new GF2nPolynomialElement[mDegree];
+ gamma[mDegree - 1] = GF2nPolynomialElement
+ .ONE((GF2nPolynomialField)B1);
+ }
+ gamma[mDegree - 2] = u;
+ for (i = mDegree - 3; i >= 0; i--)
+ {
+ gamma[i] = (GF2nElement)gamma[i + 1].multiply(u);
+ }
+ if (B1 instanceof GF2nONBField)
+ {
+ // convert horizontal gamma matrix by vertical Bitstrings
+ for (i = 0; i < mDegree; i++)
+ {
+ for (j = 0; j < mDegree; j++)
+ {
+ // TODO remember: ONB treats its Bits in reverse order !!!
+ if (gamma[i].testBit(mDegree - j - 1))
+ {
+ COBMatrix[mDegree - j - 1].setBit(mDegree - i - 1);
+ }
+ }
+ }
+ }
+ else
+ {
+ // convert horizontal gamma matrix by vertical Bitstrings
+ for (i = 0; i < mDegree; i++)
+ {
+ for (j = 0; j < mDegree; j++)
+ {
+ if (gamma[i].testBit(j))
+ {
+ COBMatrix[mDegree - j - 1].setBit(mDegree - i - 1);
+ }
+ }
+ }
+ }
+
+ // store field and matrix for further use
+ fields.addElement(B1);
+ matrices.addElement(COBMatrix);
+ // store field and inverse matrix for further use in B1
+ B1.fields.addElement(this);
+ B1.matrices.addElement(invertMatrix(COBMatrix));
+ }
+
+ /**
+ * Computes a new squaring matrix used for fast squaring.
+ *
+ * @see GF2nPolynomialElement#square
+ */
+ private void computeSquaringMatrix()
+ {
+ GF2Polynomial[] d = new GF2Polynomial[mDegree - 1];
+ int i, j;
+ squaringMatrix = new GF2Polynomial[mDegree];
+ for (i = 0; i < squaringMatrix.length; i++)
+ {
+ squaringMatrix[i] = new GF2Polynomial(mDegree, "ZERO");
+ }
+
+ for (i = 0; i < mDegree - 1; i++)
+ {
+ d[i] = new GF2Polynomial(1, "ONE").shiftLeft(mDegree + i)
+ .remainder(fieldPolynomial);
+ }
+ for (i = 1; i <= Math.abs(mDegree >> 1); i++)
+ {
+ for (j = 1; j <= mDegree; j++)
+ {
+ if (d[mDegree - (i << 1)].testBit(mDegree - j))
+ {
+ squaringMatrix[j - 1].setBit(mDegree - i);
+ }
+ }
+ }
+ for (i = Math.abs(mDegree >> 1) + 1; i <= mDegree; i++)
+ {
+ squaringMatrix[(i << 1) - mDegree - 1].setBit(mDegree - i);
+ }
+
+ }
+
+ /**
+ * Computes the field polynomial. This can take a long time for big degrees.
+ */
+ protected void computeFieldPolynomial()
+ {
+ if (testTrinomials())
+ {
+ return;
+ }
+ if (testPentanomials())
+ {
+ return;
+ }
+ testRandom();
+ }
+
+ /**
+ * Computes the field polynomial. This can take a long time for big degrees.
+ */
+ protected void computeFieldPolynomial2()
+ {
+ if (testTrinomials())
+ {
+ return;
+ }
+ if (testPentanomials())
+ {
+ return;
+ }
+ testRandom();
+ }
+
+ /**
+ * Tests all trinomials of degree (n+1) until a irreducible is found and
+ * stores the result in <i>field polynomial</i>. Returns false if no
+ * irreducible trinomial exists in GF(2^n). This can take very long for huge
+ * degrees.
+ *
+ * @return true if an irreducible trinomial is found
+ */
+ private boolean testTrinomials()
+ {
+ int i, l;
+ boolean done = false;
+ l = 0;
+
+ fieldPolynomial = new GF2Polynomial(mDegree + 1);
+ fieldPolynomial.setBit(0);
+ fieldPolynomial.setBit(mDegree);
+ for (i = 1; (i < mDegree) && !done; i++)
+ {
+ fieldPolynomial.setBit(i);
+ done = fieldPolynomial.isIrreducible();
+ l++;
+ if (done)
+ {
+ isTrinomial = true;
+ tc = i;
+ return done;
+ }
+ fieldPolynomial.resetBit(i);
+ done = fieldPolynomial.isIrreducible();
+ }
+
+ return done;
+ }
+
+ /**
+ * Tests all pentanomials of degree (n+1) until a irreducible is found and
+ * stores the result in <i>field polynomial</i>. Returns false if no
+ * irreducible pentanomial exists in GF(2^n). This can take very long for
+ * huge degrees.
+ *
+ * @return true if an irreducible pentanomial is found
+ */
+ private boolean testPentanomials()
+ {
+ int i, j, k, l;
+ boolean done = false;
+ l = 0;
+
+ fieldPolynomial = new GF2Polynomial(mDegree + 1);
+ fieldPolynomial.setBit(0);
+ fieldPolynomial.setBit(mDegree);
+ for (i = 1; (i <= (mDegree - 3)) && !done; i++)
+ {
+ fieldPolynomial.setBit(i);
+ for (j = i + 1; (j <= (mDegree - 2)) && !done; j++)
+ {
+ fieldPolynomial.setBit(j);
+ for (k = j + 1; (k <= (mDegree - 1)) && !done; k++)
+ {
+ fieldPolynomial.setBit(k);
+ if (((mDegree & 1) != 0) | ((i & 1) != 0) | ((j & 1) != 0)
+ | ((k & 1) != 0))
+ {
+ done = fieldPolynomial.isIrreducible();
+ l++;
+ if (done)
+ {
+ isPentanomial = true;
+ pc[0] = i;
+ pc[1] = j;
+ pc[2] = k;
+ return done;
+ }
+ }
+ fieldPolynomial.resetBit(k);
+ }
+ fieldPolynomial.resetBit(j);
+ }
+ fieldPolynomial.resetBit(i);
+ }
+
+ return done;
+ }
+
+ /**
+ * Tests random polynomials of degree (n+1) until an irreducible is found
+ * and stores the result in <i>field polynomial</i>. This can take very
+ * long for huge degrees.
+ *
+ * @return true
+ */
+ private boolean testRandom()
+ {
+ int l;
+ boolean done = false;
+
+ fieldPolynomial = new GF2Polynomial(mDegree + 1);
+ l = 0;
+ while (!done)
+ {
+ l++;
+ fieldPolynomial.randomize();
+ fieldPolynomial.setBit(mDegree);
+ fieldPolynomial.setBit(0);
+ if (fieldPolynomial.isIrreducible())
+ {
+ done = true;
+ return done;
+ }
+ }
+
+ return done;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GFElement.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GFElement.java
new file mode 100644
index 00000000..1e93e158
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GFElement.java
@@ -0,0 +1,158 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+import java.math.BigInteger;
+
+
+/**
+ * This interface defines a finite field element. It is implemented by the
+ * classes {@link GFPElement} and {@link GF2nElement}.
+ *
+ * @see GFPElement
+ * @see GF2nElement
+ */
+public interface GFElement
+{
+
+ /**
+ * @return a copy of this GFElement
+ */
+ Object clone();
+
+ // /////////////////////////////////////////////////////////////////
+ // comparison
+ // /////////////////////////////////////////////////////////////////
+
+ /**
+ * Compare this curve with another object.
+ *
+ * @param other the other object
+ * @return the result of the comparison
+ */
+ boolean equals(Object other);
+
+ /**
+ * @return the hash code of this element
+ */
+ int hashCode();
+
+ /**
+ * Checks whether this element is zero.
+ *
+ * @return <tt>true</tt> if <tt>this</tt> is the zero element
+ */
+ boolean isZero();
+
+ /**
+ * Checks whether this element is one.
+ *
+ * @return <tt>true</tt> if <tt>this</tt> is the one element
+ */
+ boolean isOne();
+
+ // /////////////////////////////////////////////////////////////////////
+ // arithmetic
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Compute the sum of this element and the addend.
+ *
+ * @param addend the addend
+ * @return <tt>this + other</tt> (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ GFElement add(GFElement addend)
+ throws RuntimeException;
+
+ /**
+ * Compute the sum of this element and the addend, overwriting this element.
+ *
+ * @param addend the addend
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ void addToThis(GFElement addend)
+ throws RuntimeException;
+
+ /**
+ * Compute the difference of this element and <tt>minuend</tt>.
+ *
+ * @param minuend the minuend
+ * @return <tt>this - minuend</tt> (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ GFElement subtract(GFElement minuend)
+ throws RuntimeException;
+
+ /**
+ * Compute the difference of this element and <tt>minuend</tt>,
+ * overwriting this element.
+ *
+ * @param minuend the minuend
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ void subtractFromThis(GFElement minuend);
+
+ /**
+ * Compute the product of this element and <tt>factor</tt>.
+ *
+ * @param factor the factor
+ * @return <tt>this * factor</tt> (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ GFElement multiply(GFElement factor)
+ throws RuntimeException;
+
+ /**
+ * Compute <tt>this * factor</tt> (overwrite <tt>this</tt>).
+ *
+ * @param factor the factor
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ void multiplyThisBy(GFElement factor)
+ throws RuntimeException;
+
+ /**
+ * Compute the multiplicative inverse of this element.
+ *
+ * @return <tt>this<sup>-1</sup></tt> (newly created)
+ * @throws ArithmeticException if <tt>this</tt> is the zero element.
+ */
+ GFElement invert()
+ throws ArithmeticException;
+
+ // /////////////////////////////////////////////////////////////////////
+ // conversion
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns this element as FlexiBigInt. The conversion is <a
+ * href="http://grouper.ieee.org/groups/1363/">P1363</a>-conform.
+ *
+ * @return this element as BigInt
+ */
+ BigInteger toFlexiBigInt();
+
+ /**
+ * Returns this element as byte array. The conversion is <a href =
+ * "http://grouper.ieee.org/groups/1363/">P1363</a>-conform.
+ *
+ * @return this element as byte array
+ */
+ byte[] toByteArray();
+
+ /**
+ * Return a String representation of this element.
+ *
+ * @return String representation of this element
+ */
+ String toString();
+
+ /**
+ * Return a String representation of this element. <tt>radix</tt>
+ * specifies the radix of the String representation.
+ *
+ * @param radix specifies the radix of the String representation
+ * @return String representation of this element with the specified radix
+ */
+ String toString(int radix);
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GoppaCode.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GoppaCode.java
new file mode 100644
index 00000000..cf82eaea
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GoppaCode.java
@@ -0,0 +1,310 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+import java.security.SecureRandom;
+
+/**
+ * This class describes decoding operations of an irreducible binary Goppa code.
+ * A check matrix H of the Goppa code and an irreducible Goppa polynomial are
+ * used the operations are worked over a finite field GF(2^m)
+ *
+ * @see GF2mField
+ * @see PolynomialGF2mSmallM
+ */
+public final class GoppaCode
+{
+
+ /**
+ * Default constructor (private).
+ */
+ private GoppaCode()
+ {
+ // empty
+ }
+
+ /**
+ * This class is a container for two instances of {@link GF2Matrix} and one
+ * instance of {@link Permutation}. It is used to hold the systematic form
+ * <tt>S*H*P = (Id|M)</tt> of the check matrix <tt>H</tt> as returned by
+ * {@link GoppaCode#computeSystematicForm(GF2Matrix, SecureRandom)}.
+ *
+ * @see GF2Matrix
+ * @see Permutation
+ */
+ public static class MaMaPe
+ {
+
+ private GF2Matrix s, h;
+
+ private Permutation p;
+
+ /**
+ * Construct a new {@link MaMaPe} container with the given parameters.
+ *
+ * @param s the first matrix
+ * @param h the second matrix
+ * @param p the permutation
+ */
+ public MaMaPe(GF2Matrix s, GF2Matrix h, Permutation p)
+ {
+ this.s = s;
+ this.h = h;
+ this.p = p;
+ }
+
+ /**
+ * @return the first matrix
+ */
+ public GF2Matrix getFirstMatrix()
+ {
+ return s;
+ }
+
+ /**
+ * @return the second matrix
+ */
+ public GF2Matrix getSecondMatrix()
+ {
+ return h;
+ }
+
+ /**
+ * @return the permutation
+ */
+ public Permutation getPermutation()
+ {
+ return p;
+ }
+ }
+
+ /**
+ * This class is a container for an instance of {@link GF2Matrix} and one
+ * int[]. It is used to hold a generator matrix and the set of indices such
+ * that the submatrix of the generator matrix consisting of the specified
+ * columns is the identity.
+ *
+ * @see GF2Matrix
+ * @see Permutation
+ */
+ public static class MatrixSet
+ {
+
+ private GF2Matrix g;
+
+ private int[] setJ;
+
+ /**
+ * Construct a new {@link MatrixSet} container with the given
+ * parameters.
+ *
+ * @param g the generator matrix
+ * @param setJ the set of indices such that the submatrix of the
+ * generator matrix consisting of the specified columns
+ * is the identity
+ */
+ public MatrixSet(GF2Matrix g, int[] setJ)
+ {
+ this.g = g;
+ this.setJ = setJ;
+ }
+
+ /**
+ * @return the generator matrix
+ */
+ public GF2Matrix getG()
+ {
+ return g;
+ }
+
+ /**
+ * @return the set of indices such that the submatrix of the generator
+ * matrix consisting of the specified columns is the identity
+ */
+ public int[] getSetJ()
+ {
+ return setJ;
+ }
+ }
+
+ /**
+ * Construct the check matrix of a Goppa code in canonical form from the
+ * irreducible Goppa polynomial over the finite field
+ * <tt>GF(2<sup>m</sup>)</tt>.
+ *
+ * @param field the finite field
+ * @param gp the irreducible Goppa polynomial
+ */
+ public static GF2Matrix createCanonicalCheckMatrix(GF2mField field,
+ PolynomialGF2mSmallM gp)
+ {
+ int m = field.getDegree();
+ int n = 1 << m;
+ int t = gp.getDegree();
+
+ /* create matrix H over GF(2^m) */
+
+ int[][] hArray = new int[t][n];
+
+ // create matrix YZ
+ int[][] yz = new int[t][n];
+ for (int j = 0; j < n; j++)
+ {
+ // here j is used as index and as element of field GF(2^m)
+ yz[0][j] = field.inverse(gp.evaluateAt(j));
+ }
+
+ for (int i = 1; i < t; i++)
+ {
+ for (int j = 0; j < n; j++)
+ {
+ // here j is used as index and as element of field GF(2^m)
+ yz[i][j] = field.mult(yz[i - 1][j], j);
+ }
+ }
+
+ // create matrix H = XYZ
+ for (int i = 0; i < t; i++)
+ {
+ for (int j = 0; j < n; j++)
+ {
+ for (int k = 0; k <= i; k++)
+ {
+ hArray[i][j] = field.add(hArray[i][j], field.mult(yz[k][j],
+ gp.getCoefficient(t + k - i)));
+ }
+ }
+ }
+
+ /* convert to matrix over GF(2) */
+
+ int[][] result = new int[t * m][(n + 31) >>> 5];
+
+ for (int j = 0; j < n; j++)
+ {
+ int q = j >>> 5;
+ int r = 1 << (j & 0x1f);
+ for (int i = 0; i < t; i++)
+ {
+ int e = hArray[i][j];
+ for (int u = 0; u < m; u++)
+ {
+ int b = (e >>> u) & 1;
+ if (b != 0)
+ {
+ int ind = (i + 1) * m - u - 1;
+ result[ind][q] ^= r;
+ }
+ }
+ }
+ }
+
+ return new GF2Matrix(n, result);
+ }
+
+ /**
+ * Given a check matrix <tt>H</tt>, compute matrices <tt>S</tt>,
+ * <tt>M</tt>, and a random permutation <tt>P</tt> such that
+ * <tt>S*H*P = (Id|M)</tt>. Return <tt>S^-1</tt>, <tt>M</tt>, and
+ * <tt>P</tt> as {@link MaMaPe}. The matrix <tt>(Id | M)</tt> is called
+ * the systematic form of H.
+ *
+ * @param h the check matrix
+ * @param sr a source of randomness
+ * @return the tuple <tt>(S^-1, M, P)</tt>
+ */
+ public static MaMaPe computeSystematicForm(GF2Matrix h, SecureRandom sr)
+ {
+ int n = h.getNumColumns();
+ GF2Matrix hp, sInv;
+ GF2Matrix s = null;
+ Permutation p;
+ boolean found = false;
+
+ do
+ {
+ p = new Permutation(n, sr);
+ hp = (GF2Matrix)h.rightMultiply(p);
+ sInv = hp.getLeftSubMatrix();
+ try
+ {
+ found = true;
+ s = (GF2Matrix)sInv.computeInverse();
+ }
+ catch (ArithmeticException ae)
+ {
+ found = false;
+ }
+ }
+ while (!found);
+
+ GF2Matrix shp = (GF2Matrix)s.rightMultiply(hp);
+ GF2Matrix m = shp.getRightSubMatrix();
+
+ return new MaMaPe(sInv, m, p);
+ }
+
+ /**
+ * Find an error vector <tt>e</tt> over <tt>GF(2)</tt> from an input
+ * syndrome <tt>s</tt> over <tt>GF(2<sup>m</sup>)</tt>.
+ *
+ * @param syndVec the syndrome
+ * @param field the finite field
+ * @param gp the irreducible Goppa polynomial
+ * @param sqRootMatrix the matrix for computing square roots in
+ * <tt>(GF(2<sup>m</sup>))<sup>t</sup></tt>
+ * @return the error vector
+ */
+ public static GF2Vector syndromeDecode(GF2Vector syndVec, GF2mField field,
+ PolynomialGF2mSmallM gp, PolynomialGF2mSmallM[] sqRootMatrix)
+ {
+
+ int n = 1 << field.getDegree();
+
+ // the error vector
+ GF2Vector errors = new GF2Vector(n);
+
+ // if the syndrome vector is zero, the error vector is also zero
+ if (!syndVec.isZero())
+ {
+ // convert syndrome vector to polynomial over GF(2^m)
+ PolynomialGF2mSmallM syndrome = new PolynomialGF2mSmallM(syndVec
+ .toExtensionFieldVector(field));
+
+ // compute T = syndrome^-1 mod gp
+ PolynomialGF2mSmallM t = syndrome.modInverse(gp);
+
+ // compute tau = sqRoot(T + X) mod gp
+ PolynomialGF2mSmallM tau = t.addMonomial(1);
+ tau = tau.modSquareRootMatrix(sqRootMatrix);
+
+ // compute polynomials a and b satisfying a + b*tau = 0 mod gp
+ PolynomialGF2mSmallM[] ab = tau.modPolynomialToFracton(gp);
+
+ // compute the polynomial a^2 + X*b^2
+ PolynomialGF2mSmallM a2 = ab[0].multiply(ab[0]);
+ PolynomialGF2mSmallM b2 = ab[1].multiply(ab[1]);
+ PolynomialGF2mSmallM xb2 = b2.multWithMonomial(1);
+ PolynomialGF2mSmallM a2plusXb2 = a2.add(xb2);
+
+ // normalize a^2 + X*b^2 to obtain the error locator polynomial
+ int headCoeff = a2plusXb2.getHeadCoefficient();
+ int invHeadCoeff = field.inverse(headCoeff);
+ PolynomialGF2mSmallM elp = a2plusXb2.multWithElement(invHeadCoeff);
+
+ // for all elements i of GF(2^m)
+ for (int i = 0; i < n; i++)
+ {
+ // evaluate the error locator polynomial at i
+ int z = elp.evaluateAt(i);
+ // if polynomial evaluates to zero
+ if (z == 0)
+ {
+ // set the i-th coefficient of the error vector
+ errors.setBit(i);
+ }
+ }
+ }
+
+ return errors;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/IntUtils.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/IntUtils.java
new file mode 100644
index 00000000..bfb8fca0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/IntUtils.java
@@ -0,0 +1,203 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+import java.math.BigInteger;
+
+/**
+ *
+ *
+ *
+ */
+public final class IntUtils
+{
+
+ /**
+ * Default constructor (private).
+ */
+ private IntUtils()
+ {
+ // empty
+ }
+
+ /**
+ * Compare two int arrays. No null checks are performed.
+ *
+ * @param left the first int array
+ * @param right the second int array
+ * @return the result of the comparison
+ */
+ public static boolean equals(int[] left, int[] right)
+ {
+ if (left.length != right.length)
+ {
+ return false;
+ }
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ result &= left[i] == right[i];
+ }
+ return result;
+ }
+
+ /**
+ * Return a clone of the given int array. No null checks are performed.
+ *
+ * @param array the array to clone
+ * @return the clone of the given array
+ */
+ public static int[] clone(int[] array)
+ {
+ int[] result = new int[array.length];
+ System.arraycopy(array, 0, result, 0, array.length);
+ return result;
+ }
+
+ /**
+ * Fill the given int array with the given value.
+ *
+ * @param array the array
+ * @param value the value
+ */
+ public static void fill(int[] array, int value)
+ {
+ for (int i = array.length - 1; i >= 0; i--)
+ {
+ array[i] = value;
+ }
+ }
+
+ /**
+ * Sorts this array of integers according to the Quicksort algorithm. After
+ * calling this method this array is sorted in ascending order with the
+ * smallest integer taking position 0 in the array.
+ * <p/>
+ * <p/>
+ * This implementation is based on the quicksort algorithm as described in
+ * <code>Data Structures In Java</code> by Thomas A. Standish, Chapter 10,
+ * ISBN 0-201-30564-X.
+ *
+ * @param source the array of integers that needs to be sorted.
+ */
+ public static void quicksort(int[] source)
+ {
+ quicksort(source, 0, source.length - 1);
+ }
+
+ /**
+ * Sort a subarray of a source array. The subarray is specified by its start
+ * and end index.
+ *
+ * @param source the int array to be sorted
+ * @param left the start index of the subarray
+ * @param right the end index of the subarray
+ */
+ public static void quicksort(int[] source, int left, int right)
+ {
+ if (right > left)
+ {
+ int index = partition(source, left, right, right);
+ quicksort(source, left, index - 1);
+ quicksort(source, index + 1, right);
+ }
+ }
+
+ /**
+ * Split a subarray of a source array into two partitions. The left
+ * partition contains elements that have value less than or equal to the
+ * pivot element, the right partition contains the elements that have larger
+ * value.
+ *
+ * @param source the int array whose subarray will be splitted
+ * @param left the start position of the subarray
+ * @param right the end position of the subarray
+ * @param pivotIndex the index of the pivot element inside the array
+ * @return the new index of the pivot element inside the array
+ */
+ private static int partition(int[] source, int left, int right,
+ int pivotIndex)
+ {
+
+ int pivot = source[pivotIndex];
+ source[pivotIndex] = source[right];
+ source[right] = pivot;
+
+ int index = left;
+
+ for (int i = left; i < right; i++)
+ {
+ if (source[i] <= pivot)
+ {
+ int tmp = source[index];
+ source[index] = source[i];
+ source[i] = tmp;
+ index++;
+ }
+ }
+
+ int tmp = source[index];
+ source[index] = source[right];
+ source[right] = tmp;
+
+ return index;
+ }
+
+ /**
+ * Generates a subarray of a given int array.
+ *
+ * @param input -
+ * the input int array
+ * @param start -
+ * the start index
+ * @param end -
+ * the end index
+ * @return a subarray of <tt>input</tt>, ranging from <tt>start</tt> to
+ * <tt>end</tt>
+ */
+ public static int[] subArray(final int[] input, final int start,
+ final int end)
+ {
+ int[] result = new int[end - start];
+ System.arraycopy(input, start, result, 0, end - start);
+ return result;
+ }
+
+ /**
+ * Convert an int array to a {@link FlexiBigInt} array.
+ *
+ * @param input the int array
+ * @return the {@link FlexiBigInt} array
+ */
+ public static BigInteger[] toFlexiBigIntArray(int[] input)
+ {
+ BigInteger[] result = new BigInteger[input.length];
+ for (int i = 0; i < input.length; i++)
+ {
+ result[i] = BigInteger.valueOf(input[i]);
+ }
+ return result;
+ }
+
+ /**
+ * @param input an int array
+ * @return a human readable form of the given int array
+ */
+ public static String toString(int[] input)
+ {
+ String result = "";
+ for (int i = 0; i < input.length; i++)
+ {
+ result += input[i] + " ";
+ }
+ return result;
+ }
+
+ /**
+ * @param input an int arary
+ * @return the int array as hex string
+ */
+ public static String toHexString(int[] input)
+ {
+ return ByteUtils.toHexString(BigEndianConversions.toByteArray(input));
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/IntegerFunctions.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/IntegerFunctions.java
new file mode 100644
index 00000000..763b180e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/IntegerFunctions.java
@@ -0,0 +1,1424 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/**
+ * Class of number-theory related functions for use with integers represented as
+ * <tt>int</tt>'s or <tt>BigInteger</tt> objects.
+ */
+public final class IntegerFunctions
+{
+
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ private static final BigInteger TWO = BigInteger.valueOf(2);
+
+ private static final BigInteger FOUR = BigInteger.valueOf(4);
+
+ private static final int[] SMALL_PRIMES = {3, 5, 7, 11, 13, 17, 19, 23,
+ 29, 31, 37, 41};
+
+ private static final long SMALL_PRIME_PRODUCT = 3L * 5 * 7 * 11 * 13 * 17
+ * 19 * 23 * 29 * 31 * 37 * 41;
+
+ private static SecureRandom sr = null;
+
+ // the jacobi function uses this lookup table
+ private static final int[] jacobiTable = {0, 1, 0, -1, 0, -1, 0, 1};
+
+ private IntegerFunctions()
+ {
+ // empty
+ }
+
+ /**
+ * Computes the value of the Jacobi symbol (A|B). The following properties
+ * hold for the Jacobi symbol which makes it a very efficient way to
+ * evaluate the Legendre symbol
+ * <p/>
+ * (A|B) = 0 IF gcd(A,B) > 1<br>
+ * (-1|B) = 1 IF n = 1 (mod 1)<br>
+ * (-1|B) = -1 IF n = 3 (mod 4)<br>
+ * (A|B) (C|B) = (AC|B)<br>
+ * (A|B) (A|C) = (A|CB)<br>
+ * (A|B) = (C|B) IF A = C (mod B)<br>
+ * (2|B) = 1 IF N = 1 OR 7 (mod 8)<br>
+ * (2|B) = 1 IF N = 3 OR 5 (mod 8)
+ * <p/>
+ *
+ * @param A integer value
+ * @param B integer value
+ * @return value of the jacobi symbol (A|B)
+ */
+ public static int jacobi(BigInteger A, BigInteger B)
+ {
+ BigInteger a, b, v;
+ long k = 1;
+
+ k = 1;
+
+ // test trivial cases
+ if (B.equals(ZERO))
+ {
+ a = A.abs();
+ return a.equals(ONE) ? 1 : 0;
+ }
+
+ if (!A.testBit(0) && !B.testBit(0))
+ {
+ return 0;
+ }
+
+ a = A;
+ b = B;
+
+ if (b.signum() == -1)
+ { // b < 0
+ b = b.negate(); // b = -b
+ if (a.signum() == -1)
+ {
+ k = -1;
+ }
+ }
+
+ v = ZERO;
+ while (!b.testBit(0))
+ {
+ v = v.add(ONE); // v = v + 1
+ b = b.divide(TWO); // b = b/2
+ }
+
+ if (v.testBit(0))
+ {
+ k = k * jacobiTable[a.intValue() & 7];
+ }
+
+ if (a.signum() < 0)
+ { // a < 0
+ if (b.testBit(1))
+ {
+ k = -k; // k = -k
+ }
+ a = a.negate(); // a = -a
+ }
+
+ // main loop
+ while (a.signum() != 0)
+ {
+ v = ZERO;
+ while (!a.testBit(0))
+ { // a is even
+ v = v.add(ONE);
+ a = a.divide(TWO);
+ }
+ if (v.testBit(0))
+ {
+ k = k * jacobiTable[b.intValue() & 7];
+ }
+
+ if (a.compareTo(b) < 0)
+ { // a < b
+ // swap and correct intermediate result
+ BigInteger x = a;
+ a = b;
+ b = x;
+ if (a.testBit(1) && b.testBit(1))
+ {
+ k = -k;
+ }
+ }
+ a = a.subtract(b);
+ }
+
+ return b.equals(ONE) ? (int)k : 0;
+ }
+
+ /**
+ * Computes the square root of a BigInteger modulo a prime employing the
+ * Shanks-Tonelli algorithm.
+ *
+ * @param a value out of which we extract the square root
+ * @param p prime modulus that determines the underlying field
+ * @return a number <tt>b</tt> such that b<sup>2</sup> = a (mod p) if
+ * <tt>a</tt> is a quadratic residue modulo <tt>p</tt>.
+ * @throws NoQuadraticResidueException if <tt>a</tt> is a quadratic non-residue modulo <tt>p</tt>
+ */
+ public static BigInteger ressol(BigInteger a, BigInteger p)
+ throws IllegalArgumentException
+ {
+
+ BigInteger v = null;
+
+ if (a.compareTo(ZERO) < 0)
+ {
+ a = a.add(p);
+ }
+
+ if (a.equals(ZERO))
+ {
+ return ZERO;
+ }
+
+ if (p.equals(TWO))
+ {
+ return a;
+ }
+
+ // p = 3 mod 4
+ if (p.testBit(0) && p.testBit(1))
+ {
+ if (jacobi(a, p) == 1)
+ { // a quadr. residue mod p
+ v = p.add(ONE); // v = p+1
+ v = v.shiftRight(2); // v = v/4
+ return a.modPow(v, p); // return a^v mod p
+ // return --> a^((p+1)/4) mod p
+ }
+ throw new IllegalArgumentException("No quadratic residue: " + a + ", " + p);
+ }
+
+ long t = 0;
+
+ // initialization
+ // compute k and s, where p = 2^s (2k+1) +1
+
+ BigInteger k = p.subtract(ONE); // k = p-1
+ long s = 0;
+ while (!k.testBit(0))
+ { // while k is even
+ s++; // s = s+1
+ k = k.shiftRight(1); // k = k/2
+ }
+
+ k = k.subtract(ONE); // k = k - 1
+ k = k.shiftRight(1); // k = k/2
+
+ // initial values
+ BigInteger r = a.modPow(k, p); // r = a^k mod p
+
+ BigInteger n = r.multiply(r).remainder(p); // n = r^2 % p
+ n = n.multiply(a).remainder(p); // n = n * a % p
+ r = r.multiply(a).remainder(p); // r = r * a %p
+
+ if (n.equals(ONE))
+ {
+ return r;
+ }
+
+ // non-quadratic residue
+ BigInteger z = TWO; // z = 2
+ while (jacobi(z, p) == 1)
+ {
+ // while z quadratic residue
+ z = z.add(ONE); // z = z + 1
+ }
+
+ v = k;
+ v = v.multiply(TWO); // v = 2k
+ v = v.add(ONE); // v = 2k + 1
+ BigInteger c = z.modPow(v, p); // c = z^v mod p
+
+ // iteration
+ while (n.compareTo(ONE) == 1)
+ { // n > 1
+ k = n; // k = n
+ t = s; // t = s
+ s = 0;
+
+ while (!k.equals(ONE))
+ { // k != 1
+ k = k.multiply(k).mod(p); // k = k^2 % p
+ s++; // s = s + 1
+ }
+
+ t -= s; // t = t - s
+ if (t == 0)
+ {
+ throw new IllegalArgumentException("No quadratic residue: " + a + ", " + p);
+ }
+
+ v = ONE;
+ for (long i = 0; i < t - 1; i++)
+ {
+ v = v.shiftLeft(1); // v = 1 * 2^(t - 1)
+ }
+ c = c.modPow(v, p); // c = c^v mod p
+ r = r.multiply(c).remainder(p); // r = r * c % p
+ c = c.multiply(c).remainder(p); // c = c^2 % p
+ n = n.multiply(c).mod(p); // n = n * c % p
+ }
+ return r;
+ }
+
+ /**
+ * Computes the greatest common divisor of the two specified integers
+ *
+ * @param u - first integer
+ * @param v - second integer
+ * @return gcd(a, b)
+ */
+ public static int gcd(int u, int v)
+ {
+ return BigInteger.valueOf(u).gcd(BigInteger.valueOf(v)).intValue();
+ }
+
+ /**
+ * Extended euclidian algorithm (computes gcd and representation).
+ *
+ * @param a the first integer
+ * @param b the second integer
+ * @return <tt>(g,u,v)</tt>, where <tt>g = gcd(abs(a),abs(b)) = ua + vb</tt>
+ */
+ public static int[] extGCD(int a, int b)
+ {
+ BigInteger ba = BigInteger.valueOf(a);
+ BigInteger bb = BigInteger.valueOf(b);
+ BigInteger[] bresult = extgcd(ba, bb);
+ int[] result = new int[3];
+ result[0] = bresult[0].intValue();
+ result[1] = bresult[1].intValue();
+ result[2] = bresult[2].intValue();
+ return result;
+ }
+
+ public static BigInteger divideAndRound(BigInteger a, BigInteger b)
+ {
+ if (a.signum() < 0)
+ {
+ return divideAndRound(a.negate(), b).negate();
+ }
+ if (b.signum() < 0)
+ {
+ return divideAndRound(a, b.negate()).negate();
+ }
+ return a.shiftLeft(1).add(b).divide(b.shiftLeft(1));
+ }
+
+ public static BigInteger[] divideAndRound(BigInteger[] a, BigInteger b)
+ {
+ BigInteger[] out = new BigInteger[a.length];
+ for (int i = 0; i < a.length; i++)
+ {
+ out[i] = divideAndRound(a[i], b);
+ }
+ return out;
+ }
+
+ /**
+ * Compute the smallest integer that is greater than or equal to the
+ * logarithm to the base 2 of the given BigInteger.
+ *
+ * @param a the integer
+ * @return ceil[log(a)]
+ */
+ public static int ceilLog(BigInteger a)
+ {
+ int result = 0;
+ BigInteger p = ONE;
+ while (p.compareTo(a) < 0)
+ {
+ result++;
+ p = p.shiftLeft(1);
+ }
+ return result;
+ }
+
+ /**
+ * Compute the smallest integer that is greater than or equal to the
+ * logarithm to the base 2 of the given integer.
+ *
+ * @param a the integer
+ * @return ceil[log(a)]
+ */
+ public static int ceilLog(int a)
+ {
+ int log = 0;
+ int i = 1;
+ while (i < a)
+ {
+ i <<= 1;
+ log++;
+ }
+ return log;
+ }
+
+ /**
+ * Compute <tt>ceil(log_256 n)</tt>, the number of bytes needed to encode
+ * the integer <tt>n</tt>.
+ *
+ * @param n the integer
+ * @return the number of bytes needed to encode <tt>n</tt>
+ */
+ public static int ceilLog256(int n)
+ {
+ if (n == 0)
+ {
+ return 1;
+ }
+ int m;
+ if (n < 0)
+ {
+ m = -n;
+ }
+ else
+ {
+ m = n;
+ }
+
+ int d = 0;
+ while (m > 0)
+ {
+ d++;
+ m >>>= 8;
+ }
+ return d;
+ }
+
+ /**
+ * Compute <tt>ceil(log_256 n)</tt>, the number of bytes needed to encode
+ * the long integer <tt>n</tt>.
+ *
+ * @param n the long integer
+ * @return the number of bytes needed to encode <tt>n</tt>
+ */
+ public static int ceilLog256(long n)
+ {
+ if (n == 0)
+ {
+ return 1;
+ }
+ long m;
+ if (n < 0)
+ {
+ m = -n;
+ }
+ else
+ {
+ m = n;
+ }
+
+ int d = 0;
+ while (m > 0)
+ {
+ d++;
+ m >>>= 8;
+ }
+ return d;
+ }
+
+ /**
+ * Compute the integer part of the logarithm to the base 2 of the given
+ * integer.
+ *
+ * @param a the integer
+ * @return floor[log(a)]
+ */
+ public static int floorLog(BigInteger a)
+ {
+ int result = -1;
+ BigInteger p = ONE;
+ while (p.compareTo(a) <= 0)
+ {
+ result++;
+ p = p.shiftLeft(1);
+ }
+ return result;
+ }
+
+ /**
+ * Compute the integer part of the logarithm to the base 2 of the given
+ * integer.
+ *
+ * @param a the integer
+ * @return floor[log(a)]
+ */
+ public static int floorLog(int a)
+ {
+ int h = 0;
+ if (a <= 0)
+ {
+ return -1;
+ }
+ int p = a >>> 1;
+ while (p > 0)
+ {
+ h++;
+ p >>>= 1;
+ }
+
+ return h;
+ }
+
+ /**
+ * Compute the largest <tt>h</tt> with <tt>2^h | a</tt> if <tt>a!=0</tt>.
+ *
+ * @param a an integer
+ * @return the largest <tt>h</tt> with <tt>2^h | a</tt> if <tt>a!=0</tt>,
+ * <tt>0</tt> otherwise
+ */
+ public static int maxPower(int a)
+ {
+ int h = 0;
+ if (a != 0)
+ {
+ int p = 1;
+ while ((a & p) == 0)
+ {
+ h++;
+ p <<= 1;
+ }
+ }
+
+ return h;
+ }
+
+ /**
+ * @param a an integer
+ * @return the number of ones in the binary representation of an integer
+ * <tt>a</tt>
+ */
+ public static int bitCount(int a)
+ {
+ int h = 0;
+ while (a != 0)
+ {
+ h += a & 1;
+ a >>>= 1;
+ }
+
+ return h;
+ }
+
+ /**
+ * determines the order of g modulo p, p prime and 1 < g < p. This algorithm
+ * is only efficient for small p (see X9.62-1998, p. 68).
+ *
+ * @param g an integer with 1 < g < p
+ * @param p a prime
+ * @return the order k of g (that is k is the smallest integer with
+ * g<sup>k</sup> = 1 mod p
+ */
+ public static int order(int g, int p)
+ {
+ int b, j;
+
+ b = g % p; // Reduce g mod p first.
+ j = 1;
+
+ // Check whether g == 0 mod p (avoiding endless loop).
+ if (b == 0)
+ {
+ throw new IllegalArgumentException(g + " is not an element of Z/("
+ + p + "Z)^*; it is not meaningful to compute its order.");
+ }
+
+ // Compute the order of g mod p:
+ while (b != 1)
+ {
+ b *= g;
+ b %= p;
+ if (b < 0)
+ {
+ b += p;
+ }
+ j++;
+ }
+
+ return j;
+ }
+
+ /**
+ * Reduces an integer into a given interval
+ *
+ * @param n - the integer
+ * @param begin - left bound of the interval
+ * @param end - right bound of the interval
+ * @return <tt>n</tt> reduced into <tt>[begin,end]</tt>
+ */
+ public static BigInteger reduceInto(BigInteger n, BigInteger begin,
+ BigInteger end)
+ {
+ return n.subtract(begin).mod(end.subtract(begin)).add(begin);
+ }
+
+ /**
+ * Compute <tt>a<sup>e</sup></tt>.
+ *
+ * @param a the base
+ * @param e the exponent
+ * @return <tt>a<sup>e</sup></tt>
+ */
+ public static int pow(int a, int e)
+ {
+ int result = 1;
+ while (e > 0)
+ {
+ if ((e & 1) == 1)
+ {
+ result *= a;
+ }
+ a *= a;
+ e >>>= 1;
+ }
+ return result;
+ }
+
+ /**
+ * Compute <tt>a<sup>e</sup></tt>.
+ *
+ * @param a the base
+ * @param e the exponent
+ * @return <tt>a<sup>e</sup></tt>
+ */
+ public static long pow(long a, int e)
+ {
+ long result = 1;
+ while (e > 0)
+ {
+ if ((e & 1) == 1)
+ {
+ result *= a;
+ }
+ a *= a;
+ e >>>= 1;
+ }
+ return result;
+ }
+
+ /**
+ * Compute <tt>a<sup>e</sup> mod n</tt>.
+ *
+ * @param a the base
+ * @param e the exponent
+ * @param n the modulus
+ * @return <tt>a<sup>e</sup> mod n</tt>
+ */
+ public static int modPow(int a, int e, int n)
+ {
+ if (n <= 0 || (n * n) > Integer.MAX_VALUE || e < 0)
+ {
+ return 0;
+ }
+ int result = 1;
+ a = (a % n + n) % n;
+ while (e > 0)
+ {
+ if ((e & 1) == 1)
+ {
+ result = (result * a) % n;
+ }
+ a = (a * a) % n;
+ e >>>= 1;
+ }
+ return result;
+ }
+
+ /**
+ * Extended euclidian algorithm (computes gcd and representation).
+ *
+ * @param a - the first integer
+ * @param b - the second integer
+ * @return <tt>(d,u,v)</tt>, where <tt>d = gcd(a,b) = ua + vb</tt>
+ */
+ public static BigInteger[] extgcd(BigInteger a, BigInteger b)
+ {
+ BigInteger u = ONE;
+ BigInteger v = ZERO;
+ BigInteger d = a;
+ if (b.signum() != 0)
+ {
+ BigInteger v1 = ZERO;
+ BigInteger v3 = b;
+ while (v3.signum() != 0)
+ {
+ BigInteger[] tmp = d.divideAndRemainder(v3);
+ BigInteger q = tmp[0];
+ BigInteger t3 = tmp[1];
+ BigInteger t1 = u.subtract(q.multiply(v1));
+ u = v1;
+ d = v3;
+ v1 = t1;
+ v3 = t3;
+ }
+ v = d.subtract(a.multiply(u)).divide(b);
+ }
+ return new BigInteger[]{d, u, v};
+ }
+
+ /**
+ * Computation of the least common multiple of a set of BigIntegers.
+ *
+ * @param numbers - the set of numbers
+ * @return the lcm(numbers)
+ */
+ public static BigInteger leastCommonMultiple(BigInteger[] numbers)
+ {
+ int n = numbers.length;
+ BigInteger result = numbers[0];
+ for (int i = 1; i < n; i++)
+ {
+ BigInteger gcd = result.gcd(numbers[i]);
+ result = result.multiply(numbers[i]).divide(gcd);
+ }
+ return result;
+ }
+
+ /**
+ * Returns a long integer whose value is <tt>(a mod m</tt>). This method
+ * differs from <tt>%</tt> in that it always returns a <i>non-negative</i>
+ * integer.
+ *
+ * @param a value on which the modulo operation has to be performed.
+ * @param m the modulus.
+ * @return <tt>a mod m</tt>
+ */
+ public static long mod(long a, long m)
+ {
+ long result = a % m;
+ if (result < 0)
+ {
+ result += m;
+ }
+ return result;
+ }
+
+ /**
+ * Computes the modular inverse of an integer a
+ *
+ * @param a - the integer to invert
+ * @param mod - the modulus
+ * @return <tt>a<sup>-1</sup> mod n</tt>
+ */
+ public static int modInverse(int a, int mod)
+ {
+ return BigInteger.valueOf(a).modInverse(BigInteger.valueOf(mod))
+ .intValue();
+ }
+
+ /**
+ * Computes the modular inverse of an integer a
+ *
+ * @param a - the integer to invert
+ * @param mod - the modulus
+ * @return <tt>a<sup>-1</sup> mod n</tt>
+ */
+ public static long modInverse(long a, long mod)
+ {
+ return BigInteger.valueOf(a).modInverse(BigInteger.valueOf(mod))
+ .longValue();
+ }
+
+ /**
+ * Tests whether an integer <tt>a</tt> is power of another integer
+ * <tt>p</tt>.
+ *
+ * @param a - the first integer
+ * @param p - the second integer
+ * @return n if a = p^n or -1 otherwise
+ */
+ public static int isPower(int a, int p)
+ {
+ if (a <= 0)
+ {
+ return -1;
+ }
+ int n = 0;
+ int d = a;
+ while (d > 1)
+ {
+ if (d % p != 0)
+ {
+ return -1;
+ }
+ d /= p;
+ n++;
+ }
+ return n;
+ }
+
+ /**
+ * Find and return the least non-trivial divisor of an integer <tt>a</tt>.
+ *
+ * @param a - the integer
+ * @return divisor p >1 or 1 if a = -1,0,1
+ */
+ public static int leastDiv(int a)
+ {
+ if (a < 0)
+ {
+ a = -a;
+ }
+ if (a == 0)
+ {
+ return 1;
+ }
+ if ((a & 1) == 0)
+ {
+ return 2;
+ }
+ int p = 3;
+ while (p <= (a / p))
+ {
+ if ((a % p) == 0)
+ {
+ return p;
+ }
+ p += 2;
+ }
+
+ return a;
+ }
+
+ /**
+ * Miller-Rabin-Test, determines wether the given integer is probably prime
+ * or composite. This method returns <tt>true</tt> if the given integer is
+ * prime with probability <tt>1 - 2<sup>-20</sup></tt>.
+ *
+ * @param n the integer to test for primality
+ * @return <tt>true</tt> if the given integer is prime with probability
+ * 2<sup>-100</sup>, <tt>false</tt> otherwise
+ */
+ public static boolean isPrime(int n)
+ {
+ if (n < 2)
+ {
+ return false;
+ }
+ if (n == 2)
+ {
+ return true;
+ }
+ if ((n & 1) == 0)
+ {
+ return false;
+ }
+ if (n < 42)
+ {
+ for (int i = 0; i < SMALL_PRIMES.length; i++)
+ {
+ if (n == SMALL_PRIMES[i])
+ {
+ return true;
+ }
+ }
+ }
+
+ if ((n % 3 == 0) || (n % 5 == 0) || (n % 7 == 0) || (n % 11 == 0)
+ || (n % 13 == 0) || (n % 17 == 0) || (n % 19 == 0)
+ || (n % 23 == 0) || (n % 29 == 0) || (n % 31 == 0)
+ || (n % 37 == 0) || (n % 41 == 0))
+ {
+ return false;
+ }
+
+ return BigInteger.valueOf(n).isProbablePrime(20);
+ }
+
+ /**
+ * Short trial-division test to find out whether a number is not prime. This
+ * test is usually used before a Miller-Rabin primality test.
+ *
+ * @param candidate the number to test
+ * @return <tt>true</tt> if the number has no factor of the tested primes,
+ * <tt>false</tt> if the number is definitely composite
+ */
+ public static boolean passesSmallPrimeTest(BigInteger candidate)
+ {
+ final int[] smallPrime = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37,
+ 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103,
+ 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167,
+ 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233,
+ 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307,
+ 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379,
+ 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449,
+ 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523,
+ 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607,
+ 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677,
+ 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761,
+ 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853,
+ 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937,
+ 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019,
+ 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087,
+ 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153,
+ 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229,
+ 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297,
+ 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381,
+ 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453,
+ 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499};
+
+ for (int i = 0; i < smallPrime.length; i++)
+ {
+ if (candidate.mod(BigInteger.valueOf(smallPrime[i])).equals(
+ ZERO))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns the largest prime smaller than the given integer
+ *
+ * @param n - upper bound
+ * @return the largest prime smaller than <tt>n</tt>, or <tt>1</tt> if
+ * <tt>n &lt;= 2</tt>
+ */
+ public static int nextSmallerPrime(int n)
+ {
+ if (n <= 2)
+ {
+ return 1;
+ }
+
+ if (n == 3)
+ {
+ return 2;
+ }
+
+ if ((n & 1) == 0)
+ {
+ n--;
+ }
+ else
+ {
+ n -= 2;
+ }
+
+ while (n > 3 & !isPrime(n))
+ {
+ n -= 2;
+ }
+ return n;
+ }
+
+ /**
+ * Compute the next probable prime greater than <tt>n</tt> with the
+ * specified certainty.
+ *
+ * @param n a integer number
+ * @param certainty the certainty that the generated number is prime
+ * @return the next prime greater than <tt>n</tt>
+ */
+ public static BigInteger nextProbablePrime(BigInteger n, int certainty)
+ {
+
+ if (n.signum() < 0 || n.signum() == 0 || n.equals(ONE))
+ {
+ return TWO;
+ }
+
+ BigInteger result = n.add(ONE);
+
+ // Ensure an odd number
+ if (!result.testBit(0))
+ {
+ result = result.add(ONE);
+ }
+
+ while (true)
+ {
+ // Do cheap "pre-test" if applicable
+ if (result.bitLength() > 6)
+ {
+ long r = result.remainder(
+ BigInteger.valueOf(SMALL_PRIME_PRODUCT)).longValue();
+ if ((r % 3 == 0) || (r % 5 == 0) || (r % 7 == 0)
+ || (r % 11 == 0) || (r % 13 == 0) || (r % 17 == 0)
+ || (r % 19 == 0) || (r % 23 == 0) || (r % 29 == 0)
+ || (r % 31 == 0) || (r % 37 == 0) || (r % 41 == 0))
+ {
+ result = result.add(TWO);
+ continue; // Candidate is composite; try another
+ }
+ }
+
+ // All candidates of bitLength 2 and 3 are prime by this point
+ if (result.bitLength() < 4)
+ {
+ return result;
+ }
+
+ // The expensive test
+ if (result.isProbablePrime(certainty))
+ {
+ return result;
+ }
+
+ result = result.add(TWO);
+ }
+ }
+
+ /**
+ * Compute the next probable prime greater than <tt>n</tt> with the default
+ * certainty (20).
+ *
+ * @param n a integer number
+ * @return the next prime greater than <tt>n</tt>
+ */
+ public static BigInteger nextProbablePrime(BigInteger n)
+ {
+ return nextProbablePrime(n, 20);
+ }
+
+ /**
+ * Computes the next prime greater than n.
+ *
+ * @param n a integer number
+ * @return the next prime greater than n
+ */
+ public static BigInteger nextPrime(long n)
+ {
+ long i;
+ boolean found = false;
+ long result = 0;
+
+ if (n <= 1)
+ {
+ return BigInteger.valueOf(2);
+ }
+ if (n == 2)
+ {
+ return BigInteger.valueOf(3);
+ }
+
+ for (i = n + 1 + (n & 1); (i <= n << 1) && !found; i += 2)
+ {
+ for (long j = 3; (j <= i >> 1) && !found; j += 2)
+ {
+ if (i % j == 0)
+ {
+ found = true;
+ }
+ }
+ if (found)
+ {
+ found = false;
+ }
+ else
+ {
+ result = i;
+ found = true;
+ }
+ }
+ return BigInteger.valueOf(result);
+ }
+
+ /**
+ * Computes the binomial coefficient (n|t) ("n over t"). Formula:<br/>
+ * <ul>
+ * <li>if n !=0 and t != 0 then (n|t) = Mult(i=1, t): (n-(i-1))/i</li>
+ * <li>if t = 0 then (n|t) = 1</li>
+ * <li>if n = 0 and t > 0 then (n|t) = 0</li>
+ * </ul>
+ *
+ * @param n - the "upper" integer
+ * @param t - the "lower" integer
+ * @return the binomialcoefficient "n over t" as BigInteger
+ */
+ public static BigInteger binomial(int n, int t)
+ {
+
+ BigInteger result = ONE;
+
+ if (n == 0)
+ {
+ if (t == 0)
+ {
+ return result;
+ }
+ return ZERO;
+ }
+
+ // the property (n|t) = (n|n-t) be used to reduce numbers of operations
+ if (t > (n >>> 1))
+ {
+ t = n - t;
+ }
+
+ for (int i = 1; i <= t; i++)
+ {
+ result = (result.multiply(BigInteger.valueOf(n - (i - 1))))
+ .divide(BigInteger.valueOf(i));
+ }
+
+ return result;
+ }
+
+ public static BigInteger randomize(BigInteger upperBound)
+ {
+ if (sr == null)
+ {
+ sr = new SecureRandom();
+ }
+ return randomize(upperBound, sr);
+ }
+
+ public static BigInteger randomize(BigInteger upperBound,
+ SecureRandom prng)
+ {
+ int blen = upperBound.bitLength();
+ BigInteger randomNum = BigInteger.valueOf(0);
+
+ if (prng == null)
+ {
+ prng = sr != null ? sr : new SecureRandom();
+ }
+
+ for (int i = 0; i < 20; i++)
+ {
+ randomNum = new BigInteger(blen, prng);
+ if (randomNum.compareTo(upperBound) < 0)
+ {
+ return randomNum;
+ }
+ }
+ return randomNum.mod(upperBound);
+ }
+
+ /**
+ * Extract the truncated square root of a BigInteger.
+ *
+ * @param a - value out of which we extract the square root
+ * @return the truncated square root of <tt>a</tt>
+ */
+ public static BigInteger squareRoot(BigInteger a)
+ {
+ int bl;
+ BigInteger result, remainder, b;
+
+ if (a.compareTo(ZERO) < 0)
+ {
+ throw new ArithmeticException(
+ "cannot extract root of negative number" + a + ".");
+ }
+
+ bl = a.bitLength();
+ result = ZERO;
+ remainder = ZERO;
+
+ // if the bit length is odd then extra step
+ if ((bl & 1) != 0)
+ {
+ result = result.add(ONE);
+ bl--;
+ }
+
+ while (bl > 0)
+ {
+ remainder = remainder.multiply(FOUR);
+ remainder = remainder.add(BigInteger.valueOf((a.testBit(--bl) ? 2
+ : 0)
+ + (a.testBit(--bl) ? 1 : 0)));
+ b = result.multiply(FOUR).add(ONE);
+ result = result.multiply(TWO);
+ if (remainder.compareTo(b) != -1)
+ {
+ result = result.add(ONE);
+ remainder = remainder.subtract(b);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Takes an approximation of the root from an integer base, using newton's
+ * algorithm
+ *
+ * @param base the base to take the root from
+ * @param root the root, for example 2 for a square root
+ */
+ public static float intRoot(int base, int root)
+ {
+ float gNew = base / root;
+ float gOld = 0;
+ int counter = 0;
+ while (Math.abs(gOld - gNew) > 0.0001)
+ {
+ float gPow = floatPow(gNew, root);
+ while (Float.isInfinite(gPow))
+ {
+ gNew = (gNew + gOld) / 2;
+ gPow = floatPow(gNew, root);
+ }
+ counter += 1;
+ gOld = gNew;
+ gNew = gOld - (gPow - base) / (root * floatPow(gOld, root - 1));
+ }
+ return gNew;
+ }
+
+ /**
+ * Calculation of a logarithmus of a float param
+ *
+ * @param param
+ * @return
+ */
+ public static float floatLog(float param)
+ {
+ double arg = (param - 1) / (param + 1);
+ double arg2 = arg;
+ int counter = 1;
+ float result = (float)arg;
+
+ while (arg2 > 0.001)
+ {
+ counter += 2;
+ arg2 *= arg * arg;
+ result += (1. / counter) * arg2;
+ }
+ return 2 * result;
+ }
+
+ /**
+ * int power of a base float, only use for small ints
+ *
+ * @param f
+ * @param i
+ * @return
+ */
+ public static float floatPow(float f, int i)
+ {
+ float g = 1;
+ for (; i > 0; i--)
+ {
+ g *= f;
+ }
+ return g;
+ }
+
+ /**
+ * calculate the logarithm to the base 2.
+ *
+ * @param x any double value
+ * @return log_2(x)
+ * @deprecated use MathFunctions.log(double) instead
+ */
+ public static double log(double x)
+ {
+ if (x > 0 && x < 1)
+ {
+ double d = 1 / x;
+ double result = -log(d);
+ return result;
+ }
+
+ int tmp = 0;
+ double tmp2 = 1;
+ double d = x;
+
+ while (d > 2)
+ {
+ d = d / 2;
+ tmp += 1;
+ tmp2 *= 2;
+ }
+ double rem = x / tmp2;
+ rem = logBKM(rem);
+ return tmp + rem;
+ }
+
+ /**
+ * calculate the logarithm to the base 2.
+ *
+ * @param x any long value >=1
+ * @return log_2(x)
+ * @deprecated use MathFunctions.log(long) instead
+ */
+ public static double log(long x)
+ {
+ int tmp = floorLog(BigInteger.valueOf(x));
+ long tmp2 = 1 << tmp;
+ double rem = (double)x / (double)tmp2;
+ rem = logBKM(rem);
+ return tmp + rem;
+ }
+
+ /**
+ * BKM Algorithm to calculate logarithms to the base 2.
+ *
+ * @param arg a double value with 1<= arg<= 4.768462058
+ * @return log_2(arg)
+ * @deprecated use MathFunctions.logBKM(double) instead
+ */
+ private static double logBKM(double arg)
+ {
+ double ae[] = // A_e[k] = log_2 (1 + 0.5^k)
+ {
+ 1.0000000000000000000000000000000000000000000000000000000000000000000000000000,
+ 0.5849625007211561814537389439478165087598144076924810604557526545410982276485,
+ 0.3219280948873623478703194294893901758648313930245806120547563958159347765589,
+ 0.1699250014423123629074778878956330175196288153849621209115053090821964552970,
+ 0.0874628412503394082540660108104043540112672823448206881266090643866965081686,
+ 0.0443941193584534376531019906736094674630459333742491317685543002674288465967,
+ 0.0223678130284545082671320837460849094932677948156179815932199216587899627785,
+ 0.0112272554232541203378805844158839407281095943600297940811823651462712311786,
+ 0.0056245491938781069198591026740666017211096815383520359072957784732489771013,
+ 0.0028150156070540381547362547502839489729507927389771959487826944878598909400,
+ 0.0014081943928083889066101665016890524233311715793462235597709051792834906001,
+ 0.0007042690112466432585379340422201964456668872087249334581924550139514213168,
+ 0.0003521774803010272377989609925281744988670304302127133979341729842842377649,
+ 0.0001760994864425060348637509459678580940163670081839283659942864068257522373,
+ 0.0000880524301221769086378699983597183301490534085738474534831071719854721939,
+ 0.0000440268868273167176441087067175806394819146645511899503059774914593663365,
+ 0.0000220136113603404964890728830697555571275493801909791504158295359319433723,
+ 0.0000110068476674814423006223021573490183469930819844945565597452748333526464,
+ 0.0000055034343306486037230640321058826431606183125807276574241540303833251704,
+ 0.0000027517197895612831123023958331509538486493412831626219340570294203116559,
+ 0.0000013758605508411382010566802834037147561973553922354232704569052932922954,
+ 0.0000006879304394358496786728937442939160483304056131990916985043387874690617,
+ 0.0000003439652607217645360118314743718005315334062644619363447395987584138324,
+ 0.0000001719826406118446361936972479533123619972434705828085978955697643547921,
+ 0.0000000859913228686632156462565208266682841603921494181830811515318381744650,
+ 0.0000000429956620750168703982940244684787907148132725669106053076409624949917,
+ 0.0000000214978311976797556164155504126645192380395989504741781512309853438587,
+ 0.0000000107489156388827085092095702361647949603617203979413516082280717515504,
+ 0.0000000053744578294520620044408178949217773318785601260677517784797554422804,
+ 0.0000000026872289172287079490026152352638891824761667284401180026908031182361,
+ 0.0000000013436144592400232123622589569799954658536700992739887706412976115422,
+ 0.0000000006718072297764289157920422846078078155859484240808550018085324187007,
+ 0.0000000003359036149273187853169587152657145221968468364663464125722491530858,
+ 0.0000000001679518074734354745159899223037458278711244127245990591908996412262,
+ 0.0000000000839759037391617577226571237484864917411614198675604731728132152582,
+ 0.0000000000419879518701918839775296677020135040214077417929807824842667285938,
+ 0.0000000000209939759352486932678195559552767641474249812845414125580747434389,
+ 0.0000000000104969879676625344536740142096218372850561859495065136990936290929,
+ 0.0000000000052484939838408141817781356260462777942148580518406975851213868092,
+ 0.0000000000026242469919227938296243586262369156865545638305682553644113887909,
+ 0.0000000000013121234959619935994960031017850191710121890821178731821983105443,
+ 0.0000000000006560617479811459709189576337295395590603644549624717910616347038,
+ 0.0000000000003280308739906102782522178545328259781415615142931952662153623493,
+ 0.0000000000001640154369953144623242936888032768768777422997704541618141646683,
+ 0.0000000000000820077184976595619616930350508356401599552034612281802599177300,
+ 0.0000000000000410038592488303636807330652208397742314215159774270270147020117,
+ 0.0000000000000205019296244153275153381695384157073687186580546938331088730952,
+ 0.0000000000000102509648122077001764119940017243502120046885379813510430378661,
+ 0.0000000000000051254824061038591928917243090559919209628584150482483994782302,
+ 0.0000000000000025627412030519318726172939815845367496027046030028595094737777,
+ 0.0000000000000012813706015259665053515049475574143952543145124550608158430592,
+ 0.0000000000000006406853007629833949364669629701200556369782295210193569318434,
+ 0.0000000000000003203426503814917330334121037829290364330169106716787999052925,
+ 0.0000000000000001601713251907458754080007074659337446341494733882570243497196,
+ 0.0000000000000000800856625953729399268240176265844257044861248416330071223615,
+ 0.0000000000000000400428312976864705191179247866966320469710511619971334577509,
+ 0.0000000000000000200214156488432353984854413866994246781519154793320684126179,
+ 0.0000000000000000100107078244216177339743404416874899847406043033792202127070,
+ 0.0000000000000000050053539122108088756700751579281894640362199287591340285355,
+ 0.0000000000000000025026769561054044400057638132352058574658089256646014899499,
+ 0.0000000000000000012513384780527022205455634651853807110362316427807660551208,
+ 0.0000000000000000006256692390263511104084521222346348012116229213309001913762,
+ 0.0000000000000000003128346195131755552381436585278035120438976487697544916191,
+ 0.0000000000000000001564173097565877776275512286165232838833090480508502328437,
+ 0.0000000000000000000782086548782938888158954641464170239072244145219054734086,
+ 0.0000000000000000000391043274391469444084776945327473574450334092075712154016,
+ 0.0000000000000000000195521637195734722043713378812583900953755962557525252782,
+ 0.0000000000000000000097760818597867361022187915943503728909029699365320287407,
+ 0.0000000000000000000048880409298933680511176764606054809062553340323879609794,
+ 0.0000000000000000000024440204649466840255609083961603140683286362962192177597,
+ 0.0000000000000000000012220102324733420127809717395445504379645613448652614939,
+ 0.0000000000000000000006110051162366710063906152551383735699323415812152114058,
+ 0.0000000000000000000003055025581183355031953399739107113727036860315024588989,
+ 0.0000000000000000000001527512790591677515976780735407368332862218276873443537,
+ 0.0000000000000000000000763756395295838757988410584167137033767056170417508383,
+ 0.0000000000000000000000381878197647919378994210346199431733717514843471513618,
+ 0.0000000000000000000000190939098823959689497106436628681671067254111334889005,
+ 0.0000000000000000000000095469549411979844748553534196582286585751228071408728,
+ 0.0000000000000000000000047734774705989922374276846068851506055906657137209047,
+ 0.0000000000000000000000023867387352994961187138442777065843718711089344045782,
+ 0.0000000000000000000000011933693676497480593569226324192944532044984865894525,
+ 0.0000000000000000000000005966846838248740296784614396011477934194852481410926,
+ 0.0000000000000000000000002983423419124370148392307506484490384140516252814304,
+ 0.0000000000000000000000001491711709562185074196153830361933046331030629430117,
+ 0.0000000000000000000000000745855854781092537098076934460888486730708440475045,
+ 0.0000000000000000000000000372927927390546268549038472050424734256652501673274,
+ 0.0000000000000000000000000186463963695273134274519237230207489851150821191330,
+ 0.0000000000000000000000000093231981847636567137259618916352525606281553180093,
+ 0.0000000000000000000000000046615990923818283568629809533488457973317312233323,
+ 0.0000000000000000000000000023307995461909141784314904785572277779202790023236,
+ 0.0000000000000000000000000011653997730954570892157452397493151087737428485431,
+ 0.0000000000000000000000000005826998865477285446078726199923328593402722606924,
+ 0.0000000000000000000000000002913499432738642723039363100255852559084863397344,
+ 0.0000000000000000000000000001456749716369321361519681550201473345138307215067,
+ 0.0000000000000000000000000000728374858184660680759840775119123438968122488047,
+ 0.0000000000000000000000000000364187429092330340379920387564158411083803465567,
+ 0.0000000000000000000000000000182093714546165170189960193783228378441837282509,
+ 0.0000000000000000000000000000091046857273082585094980096891901482445902524441,
+ 0.0000000000000000000000000000045523428636541292547490048446022564529197237262,
+ 0.0000000000000000000000000000022761714318270646273745024223029238091160103901};
+ int n = 53;
+ double x = 1;
+ double y = 0;
+ double z;
+ double s = 1;
+ int k;
+
+ for (k = 0; k < n; k++)
+ {
+ z = x + x * s;
+ if (z <= arg)
+ {
+ x = z;
+ y += ae[k];
+ }
+ s *= 0.5;
+ }
+ return y;
+ }
+
+ public static boolean isIncreasing(int[] a)
+ {
+ for (int i = 1; i < a.length; i++)
+ {
+ if (a[i - 1] >= a[i])
+ {
+ System.out.println("a[" + (i - 1) + "] = " + a[i - 1] + " >= "
+ + a[i] + " = a[" + i + "]");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static byte[] integerToOctets(BigInteger val)
+ {
+ byte[] valBytes = val.abs().toByteArray();
+
+ // check whether the array includes a sign bit
+ if ((val.bitLength() & 7) != 0)
+ {
+ return valBytes;
+ }
+ // get rid of the sign bit (first byte)
+ byte[] tmp = new byte[val.bitLength() >> 3];
+ System.arraycopy(valBytes, 1, tmp, 0, tmp.length);
+ return tmp;
+ }
+
+ public static BigInteger octetsToInteger(byte[] data, int offset,
+ int length)
+ {
+ byte[] val = new byte[length + 1];
+
+ val[0] = 0;
+ System.arraycopy(data, offset, val, 1, length);
+ return new BigInteger(val);
+ }
+
+ public static BigInteger octetsToInteger(byte[] data)
+ {
+ return octetsToInteger(data, 0, data.length);
+ }
+
+ public static void main(String[] args)
+ {
+ System.out.println("test");
+ // System.out.println(intRoot(37, 5));
+ // System.out.println(floatPow((float)2.5, 4));
+ System.out.println(floatLog(10));
+ System.out.println("test2");
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/LittleEndianConversions.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/LittleEndianConversions.java
new file mode 100644
index 00000000..c97fdc58
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/LittleEndianConversions.java
@@ -0,0 +1,230 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+/**
+ * This is a utility class containing data type conversions using little-endian
+ * byte order.
+ *
+ * @see BigEndianConversions
+ */
+public final class LittleEndianConversions
+{
+
+ /**
+ * Default constructor (private).
+ */
+ private LittleEndianConversions()
+ {
+ // empty
+ }
+
+ /**
+ * Convert an octet string of length 4 to an integer. No length checking is
+ * performed.
+ *
+ * @param input the byte array holding the octet string
+ * @return an integer representing the octet string <tt>input</tt>
+ * @throws ArithmeticException if the length of the given octet string is larger than 4.
+ */
+ public static int OS2IP(byte[] input)
+ {
+ return ((input[0] & 0xff)) | ((input[1] & 0xff) << 8)
+ | ((input[2] & 0xff) << 16) | ((input[3] & 0xff)) << 24;
+ }
+
+ /**
+ * Convert an byte array of length 4 beginning at <tt>offset</tt> into an
+ * integer.
+ *
+ * @param input the byte array
+ * @param inOff the offset into the byte array
+ * @return the resulting integer
+ */
+ public static int OS2IP(byte[] input, int inOff)
+ {
+ int result = input[inOff++] & 0xff;
+ result |= (input[inOff++] & 0xff) << 8;
+ result |= (input[inOff++] & 0xff) << 16;
+ result |= (input[inOff] & 0xff) << 24;
+ return result;
+ }
+
+ /**
+ * Convert a byte array of the given length beginning at <tt>offset</tt>
+ * into an integer.
+ *
+ * @param input the byte array
+ * @param inOff the offset into the byte array
+ * @param inLen the length of the encoding
+ * @return the resulting integer
+ */
+ public static int OS2IP(byte[] input, int inOff, int inLen)
+ {
+ int result = 0;
+ for (int i = inLen - 1; i >= 0; i--)
+ {
+ result |= (input[inOff + i] & 0xff) << (8 * i);
+ }
+ return result;
+ }
+
+ /**
+ * Convert a byte array of length 8 beginning at <tt>inOff</tt> into a
+ * long integer.
+ *
+ * @param input the byte array
+ * @param inOff the offset into the byte array
+ * @return the resulting long integer
+ */
+ public static long OS2LIP(byte[] input, int inOff)
+ {
+ long result = input[inOff++] & 0xff;
+ result |= (input[inOff++] & 0xff) << 8;
+ result |= (input[inOff++] & 0xff) << 16;
+ result |= ((long)input[inOff++] & 0xff) << 24;
+ result |= ((long)input[inOff++] & 0xff) << 32;
+ result |= ((long)input[inOff++] & 0xff) << 40;
+ result |= ((long)input[inOff++] & 0xff) << 48;
+ result |= ((long)input[inOff++] & 0xff) << 56;
+ return result;
+ }
+
+ /**
+ * Convert an integer to an octet string of length 4.
+ *
+ * @param x the integer to convert
+ * @return the converted integer
+ */
+ public static byte[] I2OSP(int x)
+ {
+ byte[] result = new byte[4];
+ result[0] = (byte)x;
+ result[1] = (byte)(x >>> 8);
+ result[2] = (byte)(x >>> 16);
+ result[3] = (byte)(x >>> 24);
+ return result;
+ }
+
+ /**
+ * Convert an integer into a byte array beginning at the specified offset.
+ *
+ * @param value the integer to convert
+ * @param output the byte array to hold the result
+ * @param outOff the integer offset into the byte array
+ */
+ public static void I2OSP(int value, byte[] output, int outOff)
+ {
+ output[outOff++] = (byte)value;
+ output[outOff++] = (byte)(value >>> 8);
+ output[outOff++] = (byte)(value >>> 16);
+ output[outOff++] = (byte)(value >>> 24);
+ }
+
+ /**
+ * Convert an integer to a byte array beginning at the specified offset. No
+ * length checking is performed (i.e., if the integer cannot be encoded with
+ * <tt>length</tt> octets, it is truncated).
+ *
+ * @param value the integer to convert
+ * @param output the byte array to hold the result
+ * @param outOff the integer offset into the byte array
+ * @param outLen the length of the encoding
+ */
+ public static void I2OSP(int value, byte[] output, int outOff, int outLen)
+ {
+ for (int i = outLen - 1; i >= 0; i--)
+ {
+ output[outOff + i] = (byte)(value >>> (8 * i));
+ }
+ }
+
+ /**
+ * Convert an integer to a byte array of length 8.
+ *
+ * @param input the integer to convert
+ * @return the converted integer
+ */
+ public static byte[] I2OSP(long input)
+ {
+ byte[] output = new byte[8];
+ output[0] = (byte)input;
+ output[1] = (byte)(input >>> 8);
+ output[2] = (byte)(input >>> 16);
+ output[3] = (byte)(input >>> 24);
+ output[4] = (byte)(input >>> 32);
+ output[5] = (byte)(input >>> 40);
+ output[6] = (byte)(input >>> 48);
+ output[7] = (byte)(input >>> 56);
+ return output;
+ }
+
+ /**
+ * Convert an integer to a byte array of length 8.
+ *
+ * @param input the integer to convert
+ * @param output byte array holding the output
+ * @param outOff offset in output array where the result is stored
+ */
+ public static void I2OSP(long input, byte[] output, int outOff)
+ {
+ output[outOff++] = (byte)input;
+ output[outOff++] = (byte)(input >>> 8);
+ output[outOff++] = (byte)(input >>> 16);
+ output[outOff++] = (byte)(input >>> 24);
+ output[outOff++] = (byte)(input >>> 32);
+ output[outOff++] = (byte)(input >>> 40);
+ output[outOff++] = (byte)(input >>> 48);
+ output[outOff] = (byte)(input >>> 56);
+ }
+
+ /**
+ * Convert an int array to a byte array of the specified length. No length
+ * checking is performed (i.e., if the last integer cannot be encoded with
+ * <tt>length % 4</tt> octets, it is truncated).
+ *
+ * @param input the int array
+ * @param outLen the length of the converted array
+ * @return the converted array
+ */
+ public static byte[] toByteArray(int[] input, int outLen)
+ {
+ int intLen = input.length;
+ byte[] result = new byte[outLen];
+ int index = 0;
+ for (int i = 0; i <= intLen - 2; i++, index += 4)
+ {
+ I2OSP(input[i], result, index);
+ }
+ I2OSP(input[intLen - 1], result, index, outLen - index);
+ return result;
+ }
+
+ /**
+ * Convert a byte array to an int array.
+ *
+ * @param input the byte array
+ * @return the converted array
+ */
+ public static int[] toIntArray(byte[] input)
+ {
+ int intLen = (input.length + 3) / 4;
+ int lastLen = input.length & 0x03;
+ int[] result = new int[intLen];
+
+ int index = 0;
+ for (int i = 0; i <= intLen - 2; i++, index += 4)
+ {
+ result[i] = OS2IP(input, index);
+ }
+ if (lastLen != 0)
+ {
+ result[intLen - 1] = OS2IP(input, index, lastLen);
+ }
+ else
+ {
+ result[intLen - 1] = OS2IP(input, index);
+ }
+
+ return result;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Matrix.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Matrix.java
new file mode 100644
index 00000000..2c9a0eb6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Matrix.java
@@ -0,0 +1,131 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+/**
+ * This abstract class defines matrices. It holds the number of rows and the
+ * number of columns of the matrix and defines some basic methods.
+ */
+public abstract class Matrix
+{
+
+ /**
+ * number of rows
+ */
+ protected int numRows;
+
+ /**
+ * number of columns
+ */
+ protected int numColumns;
+
+ // ----------------------------------------------------
+ // some constants (matrix types)
+ // ----------------------------------------------------
+
+ /**
+ * zero matrix
+ */
+ public static final char MATRIX_TYPE_ZERO = 'Z';
+
+ /**
+ * unit matrix
+ */
+ public static final char MATRIX_TYPE_UNIT = 'I';
+
+ /**
+ * random lower triangular matrix
+ */
+ public static final char MATRIX_TYPE_RANDOM_LT = 'L';
+
+ /**
+ * random upper triangular matrix
+ */
+ public static final char MATRIX_TYPE_RANDOM_UT = 'U';
+
+ /**
+ * random regular matrix
+ */
+ public static final char MATRIX_TYPE_RANDOM_REGULAR = 'R';
+
+ // ----------------------------------------------------
+ // getters
+ // ----------------------------------------------------
+
+ /**
+ * @return the number of rows in the matrix
+ */
+ public int getNumRows()
+ {
+ return numRows;
+ }
+
+ /**
+ * @return the number of columns in the binary matrix
+ */
+ public int getNumColumns()
+ {
+ return numColumns;
+ }
+
+ /**
+ * @return the encoded matrix, i.e., this matrix in byte array form.
+ */
+ public abstract byte[] getEncoded();
+
+ // ----------------------------------------------------
+ // arithmetic
+ // ----------------------------------------------------
+
+ /**
+ * Compute the inverse of this matrix.
+ *
+ * @return the inverse of this matrix (newly created).
+ */
+ public abstract Matrix computeInverse();
+
+ /**
+ * Check if this is the zero matrix (i.e., all entries are zero).
+ *
+ * @return <tt>true</tt> if this is the zero matrix
+ */
+ public abstract boolean isZero();
+
+ /**
+ * Compute the product of this matrix and another matrix.
+ *
+ * @param a the other matrix
+ * @return <tt>this * a</tt> (newly created)
+ */
+ public abstract Matrix rightMultiply(Matrix a);
+
+ /**
+ * Compute the product of this matrix and a permutation.
+ *
+ * @param p the permutation
+ * @return <tt>this * p</tt> (newly created)
+ */
+ public abstract Matrix rightMultiply(Permutation p);
+
+ /**
+ * Compute the product of a vector and this matrix. If the length of the
+ * vector is greater than the number of rows of this matrix, the matrix is
+ * multiplied by each m-bit part of the vector.
+ *
+ * @param vector a vector
+ * @return <tt>vector * this</tt> (newly created)
+ */
+ public abstract Vector leftMultiply(Vector vector);
+
+ /**
+ * Compute the product of this matrix and a vector.
+ *
+ * @param vector a vector
+ * @return <tt>this * vector</tt> (newly created)
+ */
+ public abstract Vector rightMultiply(Vector vector);
+
+ /**
+ * @return a human readable form of the matrix.
+ */
+ public abstract String toString();
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Permutation.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Permutation.java
new file mode 100644
index 00000000..80cd2e5e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Permutation.java
@@ -0,0 +1,247 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+import java.security.SecureRandom;
+
+/**
+ * This class implements permutations of the set {0,1,...,n-1} for some given n
+ * &gt; 0, i.e., ordered sequences containing each number <tt>m</tt> (<tt>0 &lt;=
+ * m &lt; n</tt>)
+ * once and only once.
+ */
+public class Permutation
+{
+
+ /**
+ * perm holds the elements of the permutation vector, i.e. <tt>[perm(0),
+ * perm(1), ..., perm(n-1)]</tt>
+ */
+ private int[] perm;
+
+ /**
+ * Create the identity permutation of the given size.
+ *
+ * @param n the size of the permutation
+ */
+ public Permutation(int n)
+ {
+ if (n <= 0)
+ {
+ throw new IllegalArgumentException("invalid length");
+ }
+
+ perm = new int[n];
+ for (int i = n - 1; i >= 0; i--)
+ {
+ perm[i] = i;
+ }
+ }
+
+ /**
+ * Create a permutation using the given permutation vector.
+ *
+ * @param perm the permutation vector
+ */
+ public Permutation(int[] perm)
+ {
+ if (!isPermutation(perm))
+ {
+ throw new IllegalArgumentException(
+ "array is not a permutation vector");
+ }
+
+ this.perm = IntUtils.clone(perm);
+ }
+
+ /**
+ * Create a permutation from an encoded permutation.
+ *
+ * @param enc the encoded permutation
+ */
+ public Permutation(byte[] enc)
+ {
+ if (enc.length <= 4)
+ {
+ throw new IllegalArgumentException("invalid encoding");
+ }
+
+ int n = LittleEndianConversions.OS2IP(enc, 0);
+ int size = IntegerFunctions.ceilLog256(n - 1);
+
+ if (enc.length != 4 + n * size)
+ {
+ throw new IllegalArgumentException("invalid encoding");
+ }
+
+ perm = new int[n];
+ for (int i = 0; i < n; i++)
+ {
+ perm[i] = LittleEndianConversions.OS2IP(enc, 4 + i * size, size);
+ }
+
+ if (!isPermutation(perm))
+ {
+ throw new IllegalArgumentException("invalid encoding");
+ }
+
+ }
+
+ /**
+ * Create a random permutation of the given size.
+ *
+ * @param n the size of the permutation
+ * @param sr the source of randomness
+ */
+ public Permutation(int n, SecureRandom sr)
+ {
+ if (n <= 0)
+ {
+ throw new IllegalArgumentException("invalid length");
+ }
+
+ perm = new int[n];
+
+ int[] help = new int[n];
+ for (int i = 0; i < n; i++)
+ {
+ help[i] = i;
+ }
+
+ int k = n;
+ for (int j = 0; j < n; j++)
+ {
+ int i = RandUtils.nextInt(sr, k);
+ k--;
+ perm[j] = help[i];
+ help[i] = help[k];
+ }
+ }
+
+ /**
+ * Encode this permutation as byte array.
+ *
+ * @return the encoded permutation
+ */
+ public byte[] getEncoded()
+ {
+ int n = perm.length;
+ int size = IntegerFunctions.ceilLog256(n - 1);
+ byte[] result = new byte[4 + n * size];
+ LittleEndianConversions.I2OSP(n, result, 0);
+ for (int i = 0; i < n; i++)
+ {
+ LittleEndianConversions.I2OSP(perm[i], result, 4 + i * size, size);
+ }
+ return result;
+ }
+
+ /**
+ * @return the permutation vector <tt>(perm(0),perm(1),...,perm(n-1))</tt>
+ */
+ public int[] getVector()
+ {
+ return IntUtils.clone(perm);
+ }
+
+ /**
+ * Compute the inverse permutation <tt>P<sup>-1</sup></tt>.
+ *
+ * @return <tt>this<sup>-1</sup></tt>
+ */
+ public Permutation computeInverse()
+ {
+ Permutation result = new Permutation(perm.length);
+ for (int i = perm.length - 1; i >= 0; i--)
+ {
+ result.perm[perm[i]] = i;
+ }
+ return result;
+ }
+
+ /**
+ * Compute the product of this permutation and another permutation.
+ *
+ * @param p the other permutation
+ * @return <tt>this * p</tt>
+ */
+ public Permutation rightMultiply(Permutation p)
+ {
+ if (p.perm.length != perm.length)
+ {
+ throw new IllegalArgumentException("length mismatch");
+ }
+ Permutation result = new Permutation(perm.length);
+ for (int i = perm.length - 1; i >= 0; i--)
+ {
+ result.perm[i] = perm[p.perm[i]];
+ }
+ return result;
+ }
+
+ /**
+ * checks if given object is equal to this permutation.
+ * <p/>
+ * The method returns false whenever the given object is not permutation.
+ *
+ * @param other -
+ * permutation
+ * @return true or false
+ */
+ public boolean equals(Object other)
+ {
+
+ if (!(other instanceof Permutation))
+ {
+ return false;
+ }
+ Permutation otherPerm = (Permutation)other;
+
+ return IntUtils.equals(perm, otherPerm.perm);
+ }
+
+ /**
+ * @return a human readable form of the permutation
+ */
+ public String toString()
+ {
+ String result = "[" + perm[0];
+ for (int i = 1; i < perm.length; i++)
+ {
+ result += ", " + perm[i];
+ }
+ result += "]";
+ return result;
+ }
+
+ /**
+ * @return the hash code of this permutation
+ */
+ public int hashCode()
+ {
+ return perm.hashCode();
+ }
+
+ /**
+ * Check that the given array corresponds to a permutation of the set
+ * <tt>{0, 1, ..., n-1}</tt>.
+ *
+ * @param perm permutation vector
+ * @return true if perm represents an n-permutation and false otherwise
+ */
+ private boolean isPermutation(int[] perm)
+ {
+ int n = perm.length;
+ boolean[] onlyOnce = new boolean[n];
+
+ for (int i = 0; i < n; i++)
+ {
+ if ((perm[i] < 0) || (perm[i] >= n) || onlyOnce[perm[i]])
+ {
+ return false;
+ }
+ onlyOnce[perm[i]] = true;
+ }
+
+ return true;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/PolynomialGF2mSmallM.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/PolynomialGF2mSmallM.java
new file mode 100644
index 00000000..668fbf93
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/PolynomialGF2mSmallM.java
@@ -0,0 +1,1125 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+import java.security.SecureRandom;
+
+/**
+ * This class describes operations with polynomials from the ring R =
+ * GF(2^m)[X], where 2 <= m <=31.
+ *
+ * @see GF2mField
+ * @see PolynomialRingGF2m
+ */
+public class PolynomialGF2mSmallM
+{
+
+ /**
+ * the finite field GF(2^m)
+ */
+ private GF2mField field;
+
+ /**
+ * the degree of this polynomial
+ */
+ private int degree;
+
+ /**
+ * For the polynomial representation the map f: R->Z*,
+ * <tt>poly(X) -> [coef_0, coef_1, ...]</tt> is used, where
+ * <tt>coef_i</tt> is the <tt>i</tt>th coefficient of the polynomial
+ * represented as int (see {@link GF2mField}). The polynomials are stored
+ * as int arrays.
+ */
+ private int[] coefficients;
+
+ /*
+ * some types of polynomials
+ */
+
+ /**
+ * Constant used for polynomial construction (see constructor
+ * {@link #PolynomialGF2mSmallM(GF2mField, int, char, SecureRandom)}).
+ */
+ public static final char RANDOM_IRREDUCIBLE_POLYNOMIAL = 'I';
+
+ /**
+ * Construct the zero polynomial over the finite field GF(2^m).
+ *
+ * @param field the finite field GF(2^m)
+ */
+ public PolynomialGF2mSmallM(GF2mField field)
+ {
+ this.field = field;
+ degree = -1;
+ coefficients = new int[1];
+ }
+
+ /**
+ * Construct a polynomial over the finite field GF(2^m).
+ *
+ * @param field the finite field GF(2^m)
+ * @param deg degree of polynomial
+ * @param typeOfPolynomial type of polynomial
+ * @param sr PRNG
+ */
+ public PolynomialGF2mSmallM(GF2mField field, int deg,
+ char typeOfPolynomial, SecureRandom sr)
+ {
+ this.field = field;
+
+ switch (typeOfPolynomial)
+ {
+ case PolynomialGF2mSmallM.RANDOM_IRREDUCIBLE_POLYNOMIAL:
+ coefficients = createRandomIrreduciblePolynomial(deg, sr);
+ break;
+ default:
+ throw new IllegalArgumentException(" Error: type "
+ + typeOfPolynomial
+ + " is not defined for GF2smallmPolynomial");
+ }
+ computeDegree();
+ }
+
+ /**
+ * Create an irreducible polynomial with the given degree over the field
+ * <tt>GF(2^m)</tt>.
+ *
+ * @param deg polynomial degree
+ * @param sr source of randomness
+ * @return the generated irreducible polynomial
+ */
+ private int[] createRandomIrreduciblePolynomial(int deg, SecureRandom sr)
+ {
+ int[] resCoeff = new int[deg + 1];
+ resCoeff[deg] = 1;
+ resCoeff[0] = field.getRandomNonZeroElement(sr);
+ for (int i = 1; i < deg; i++)
+ {
+ resCoeff[i] = field.getRandomElement(sr);
+ }
+ while (!isIrreducible(resCoeff))
+ {
+ int n = RandUtils.nextInt(sr, deg);
+ if (n == 0)
+ {
+ resCoeff[0] = field.getRandomNonZeroElement(sr);
+ }
+ else
+ {
+ resCoeff[n] = field.getRandomElement(sr);
+ }
+ }
+ return resCoeff;
+ }
+
+ /**
+ * Construct a monomial of the given degree over the finite field GF(2^m).
+ *
+ * @param field the finite field GF(2^m)
+ * @param degree the degree of the monomial
+ */
+ public PolynomialGF2mSmallM(GF2mField field, int degree)
+ {
+ this.field = field;
+ this.degree = degree;
+ coefficients = new int[degree + 1];
+ coefficients[degree] = 1;
+ }
+
+ /**
+ * Construct the polynomial over the given finite field GF(2^m) from the
+ * given coefficient vector.
+ *
+ * @param field finite field GF2m
+ * @param coeffs the coefficient vector
+ */
+ public PolynomialGF2mSmallM(GF2mField field, int[] coeffs)
+ {
+ this.field = field;
+ coefficients = normalForm(coeffs);
+ computeDegree();
+ }
+
+ /**
+ * Create a polynomial over the finite field GF(2^m).
+ *
+ * @param field the finite field GF(2^m)
+ * @param enc byte[] polynomial in byte array form
+ */
+ public PolynomialGF2mSmallM(GF2mField field, byte[] enc)
+ {
+ this.field = field;
+
+ // decodes polynomial
+ int d = 8;
+ int count = 1;
+ while (field.getDegree() > d)
+ {
+ count++;
+ d += 8;
+ }
+
+ if ((enc.length % count) != 0)
+ {
+ throw new IllegalArgumentException(
+ " Error: byte array is not encoded polynomial over given finite field GF2m");
+ }
+
+ coefficients = new int[enc.length / count];
+ count = 0;
+ for (int i = 0; i < coefficients.length; i++)
+ {
+ for (int j = 0; j < d; j += 8)
+ {
+ coefficients[i] ^= (enc[count++] & 0x000000ff) << j;
+ }
+ if (!this.field.isElementOfThisField(coefficients[i]))
+ {
+ throw new IllegalArgumentException(
+ " Error: byte array is not encoded polynomial over given finite field GF2m");
+ }
+ }
+ // if HC = 0 for non-zero polynomial, returns error
+ if ((coefficients.length != 1)
+ && (coefficients[coefficients.length - 1] == 0))
+ {
+ throw new IllegalArgumentException(
+ " Error: byte array is not encoded polynomial over given finite field GF2m");
+ }
+ computeDegree();
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other another {@link PolynomialGF2mSmallM}
+ */
+ public PolynomialGF2mSmallM(PolynomialGF2mSmallM other)
+ {
+ // field needs not to be cloned since it is immutable
+ field = other.field;
+ degree = other.degree;
+ coefficients = IntUtils.clone(other.coefficients);
+ }
+
+ /**
+ * Create a polynomial over the finite field GF(2^m) out of the given
+ * coefficient vector. The finite field is also obtained from the
+ * {@link GF2mVector}.
+ *
+ * @param vect the coefficient vector
+ */
+ public PolynomialGF2mSmallM(GF2mVector vect)
+ {
+ this(vect.getField(), vect.getIntArrayForm());
+ }
+
+ /*
+ * ------------------------
+ */
+
+ /**
+ * Return the degree of this polynomial
+ *
+ * @return int degree of this polynomial if this is zero polynomial return
+ * -1
+ */
+ public int getDegree()
+ {
+ int d = coefficients.length - 1;
+ if (coefficients[d] == 0)
+ {
+ return -1;
+ }
+ return d;
+ }
+
+ /**
+ * @return the head coefficient of this polynomial
+ */
+ public int getHeadCoefficient()
+ {
+ if (degree == -1)
+ {
+ return 0;
+ }
+ return coefficients[degree];
+ }
+
+ /**
+ * Return the head coefficient of a polynomial.
+ *
+ * @param a the polynomial
+ * @return the head coefficient of <tt>a</tt>
+ */
+ private static int headCoefficient(int[] a)
+ {
+ int degree = computeDegree(a);
+ if (degree == -1)
+ {
+ return 0;
+ }
+ return a[degree];
+ }
+
+ /**
+ * Return the coefficient with the given index.
+ *
+ * @param index the index
+ * @return the coefficient with the given index
+ */
+ public int getCoefficient(int index)
+ {
+ if ((index < 0) || (index > degree))
+ {
+ return 0;
+ }
+ return coefficients[index];
+ }
+
+ /**
+ * Returns encoded polynomial, i.e., this polynomial in byte array form
+ *
+ * @return the encoded polynomial
+ */
+ public byte[] getEncoded()
+ {
+ int d = 8;
+ int count = 1;
+ while (field.getDegree() > d)
+ {
+ count++;
+ d += 8;
+ }
+
+ byte[] res = new byte[coefficients.length * count];
+ count = 0;
+ for (int i = 0; i < coefficients.length; i++)
+ {
+ for (int j = 0; j < d; j += 8)
+ {
+ res[count++] = (byte)(coefficients[i] >>> j);
+ }
+ }
+
+ return res;
+ }
+
+ /**
+ * Evaluate this polynomial <tt>p</tt> at a value <tt>e</tt> (in
+ * <tt>GF(2^m)</tt>) with the Horner scheme.
+ *
+ * @param e the element of the finite field GF(2^m)
+ * @return <tt>this(e)</tt>
+ */
+ public int evaluateAt(int e)
+ {
+ int result = coefficients[degree];
+ for (int i = degree - 1; i >= 0; i--)
+ {
+ result = field.mult(result, e) ^ coefficients[i];
+ }
+ return result;
+ }
+
+ /**
+ * Compute the sum of this polynomial and the given polynomial.
+ *
+ * @param addend the addend
+ * @return <tt>this + a</tt> (newly created)
+ */
+ public PolynomialGF2mSmallM add(PolynomialGF2mSmallM addend)
+ {
+ int[] resultCoeff = add(coefficients, addend.coefficients);
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Add the given polynomial to this polynomial (overwrite this).
+ *
+ * @param addend the addend
+ */
+ public void addToThis(PolynomialGF2mSmallM addend)
+ {
+ coefficients = add(coefficients, addend.coefficients);
+ computeDegree();
+ }
+
+ /**
+ * Compute the sum of two polynomials a and b over the finite field
+ * <tt>GF(2^m)</tt>.
+ *
+ * @param a the first polynomial
+ * @param b the second polynomial
+ * @return a + b
+ */
+ private int[] add(int[] a, int[] b)
+ {
+ int[] result, addend;
+ if (a.length < b.length)
+ {
+ result = new int[b.length];
+ System.arraycopy(b, 0, result, 0, b.length);
+ addend = a;
+ }
+ else
+ {
+ result = new int[a.length];
+ System.arraycopy(a, 0, result, 0, a.length);
+ addend = b;
+ }
+
+ for (int i = addend.length - 1; i >= 0; i--)
+ {
+ result[i] = field.add(result[i], addend[i]);
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute the sum of this polynomial and the monomial of the given degree.
+ *
+ * @param degree the degree of the monomial
+ * @return <tt>this + X^k</tt>
+ */
+ public PolynomialGF2mSmallM addMonomial(int degree)
+ {
+ int[] monomial = new int[degree + 1];
+ monomial[degree] = 1;
+ int[] resultCoeff = add(coefficients, monomial);
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Compute the product of this polynomial with an element from GF(2^m).
+ *
+ * @param element an element of the finite field GF(2^m)
+ * @return <tt>this * element</tt> (newly created)
+ * @throws ArithmeticException if <tt>element</tt> is not an element of the finite
+ * field this polynomial is defined over.
+ */
+ public PolynomialGF2mSmallM multWithElement(int element)
+ {
+ if (!field.isElementOfThisField(element))
+ {
+ throw new ArithmeticException(
+ "Not an element of the finite field this polynomial is defined over.");
+ }
+ int[] resultCoeff = multWithElement(coefficients, element);
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Multiply this polynomial with an element from GF(2^m).
+ *
+ * @param element an element of the finite field GF(2^m)
+ * @throws ArithmeticException if <tt>element</tt> is not an element of the finite
+ * field this polynomial is defined over.
+ */
+ public void multThisWithElement(int element)
+ {
+ if (!field.isElementOfThisField(element))
+ {
+ throw new ArithmeticException(
+ "Not an element of the finite field this polynomial is defined over.");
+ }
+ coefficients = multWithElement(coefficients, element);
+ computeDegree();
+ }
+
+ /**
+ * Compute the product of a polynomial a with an element from the finite
+ * field <tt>GF(2^m)</tt>.
+ *
+ * @param a the polynomial
+ * @param element an element of the finite field GF(2^m)
+ * @return <tt>a * element</tt>
+ */
+ private int[] multWithElement(int[] a, int element)
+ {
+ int degree = computeDegree(a);
+ if (degree == -1 || element == 0)
+ {
+ return new int[1];
+ }
+
+ if (element == 1)
+ {
+ return IntUtils.clone(a);
+ }
+
+ int[] result = new int[degree + 1];
+ for (int i = degree; i >= 0; i--)
+ {
+ result[i] = field.mult(a[i], element);
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute the product of this polynomial with a monomial X^k.
+ *
+ * @param k the degree of the monomial
+ * @return <tt>this * X^k</tt>
+ */
+ public PolynomialGF2mSmallM multWithMonomial(int k)
+ {
+ int[] resultCoeff = multWithMonomial(coefficients, k);
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Compute the product of a polynomial with a monomial X^k.
+ *
+ * @param a the polynomial
+ * @param k the degree of the monomial
+ * @return <tt>a * X^k</tt>
+ */
+ private static int[] multWithMonomial(int[] a, int k)
+ {
+ int d = computeDegree(a);
+ if (d == -1)
+ {
+ return new int[1];
+ }
+ int[] result = new int[d + k + 1];
+ System.arraycopy(a, 0, result, k, d + 1);
+ return result;
+ }
+
+ /**
+ * Divide this polynomial by the given polynomial.
+ *
+ * @param f a polynomial
+ * @return polynomial pair = {q,r} where this = q*f+r and deg(r) &lt;
+ * deg(f);
+ */
+ public PolynomialGF2mSmallM[] div(PolynomialGF2mSmallM f)
+ {
+ int[][] resultCoeffs = div(coefficients, f.coefficients);
+ return new PolynomialGF2mSmallM[]{
+ new PolynomialGF2mSmallM(field, resultCoeffs[0]),
+ new PolynomialGF2mSmallM(field, resultCoeffs[1])};
+ }
+
+ /**
+ * Compute the result of the division of two polynomials over the field
+ * <tt>GF(2^m)</tt>.
+ *
+ * @param a the first polynomial
+ * @param f the second polynomial
+ * @return int[][] {q,r}, where a = q*f+r and deg(r) &lt; deg(f);
+ */
+ private int[][] div(int[] a, int[] f)
+ {
+ int df = computeDegree(f);
+ int da = computeDegree(a) + 1;
+ if (df == -1)
+ {
+ throw new ArithmeticException("Division by zero.");
+ }
+ int[][] result = new int[2][];
+ result[0] = new int[1];
+ result[1] = new int[da];
+ int hc = headCoefficient(f);
+ hc = field.inverse(hc);
+ result[0][0] = 0;
+ System.arraycopy(a, 0, result[1], 0, result[1].length);
+ while (df <= computeDegree(result[1]))
+ {
+ int[] q;
+ int[] coeff = new int[1];
+ coeff[0] = field.mult(headCoefficient(result[1]), hc);
+ q = multWithElement(f, coeff[0]);
+ int n = computeDegree(result[1]) - df;
+ q = multWithMonomial(q, n);
+ coeff = multWithMonomial(coeff, n);
+ result[0] = add(coeff, result[0]);
+ result[1] = add(q, result[1]);
+ }
+ return result;
+ }
+
+ /**
+ * Return the greatest common divisor of this and a polynomial <i>f</i>
+ *
+ * @param f polynomial
+ * @return GCD(this, f)
+ */
+ public PolynomialGF2mSmallM gcd(PolynomialGF2mSmallM f)
+ {
+ int[] resultCoeff = gcd(coefficients, f.coefficients);
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Return the greatest common divisor of two polynomials over the field
+ * <tt>GF(2^m)</tt>.
+ *
+ * @param f the first polynomial
+ * @param g the second polynomial
+ * @return <tt>gcd(f, g)</tt>
+ */
+ private int[] gcd(int[] f, int[] g)
+ {
+ int[] a = f;
+ int[] b = g;
+ if (computeDegree(a) == -1)
+ {
+ return b;
+ }
+ while (computeDegree(b) != -1)
+ {
+ int[] c = mod(a, b);
+ a = new int[b.length];
+ System.arraycopy(b, 0, a, 0, a.length);
+ b = new int[c.length];
+ System.arraycopy(c, 0, b, 0, b.length);
+ }
+ int coeff = field.inverse(headCoefficient(a));
+ return multWithElement(a, coeff);
+ }
+
+ /**
+ * Compute the product of this polynomial and the given factor using a
+ * Karatzuba like scheme.
+ *
+ * @param factor the polynomial
+ * @return <tt>this * factor</tt>
+ */
+ public PolynomialGF2mSmallM multiply(PolynomialGF2mSmallM factor)
+ {
+ int[] resultCoeff = multiply(coefficients, factor.coefficients);
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Compute the product of two polynomials over the field <tt>GF(2^m)</tt>
+ * using a Karatzuba like multiplication.
+ *
+ * @param a the first polynomial
+ * @param b the second polynomial
+ * @return a * b
+ */
+ private int[] multiply(int[] a, int[] b)
+ {
+ int[] mult1, mult2;
+ if (computeDegree(a) < computeDegree(b))
+ {
+ mult1 = b;
+ mult2 = a;
+ }
+ else
+ {
+ mult1 = a;
+ mult2 = b;
+ }
+
+ mult1 = normalForm(mult1);
+ mult2 = normalForm(mult2);
+
+ if (mult2.length == 1)
+ {
+ return multWithElement(mult1, mult2[0]);
+ }
+
+ int d1 = mult1.length;
+ int d2 = mult2.length;
+ int[] result = new int[d1 + d2 - 1];
+
+ if (d2 != d1)
+ {
+ int[] res1 = new int[d2];
+ int[] res2 = new int[d1 - d2];
+ System.arraycopy(mult1, 0, res1, 0, res1.length);
+ System.arraycopy(mult1, d2, res2, 0, res2.length);
+ res1 = multiply(res1, mult2);
+ res2 = multiply(res2, mult2);
+ res2 = multWithMonomial(res2, d2);
+ result = add(res1, res2);
+ }
+ else
+ {
+ d2 = (d1 + 1) >>> 1;
+ int d = d1 - d2;
+ int[] firstPartMult1 = new int[d2];
+ int[] firstPartMult2 = new int[d2];
+ int[] secondPartMult1 = new int[d];
+ int[] secondPartMult2 = new int[d];
+ System
+ .arraycopy(mult1, 0, firstPartMult1, 0,
+ firstPartMult1.length);
+ System.arraycopy(mult1, d2, secondPartMult1, 0,
+ secondPartMult1.length);
+ System
+ .arraycopy(mult2, 0, firstPartMult2, 0,
+ firstPartMult2.length);
+ System.arraycopy(mult2, d2, secondPartMult2, 0,
+ secondPartMult2.length);
+ int[] helpPoly1 = add(firstPartMult1, secondPartMult1);
+ int[] helpPoly2 = add(firstPartMult2, secondPartMult2);
+ int[] res1 = multiply(firstPartMult1, firstPartMult2);
+ int[] res2 = multiply(helpPoly1, helpPoly2);
+ int[] res3 = multiply(secondPartMult1, secondPartMult2);
+ res2 = add(res2, res1);
+ res2 = add(res2, res3);
+ res3 = multWithMonomial(res3, d2);
+ result = add(res2, res3);
+ result = multWithMonomial(result, d2);
+ result = add(result, res1);
+ }
+
+ return result;
+ }
+
+ /*
+ * ---------------- PART II ----------------
+ *
+ */
+
+ /**
+ * Check a polynomial for irreducibility over the field <tt>GF(2^m)</tt>.
+ *
+ * @param a the polynomial to check
+ * @return true if a is irreducible, false otherwise
+ */
+ private boolean isIrreducible(int[] a)
+ {
+ if (a[0] == 0)
+ {
+ return false;
+ }
+ int d = computeDegree(a) >> 1;
+ int[] u = {0, 1};
+ final int[] Y = {0, 1};
+ int fieldDegree = field.getDegree();
+ for (int i = 0; i < d; i++)
+ {
+ for (int j = fieldDegree - 1; j >= 0; j--)
+ {
+ u = modMultiply(u, u, a);
+ }
+ u = normalForm(u);
+ int[] g = gcd(add(u, Y), a);
+ if (computeDegree(g) != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Reduce this polynomial modulo another polynomial.
+ *
+ * @param f the reduction polynomial
+ * @return <tt>this mod f</tt>
+ */
+ public PolynomialGF2mSmallM mod(PolynomialGF2mSmallM f)
+ {
+ int[] resultCoeff = mod(coefficients, f.coefficients);
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Reduce a polynomial modulo another polynomial.
+ *
+ * @param a the polynomial
+ * @param f the reduction polynomial
+ * @return <tt>a mod f</tt>
+ */
+ private int[] mod(int[] a, int[] f)
+ {
+ int df = computeDegree(f);
+ if (df == -1)
+ {
+ throw new ArithmeticException("Division by zero");
+ }
+ int[] result = new int[a.length];
+ int hc = headCoefficient(f);
+ hc = field.inverse(hc);
+ System.arraycopy(a, 0, result, 0, result.length);
+ while (df <= computeDegree(result))
+ {
+ int[] q;
+ int coeff = field.mult(headCoefficient(result), hc);
+ q = multWithMonomial(f, computeDegree(result) - df);
+ q = multWithElement(q, coeff);
+ result = add(q, result);
+ }
+ return result;
+ }
+
+ /**
+ * Compute the product of this polynomial and another polynomial modulo a
+ * third polynomial.
+ *
+ * @param a another polynomial
+ * @param b the reduction polynomial
+ * @return <tt>this * a mod b</tt>
+ */
+ public PolynomialGF2mSmallM modMultiply(PolynomialGF2mSmallM a,
+ PolynomialGF2mSmallM b)
+ {
+ int[] resultCoeff = modMultiply(coefficients, a.coefficients,
+ b.coefficients);
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Square this polynomial using a squaring matrix.
+ *
+ * @param matrix the squaring matrix
+ * @return <tt>this^2</tt> modulo the reduction polynomial implicitly
+ * given via the squaring matrix
+ */
+ public PolynomialGF2mSmallM modSquareMatrix(PolynomialGF2mSmallM[] matrix)
+ {
+
+ int length = matrix.length;
+
+ int[] resultCoeff = new int[length];
+ int[] thisSquare = new int[length];
+
+ // square each entry of this polynomial
+ for (int i = 0; i < coefficients.length; i++)
+ {
+ thisSquare[i] = field.mult(coefficients[i], coefficients[i]);
+ }
+
+ // do matrix-vector multiplication
+ for (int i = 0; i < length; i++)
+ {
+ // compute scalar product of i-th row and coefficient vector
+ for (int j = 0; j < length; j++)
+ {
+ if (i >= matrix[j].coefficients.length)
+ {
+ continue;
+ }
+ int scalarTerm = field.mult(matrix[j].coefficients[i],
+ thisSquare[j]);
+ resultCoeff[i] = field.add(resultCoeff[i], scalarTerm);
+ }
+ }
+
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Compute the product of two polynomials modulo a third polynomial over the
+ * finite field <tt>GF(2^m)</tt>.
+ *
+ * @param a the first polynomial
+ * @param b the second polynomial
+ * @param g the reduction polynomial
+ * @return <tt>a * b mod g</tt>
+ */
+ private int[] modMultiply(int[] a, int[] b, int[] g)
+ {
+ return mod(multiply(a, b), g);
+ }
+
+ /**
+ * Compute the square root of this polynomial modulo the given polynomial.
+ *
+ * @param a the reduction polynomial
+ * @return <tt>this^(1/2) mod a</tt>
+ */
+ public PolynomialGF2mSmallM modSquareRoot(PolynomialGF2mSmallM a)
+ {
+ int[] resultCoeff = IntUtils.clone(coefficients);
+ int[] help = modMultiply(resultCoeff, resultCoeff, a.coefficients);
+ while (!isEqual(help, coefficients))
+ {
+ resultCoeff = normalForm(help);
+ help = modMultiply(resultCoeff, resultCoeff, a.coefficients);
+ }
+
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Compute the square root of this polynomial using a square root matrix.
+ *
+ * @param matrix the matrix for computing square roots in
+ * <tt>(GF(2^m))^t</tt> the polynomial ring defining the
+ * square root matrix
+ * @return <tt>this^(1/2)</tt> modulo the reduction polynomial implicitly
+ * given via the square root matrix
+ */
+ public PolynomialGF2mSmallM modSquareRootMatrix(
+ PolynomialGF2mSmallM[] matrix)
+ {
+
+ int length = matrix.length;
+
+ int[] resultCoeff = new int[length];
+
+ // do matrix multiplication
+ for (int i = 0; i < length; i++)
+ {
+ // compute scalar product of i-th row and j-th column
+ for (int j = 0; j < length; j++)
+ {
+ if (i >= matrix[j].coefficients.length)
+ {
+ continue;
+ }
+ if (j < coefficients.length)
+ {
+ int scalarTerm = field.mult(matrix[j].coefficients[i],
+ coefficients[j]);
+ resultCoeff[i] = field.add(resultCoeff[i], scalarTerm);
+ }
+ }
+ }
+
+ // compute the square root of each entry of the result coefficients
+ for (int i = 0; i < length; i++)
+ {
+ resultCoeff[i] = field.sqRoot(resultCoeff[i]);
+ }
+
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Compute the result of the division of this polynomial by another
+ * polynomial modulo a third polynomial.
+ *
+ * @param divisor the divisor
+ * @param modulus the reduction polynomial
+ * @return <tt>this * divisor^(-1) mod modulus</tt>
+ */
+ public PolynomialGF2mSmallM modDiv(PolynomialGF2mSmallM divisor,
+ PolynomialGF2mSmallM modulus)
+ {
+ int[] resultCoeff = modDiv(coefficients, divisor.coefficients,
+ modulus.coefficients);
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Compute the result of the division of two polynomials modulo a third
+ * polynomial over the field <tt>GF(2^m)</tt>.
+ *
+ * @param a the first polynomial
+ * @param b the second polynomial
+ * @param g the reduction polynomial
+ * @return <tt>a * b^(-1) mod g</tt>
+ */
+ private int[] modDiv(int[] a, int[] b, int[] g)
+ {
+ int[] r0 = normalForm(g);
+ int[] r1 = mod(b, g);
+ int[] s0 = {0};
+ int[] s1 = mod(a, g);
+ int[] s2;
+ int[][] q;
+ while (computeDegree(r1) != -1)
+ {
+ q = div(r0, r1);
+ r0 = normalForm(r1);
+ r1 = normalForm(q[1]);
+ s2 = add(s0, modMultiply(q[0], s1, g));
+ s0 = normalForm(s1);
+ s1 = normalForm(s2);
+
+ }
+ int hc = headCoefficient(r0);
+ s0 = multWithElement(s0, field.inverse(hc));
+ return s0;
+ }
+
+ /**
+ * Compute the inverse of this polynomial modulo the given polynomial.
+ *
+ * @param a the reduction polynomial
+ * @return <tt>this^(-1) mod a</tt>
+ */
+ public PolynomialGF2mSmallM modInverse(PolynomialGF2mSmallM a)
+ {
+ int[] unit = {1};
+ int[] resultCoeff = modDiv(unit, coefficients, a.coefficients);
+ return new PolynomialGF2mSmallM(field, resultCoeff);
+ }
+
+ /**
+ * Compute a polynomial pair (a,b) from this polynomial and the given
+ * polynomial g with the property b*this = a mod g and deg(a)<=deg(g)/2.
+ *
+ * @param g the reduction polynomial
+ * @return PolynomialGF2mSmallM[] {a,b} with b*this = a mod g and deg(a)<=
+ * deg(g)/2
+ */
+ public PolynomialGF2mSmallM[] modPolynomialToFracton(PolynomialGF2mSmallM g)
+ {
+ int dg = g.degree >> 1;
+ int[] a0 = normalForm(g.coefficients);
+ int[] a1 = mod(coefficients, g.coefficients);
+ int[] b0 = {0};
+ int[] b1 = {1};
+ while (computeDegree(a1) > dg)
+ {
+ int[][] q = div(a0, a1);
+ a0 = a1;
+ a1 = q[1];
+ int[] b2 = add(b0, modMultiply(q[0], b1, g.coefficients));
+ b0 = b1;
+ b1 = b2;
+ }
+
+ return new PolynomialGF2mSmallM[]{
+ new PolynomialGF2mSmallM(field, a1),
+ new PolynomialGF2mSmallM(field, b1)};
+ }
+
+ /**
+ * checks if given object is equal to this polynomial.
+ * <p/>
+ * The method returns false whenever the given object is not polynomial over
+ * GF(2^m).
+ *
+ * @param other object
+ * @return true or false
+ */
+ public boolean equals(Object other)
+ {
+
+ if (other == null || !(other instanceof PolynomialGF2mSmallM))
+ {
+ return false;
+ }
+
+ PolynomialGF2mSmallM p = (PolynomialGF2mSmallM)other;
+
+ if ((field.equals(p.field)) && (degree == p.degree)
+ && (isEqual(coefficients, p.coefficients)))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Compare two polynomials given as int arrays.
+ *
+ * @param a the first polynomial
+ * @param b the second polynomial
+ * @return <tt>true</tt> if <tt>a</tt> and <tt>b</tt> represent the
+ * same polynomials, <tt>false</tt> otherwise
+ */
+ private static boolean isEqual(int[] a, int[] b)
+ {
+ int da = computeDegree(a);
+ int db = computeDegree(b);
+ if (da != db)
+ {
+ return false;
+ }
+ for (int i = 0; i <= da; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @return the hash code of this polynomial
+ */
+ public int hashCode()
+ {
+ int hash = field.hashCode();
+ for (int j = 0; j < coefficients.length; j++)
+ {
+ hash = hash * 31 + coefficients[j];
+ }
+ return hash;
+ }
+
+ /**
+ * Returns a human readable form of the polynomial.
+ * <p/>
+ *
+ * @return a human readable form of the polynomial.
+ */
+ public String toString()
+ {
+ String str = " Polynomial over " + field.toString() + ": \n";
+
+ for (int i = 0; i < coefficients.length; i++)
+ {
+ str = str + field.elementToStr(coefficients[i]) + "Y^" + i + "+";
+ }
+ str = str + ";";
+
+ return str;
+ }
+
+ /**
+ * Compute the degree of this polynomial. If this is the zero polynomial,
+ * the degree is -1.
+ */
+ private void computeDegree()
+ {
+ for (degree = coefficients.length - 1; degree >= 0
+ && coefficients[degree] == 0; degree--)
+ {
+ ;
+ }
+ }
+
+ /**
+ * Compute the degree of a polynomial.
+ *
+ * @param a the polynomial
+ * @return the degree of the polynomial <tt>a</tt>. If <tt>a</tt> is
+ * the zero polynomial, return -1.
+ */
+ private static int computeDegree(int[] a)
+ {
+ int degree;
+ for (degree = a.length - 1; degree >= 0 && a[degree] == 0; degree--)
+ {
+ ;
+ }
+ return degree;
+ }
+
+ /**
+ * Strip leading zero coefficients from the given polynomial.
+ *
+ * @param a the polynomial
+ * @return the reduced polynomial
+ */
+ private static int[] normalForm(int[] a)
+ {
+ int d = computeDegree(a);
+
+ // if a is the zero polynomial
+ if (d == -1)
+ {
+ // return new zero polynomial
+ return new int[1];
+ }
+
+ // if a already is in normal form
+ if (a.length == d + 1)
+ {
+ // return a clone of a
+ return IntUtils.clone(a);
+ }
+
+ // else, reduce a
+ int[] result = new int[d + 1];
+ System.arraycopy(a, 0, result, 0, d + 1);
+ return result;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/PolynomialRingGF2.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/PolynomialRingGF2.java
new file mode 100644
index 00000000..0bdbc41a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/PolynomialRingGF2.java
@@ -0,0 +1,278 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+/**
+ * This class describes operations with polynomials over finite field GF(2), i e
+ * polynomial ring R = GF(2)[X]. All operations are defined only for polynomials
+ * with degree <=32. For the polynomial representation the map f: R->Z,
+ * poly(X)->poly(2) is used, where integers have the binary representation. For
+ * example: X^7+X^3+X+1 -> (00...0010001011)=139 Also for polynomials type
+ * Integer is used.
+ *
+ * @see GF2mField
+ */
+public final class PolynomialRingGF2
+{
+
+ /**
+ * Default constructor (private).
+ */
+ private PolynomialRingGF2()
+ {
+ // empty
+ }
+
+ /**
+ * Return sum of two polyomials
+ *
+ * @param p polynomial
+ * @param q polynomial
+ * @return p+q
+ */
+
+ public static int add(int p, int q)
+ {
+ return p ^ q;
+ }
+
+ /**
+ * Return product of two polynomials
+ *
+ * @param p polynomial
+ * @param q polynomial
+ * @return p*q
+ */
+
+ public static long multiply(int p, int q)
+ {
+ long result = 0;
+ if (q != 0)
+ {
+ long q1 = q & 0x00000000ffffffffL;
+
+ while (p != 0)
+ {
+ byte b = (byte)(p & 0x01);
+ if (b == 1)
+ {
+ result ^= q1;
+ }
+ p >>>= 1;
+ q1 <<= 1;
+
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Compute the product of two polynomials modulo a third polynomial.
+ *
+ * @param a the first polynomial
+ * @param b the second polynomial
+ * @param r the reduction polynomial
+ * @return <tt>a * b mod r</tt>
+ */
+ public static int modMultiply(int a, int b, int r)
+ {
+ int result = 0;
+ int p = remainder(a, r);
+ int q = remainder(b, r);
+ if (q != 0)
+ {
+ int d = 1 << degree(r);
+
+ while (p != 0)
+ {
+ byte pMod2 = (byte)(p & 0x01);
+ if (pMod2 == 1)
+ {
+ result ^= q;
+ }
+ p >>>= 1;
+ q <<= 1;
+ if (q >= d)
+ {
+ q ^= r;
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Return the degree of a polynomial
+ *
+ * @param p polynomial p
+ * @return degree(p)
+ */
+
+ public static int degree(int p)
+ {
+ int result = -1;
+ while (p != 0)
+ {
+ result++;
+ p >>>= 1;
+ }
+ return result;
+ }
+
+ /**
+ * Return the degree of a polynomial
+ *
+ * @param p polynomial p
+ * @return degree(p)
+ */
+
+ public static int degree(long p)
+ {
+ int result = 0;
+ while (p != 0)
+ {
+ result++;
+ p >>>= 1;
+ }
+ return result - 1;
+ }
+
+ /**
+ * Return the remainder of a polynomial division of two polynomials.
+ *
+ * @param p dividend
+ * @param q divisor
+ * @return <tt>p mod q</tt>
+ */
+ public static int remainder(int p, int q)
+ {
+ int result = p;
+
+ if (q == 0)
+ {
+ System.err.println("Error: to be divided by 0");
+ return 0;
+ }
+
+ while (degree(result) >= degree(q))
+ {
+ result ^= q << (degree(result) - degree(q));
+ }
+
+ return result;
+ }
+
+ /**
+ * Return the rest of devision two polynomials
+ *
+ * @param p polinomial
+ * @param q polinomial
+ * @return p mod q
+ */
+
+ public static int rest(long p, int q)
+ {
+ long p1 = p;
+ if (q == 0)
+ {
+ System.err.println("Error: to be divided by 0");
+ return 0;
+ }
+ long q1 = q & 0x00000000ffffffffL;
+ while ((p1 >>> 32) != 0)
+ {
+ p1 ^= q1 << (degree(p1) - degree(q1));
+ }
+
+ int result = (int)(p1 & 0xffffffff);
+ while (degree(result) >= degree(q))
+ {
+ result ^= q << (degree(result) - degree(q));
+ }
+
+ return result;
+ }
+
+ /**
+ * Return the greatest common divisor of two polynomials
+ *
+ * @param p polinomial
+ * @param q polinomial
+ * @return GCD(p, q)
+ */
+
+ public static int gcd(int p, int q)
+ {
+ int a, b, c;
+ a = p;
+ b = q;
+ while (b != 0)
+ {
+ c = remainder(a, b);
+ a = b;
+ b = c;
+
+ }
+ return a;
+ }
+
+ /**
+ * Checking polynomial for irreducibility
+ *
+ * @param p polinomial
+ * @return true if p is irreducible and false otherwise
+ */
+
+ public static boolean isIrreducible(int p)
+ {
+ if (p == 0)
+ {
+ return false;
+ }
+ int d = degree(p) >>> 1;
+ int u = 2;
+ for (int i = 0; i < d; i++)
+ {
+ u = modMultiply(u, u, p);
+ if (gcd(u ^ 2, p) != 1)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Creates irreducible polynomial with degree d
+ *
+ * @param deg polynomial degree
+ * @return irreducible polynomial p
+ */
+ public static int getIrreduciblePolynomial(int deg)
+ {
+ if (deg < 0)
+ {
+ System.err.println("The Degree is negative");
+ return 0;
+ }
+ if (deg > 31)
+ {
+ System.err.println("The Degree is more then 31");
+ return 0;
+ }
+ if (deg == 0)
+ {
+ return 1;
+ }
+ int a = 1 << deg;
+ a++;
+ int b = 1 << (deg + 1);
+ for (int i = a; i < b; i += 2)
+ {
+ if (isIrreducible(i))
+ {
+ return i;
+ }
+ }
+ return 0;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/PolynomialRingGF2m.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/PolynomialRingGF2m.java
new file mode 100644
index 00000000..0711583b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/PolynomialRingGF2m.java
@@ -0,0 +1,175 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+/**
+ * This class represents polynomial rings <tt>GF(2^m)[X]/p(X)</tt> for
+ * <tt>m&lt<;32</tt>. If <tt>p(X)</tt> is irreducible, the polynomial ring
+ * is in fact an extension field of <tt>GF(2^m)</tt>.
+ */
+public class PolynomialRingGF2m
+{
+
+ /**
+ * the finite field this polynomial ring is defined over
+ */
+ private GF2mField field;
+
+ /**
+ * the reduction polynomial
+ */
+ private PolynomialGF2mSmallM p;
+
+ /**
+ * the squaring matrix for this polynomial ring (given as the array of its
+ * row vectors)
+ */
+ protected PolynomialGF2mSmallM[] sqMatrix;
+
+ /**
+ * the matrix for computing square roots in this polynomial ring (given as
+ * the array of its row vectors). This matrix is computed as the inverse of
+ * the squaring matrix.
+ */
+ protected PolynomialGF2mSmallM[] sqRootMatrix;
+
+ /**
+ * Constructor.
+ *
+ * @param field the finite field
+ * @param p the reduction polynomial
+ */
+ public PolynomialRingGF2m(GF2mField field, PolynomialGF2mSmallM p)
+ {
+ this.field = field;
+ this.p = p;
+ computeSquaringMatrix();
+ computeSquareRootMatrix();
+ }
+
+ /**
+ * @return the squaring matrix for this polynomial ring
+ */
+ public PolynomialGF2mSmallM[] getSquaringMatrix()
+ {
+ return sqMatrix;
+ }
+
+ /**
+ * @return the matrix for computing square roots for this polynomial ring
+ */
+ public PolynomialGF2mSmallM[] getSquareRootMatrix()
+ {
+ return sqRootMatrix;
+ }
+
+ /**
+ * Compute the squaring matrix for this polynomial ring, using the base
+ * field and the reduction polynomial.
+ */
+ private void computeSquaringMatrix()
+ {
+ int numColumns = p.getDegree();
+ sqMatrix = new PolynomialGF2mSmallM[numColumns];
+ for (int i = 0; i < numColumns >> 1; i++)
+ {
+ int[] monomCoeffs = new int[(i << 1) + 1];
+ monomCoeffs[i << 1] = 1;
+ sqMatrix[i] = new PolynomialGF2mSmallM(field, monomCoeffs);
+ }
+ for (int i = numColumns >> 1; i < numColumns; i++)
+ {
+ int[] monomCoeffs = new int[(i << 1) + 1];
+ monomCoeffs[i << 1] = 1;
+ PolynomialGF2mSmallM monomial = new PolynomialGF2mSmallM(field,
+ monomCoeffs);
+ sqMatrix[i] = monomial.mod(p);
+ }
+ }
+
+ /**
+ * Compute the matrix for computing square roots in this polynomial ring by
+ * inverting the squaring matrix.
+ */
+ private void computeSquareRootMatrix()
+ {
+ int numColumns = p.getDegree();
+
+ // clone squaring matrix
+ PolynomialGF2mSmallM[] tmpMatrix = new PolynomialGF2mSmallM[numColumns];
+ for (int i = numColumns - 1; i >= 0; i--)
+ {
+ tmpMatrix[i] = new PolynomialGF2mSmallM(sqMatrix[i]);
+ }
+
+ // initialize square root matrix as unit matrix
+ sqRootMatrix = new PolynomialGF2mSmallM[numColumns];
+ for (int i = numColumns - 1; i >= 0; i--)
+ {
+ sqRootMatrix[i] = new PolynomialGF2mSmallM(field, i);
+ }
+
+ // simultaneously compute Gaussian reduction of squaring matrix and unit
+ // matrix
+ for (int i = 0; i < numColumns; i++)
+ {
+ // if diagonal element is zero
+ if (tmpMatrix[i].getCoefficient(i) == 0)
+ {
+ boolean foundNonZero = false;
+ // find a non-zero element in the same row
+ for (int j = i + 1; j < numColumns; j++)
+ {
+ if (tmpMatrix[j].getCoefficient(i) != 0)
+ {
+ // found it, swap columns ...
+ foundNonZero = true;
+ swapColumns(tmpMatrix, i, j);
+ swapColumns(sqRootMatrix, i, j);
+ // ... and quit searching
+ j = numColumns;
+ continue;
+ }
+ }
+ // if no non-zero element was found
+ if (!foundNonZero)
+ {
+ // the matrix is not invertible
+ throw new ArithmeticException(
+ "Squaring matrix is not invertible.");
+ }
+ }
+
+ // normalize i-th column
+ int coef = tmpMatrix[i].getCoefficient(i);
+ int invCoef = field.inverse(coef);
+ tmpMatrix[i].multThisWithElement(invCoef);
+ sqRootMatrix[i].multThisWithElement(invCoef);
+
+ // normalize all other columns
+ for (int j = 0; j < numColumns; j++)
+ {
+ if (j != i)
+ {
+ coef = tmpMatrix[j].getCoefficient(i);
+ if (coef != 0)
+ {
+ PolynomialGF2mSmallM tmpSqColumn = tmpMatrix[i]
+ .multWithElement(coef);
+ PolynomialGF2mSmallM tmpInvColumn = sqRootMatrix[i]
+ .multWithElement(coef);
+ tmpMatrix[j].addToThis(tmpSqColumn);
+ sqRootMatrix[j].addToThis(tmpInvColumn);
+ }
+ }
+ }
+ }
+ }
+
+ private static void swapColumns(PolynomialGF2mSmallM[] matrix, int first,
+ int second)
+ {
+ PolynomialGF2mSmallM tmp = matrix[first];
+ matrix[first] = matrix[second];
+ matrix[second] = tmp;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/RandUtils.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/RandUtils.java
new file mode 100644
index 00000000..dbb1d4a8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/RandUtils.java
@@ -0,0 +1,25 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+import java.security.SecureRandom;
+
+public class RandUtils
+{
+ static int nextInt(SecureRandom rand, int n)
+ {
+
+ if ((n & -n) == n) // i.e., n is a power of 2
+ {
+ return (int)((n * (long)(rand.nextInt() >>> 1)) >> 31);
+ }
+
+ int bits, value;
+ do
+ {
+ bits = rand.nextInt() >>> 1;
+ value = bits % n;
+ }
+ while (bits - value + (n - 1) < 0);
+
+ return value;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Vector.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Vector.java
new file mode 100644
index 00000000..7e171643
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Vector.java
@@ -0,0 +1,69 @@
+package org.bouncycastle.pqc.math.linearalgebra;
+
+/**
+ * This abstract class defines vectors. It holds the length of vector.
+ */
+public abstract class Vector
+{
+
+ /**
+ * the length of this vector
+ */
+ protected int length;
+
+ /**
+ * @return the length of this vector
+ */
+ public final int getLength()
+ {
+ return length;
+ }
+
+ /**
+ * @return this vector as byte array
+ */
+ public abstract byte[] getEncoded();
+
+ /**
+ * Return whether this is the zero vector (i.e., all elements are zero).
+ *
+ * @return <tt>true</tt> if this is the zero vector, <tt>false</tt>
+ * otherwise
+ */
+ public abstract boolean isZero();
+
+ /**
+ * Add another vector to this vector.
+ *
+ * @param addend the other vector
+ * @return <tt>this + addend</tt>
+ */
+ public abstract Vector add(Vector addend);
+
+ /**
+ * Multiply this vector with a permutation.
+ *
+ * @param p the permutation
+ * @return <tt>this*p = p*this</tt>
+ */
+ public abstract Vector multiply(Permutation p);
+
+ /**
+ * Check if the given object is equal to this vector.
+ *
+ * @param other vector
+ * @return the result of the comparison
+ */
+ public abstract boolean equals(Object other);
+
+ /**
+ * @return the hash code of this vector
+ */
+ public abstract int hashCode();
+
+ /**
+ * @return a human readable form of this vector
+ */
+ public abstract String toString();
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/ntru/euclid/BigIntEuclidean.java b/core/src/main/java/org/bouncycastle/pqc/math/ntru/euclid/BigIntEuclidean.java
new file mode 100644
index 00000000..b5af2ec5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/ntru/euclid/BigIntEuclidean.java
@@ -0,0 +1,54 @@
+package org.bouncycastle.pqc.math.ntru.euclid;
+
+import java.math.BigInteger;
+
+/**
+ * Extended Euclidean Algorithm in <code>BigInteger</code>s
+ */
+public class BigIntEuclidean
+{
+ public BigInteger x, y, gcd;
+
+ private BigIntEuclidean()
+ {
+ }
+
+ /**
+ * Runs the EEA on two <code>BigInteger</code>s<br/>
+ * Implemented from pseudocode on <a href="http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm">Wikipedia</a>.
+ *
+ * @param a
+ * @param b
+ * @return a <code>BigIntEuclidean</code> object that contains the result in the variables <code>x</code>, <code>y</code>, and <code>gcd</code>
+ */
+ public static BigIntEuclidean calculate(BigInteger a, BigInteger b)
+ {
+ BigInteger x = BigInteger.ZERO;
+ BigInteger lastx = BigInteger.ONE;
+ BigInteger y = BigInteger.ONE;
+ BigInteger lasty = BigInteger.ZERO;
+ while (!b.equals(BigInteger.ZERO))
+ {
+ BigInteger[] quotientAndRemainder = a.divideAndRemainder(b);
+ BigInteger quotient = quotientAndRemainder[0];
+
+ BigInteger temp = a;
+ a = b;
+ b = quotientAndRemainder[1];
+
+ temp = x;
+ x = lastx.subtract(quotient.multiply(x));
+ lastx = temp;
+
+ temp = y;
+ y = lasty.subtract(quotient.multiply(y));
+ lasty = temp;
+ }
+
+ BigIntEuclidean result = new BigIntEuclidean();
+ result.x = lastx;
+ result.y = lasty;
+ result.gcd = a;
+ return result;
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/ntru/euclid/IntEuclidean.java b/core/src/main/java/org/bouncycastle/pqc/math/ntru/euclid/IntEuclidean.java
new file mode 100644
index 00000000..3ada3d4e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/ntru/euclid/IntEuclidean.java
@@ -0,0 +1,51 @@
+package org.bouncycastle.pqc.math.ntru.euclid;
+
+/**
+ * Extended Euclidean Algorithm in <code>int</code>s
+ */
+public class IntEuclidean
+{
+ public int x, y, gcd;
+
+ private IntEuclidean()
+ {
+ }
+
+ /**
+ * Runs the EEA on two <code>int</code>s<br/>
+ * Implemented from pseudocode on <a href="http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm">Wikipedia</a>.
+ *
+ * @param a
+ * @param b
+ * @return a <code>IntEuclidean</code> object that contains the result in the variables <code>x</code>, <code>y</code>, and <code>gcd</code>
+ */
+ public static IntEuclidean calculate(int a, int b)
+ {
+ int x = 0;
+ int lastx = 1;
+ int y = 1;
+ int lasty = 0;
+ while (b != 0)
+ {
+ int quotient = a / b;
+
+ int temp = a;
+ a = b;
+ b = temp % b;
+
+ temp = x;
+ x = lastx - quotient * x;
+ lastx = temp;
+
+ temp = y;
+ y = lasty - quotient * y;
+ lasty = temp;
+ }
+
+ IntEuclidean result = new IntEuclidean();
+ result.x = lastx;
+ result.y = lasty;
+ result.gcd = a;
+ return result;
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/BigDecimalPolynomial.java b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/BigDecimalPolynomial.java
new file mode 100644
index 00000000..697f51a6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/BigDecimalPolynomial.java
@@ -0,0 +1,258 @@
+package org.bouncycastle.pqc.math.ntru.polynomial;
+
+import java.math.BigDecimal;
+
+/**
+ * A polynomial with {@link BigDecimal} coefficients.
+ * Some methods (like <code>add</code>) change the polynomial, others (like <code>mult</code>) do
+ * not but return the result as a new polynomial.
+ */
+public class BigDecimalPolynomial
+{
+ private static final BigDecimal ZERO = new BigDecimal("0");
+ private static final BigDecimal ONE_HALF = new BigDecimal("0.5");
+
+ BigDecimal[] coeffs;
+
+ /**
+ * Constructs a new polynomial with <code>N</code> coefficients initialized to 0.
+ *
+ * @param N the number of coefficients
+ */
+ BigDecimalPolynomial(int N)
+ {
+ coeffs = new BigDecimal[N];
+ for (int i = 0; i < N; i++)
+ {
+ coeffs[i] = ZERO;
+ }
+ }
+
+ /**
+ * Constructs a new polynomial with a given set of coefficients.
+ *
+ * @param coeffs the coefficients
+ */
+ BigDecimalPolynomial(BigDecimal[] coeffs)
+ {
+ this.coeffs = coeffs;
+ }
+
+ /**
+ * Constructs a <code>BigDecimalPolynomial</code> from a <code>BigIntPolynomial</code>. The two polynomials are independent of each other.
+ *
+ * @param p the original polynomial
+ */
+ public BigDecimalPolynomial(BigIntPolynomial p)
+ {
+ int N = p.coeffs.length;
+ coeffs = new BigDecimal[N];
+ for (int i = 0; i < N; i++)
+ {
+ coeffs[i] = new BigDecimal(p.coeffs[i]);
+ }
+ }
+
+ /**
+ * Divides all coefficients by 2.
+ */
+ public void halve()
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].multiply(ONE_HALF);
+ }
+ }
+
+ /**
+ * Multiplies the polynomial by another. Does not change this polynomial
+ * but returns the result as a new polynomial.
+ *
+ * @param poly2 the polynomial to multiply by
+ * @return a new polynomial
+ */
+ public BigDecimalPolynomial mult(BigIntPolynomial poly2)
+ {
+ return mult(new BigDecimalPolynomial(poly2));
+ }
+
+ /**
+ * Multiplies the polynomial by another, taking the indices mod N. Does not
+ * change this polynomial but returns the result as a new polynomial.
+ *
+ * @param poly2 the polynomial to multiply by
+ * @return a new polynomial
+ */
+ public BigDecimalPolynomial mult(BigDecimalPolynomial poly2)
+ {
+ int N = coeffs.length;
+ if (poly2.coeffs.length != N)
+ {
+ throw new IllegalArgumentException("Number of coefficients must be the same");
+ }
+
+ BigDecimalPolynomial c = multRecursive(poly2);
+
+ if (c.coeffs.length > N)
+ {
+ for (int k = N; k < c.coeffs.length; k++)
+ {
+ c.coeffs[k - N] = c.coeffs[k - N].add(c.coeffs[k]);
+ }
+ c.coeffs = copyOf(c.coeffs, N);
+ }
+ return c;
+ }
+
+ /**
+ * Karazuba multiplication
+ */
+ private BigDecimalPolynomial multRecursive(BigDecimalPolynomial poly2)
+ {
+ BigDecimal[] a = coeffs;
+ BigDecimal[] b = poly2.coeffs;
+
+ int n = poly2.coeffs.length;
+ if (n <= 1)
+ {
+ BigDecimal[] c = coeffs.clone();
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ c[i] = c[i].multiply(poly2.coeffs[0]);
+ }
+ return new BigDecimalPolynomial(c);
+ }
+ else
+ {
+ int n1 = n / 2;
+
+ BigDecimalPolynomial a1 = new BigDecimalPolynomial(copyOf(a, n1));
+ BigDecimalPolynomial a2 = new BigDecimalPolynomial(copyOfRange(a, n1, n));
+ BigDecimalPolynomial b1 = new BigDecimalPolynomial(copyOf(b, n1));
+ BigDecimalPolynomial b2 = new BigDecimalPolynomial(copyOfRange(b, n1, n));
+
+ BigDecimalPolynomial A = (BigDecimalPolynomial)a1.clone();
+ A.add(a2);
+ BigDecimalPolynomial B = (BigDecimalPolynomial)b1.clone();
+ B.add(b2);
+
+ BigDecimalPolynomial c1 = a1.multRecursive(b1);
+ BigDecimalPolynomial c2 = a2.multRecursive(b2);
+ BigDecimalPolynomial c3 = A.multRecursive(B);
+ c3.sub(c1);
+ c3.sub(c2);
+
+ BigDecimalPolynomial c = new BigDecimalPolynomial(2 * n - 1);
+ for (int i = 0; i < c1.coeffs.length; i++)
+ {
+ c.coeffs[i] = c1.coeffs[i];
+ }
+ for (int i = 0; i < c3.coeffs.length; i++)
+ {
+ c.coeffs[n1 + i] = c.coeffs[n1 + i].add(c3.coeffs[i]);
+ }
+ for (int i = 0; i < c2.coeffs.length; i++)
+ {
+ c.coeffs[2 * n1 + i] = c.coeffs[2 * n1 + i].add(c2.coeffs[i]);
+ }
+ return c;
+ }
+ }
+
+ /**
+ * Adds another polynomial which can have a different number of coefficients.
+ *
+ * @param b another polynomial
+ */
+ public void add(BigDecimalPolynomial b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ int N = coeffs.length;
+ coeffs = copyOf(coeffs, b.coeffs.length);
+ for (int i = N; i < coeffs.length; i++)
+ {
+ coeffs[i] = ZERO;
+ }
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].add(b.coeffs[i]);
+ }
+ }
+
+ /**
+ * Subtracts another polynomial which can have a different number of coefficients.
+ *
+ * @param b
+ */
+ void sub(BigDecimalPolynomial b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ int N = coeffs.length;
+ coeffs = copyOf(coeffs, b.coeffs.length);
+ for (int i = N; i < coeffs.length; i++)
+ {
+ coeffs[i] = ZERO;
+ }
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].subtract(b.coeffs[i]);
+ }
+ }
+
+ /**
+ * Rounds all coefficients to the nearest integer.
+ *
+ * @return a new polynomial with <code>BigInteger</code> coefficients
+ */
+ public BigIntPolynomial round()
+ {
+ int N = coeffs.length;
+ BigIntPolynomial p = new BigIntPolynomial(N);
+ for (int i = 0; i < N; i++)
+ {
+ p.coeffs[i] = coeffs[i].setScale(0, BigDecimal.ROUND_HALF_EVEN).toBigInteger();
+ }
+ return p;
+ }
+
+ /**
+ * Makes a copy of the polynomial that is independent of the original.
+ */
+ public Object clone()
+ {
+ return new BigDecimalPolynomial(coeffs.clone());
+ }
+
+ private BigDecimal[] copyOf(BigDecimal[] a, int length)
+ {
+ BigDecimal[] tmp = new BigDecimal[length];
+
+ System.arraycopy(a, 0, tmp, 0, a.length < length ? a.length : length);
+
+ return tmp;
+ }
+
+ private BigDecimal[] copyOfRange(BigDecimal[] a, int from, int to)
+ {
+ int newLength = to - from;
+ BigDecimal[] tmp = new BigDecimal[to - from];
+
+ System.arraycopy(a, from, tmp, 0, (a.length - from) < newLength ? (a.length - from) : newLength);
+
+ return tmp;
+ }
+
+ public BigDecimal[] getCoeffs()
+ {
+ BigDecimal[] tmp = new BigDecimal[coeffs.length];
+
+ System.arraycopy(coeffs, 0, tmp, 0, coeffs.length);
+
+ return tmp;
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/BigIntPolynomial.java b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/BigIntPolynomial.java
new file mode 100644
index 00000000..fadd3912
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/BigIntPolynomial.java
@@ -0,0 +1,394 @@
+package org.bouncycastle.pqc.math.ntru.polynomial;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A polynomial with {@link BigInteger} coefficients.<br/>
+ * Some methods (like <code>add</code>) change the polynomial, others (like <code>mult</code>) do
+ * not but return the result as a new polynomial.
+ */
+public class BigIntPolynomial
+{
+ private final static double LOG_10_2 = Math.log10(2);
+
+ BigInteger[] coeffs;
+
+ /**
+ * Constructs a new polynomial with <code>N</code> coefficients initialized to 0.
+ *
+ * @param N the number of coefficients
+ */
+ BigIntPolynomial(int N)
+ {
+ coeffs = new BigInteger[N];
+ for (int i = 0; i < N; i++)
+ {
+ coeffs[i] = Constants.BIGINT_ZERO;
+ }
+ }
+
+ /**
+ * Constructs a new polynomial with a given set of coefficients.
+ *
+ * @param coeffs the coefficients
+ */
+ BigIntPolynomial(BigInteger[] coeffs)
+ {
+ this.coeffs = coeffs;
+ }
+
+ /**
+ * Constructs a <code>BigIntPolynomial</code> from a <code>IntegerPolynomial</code>. The two polynomials are
+ * independent of each other.
+ *
+ * @param p the original polynomial
+ */
+ public BigIntPolynomial(IntegerPolynomial p)
+ {
+ coeffs = new BigInteger[p.coeffs.length];
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] = BigInteger.valueOf(p.coeffs[i]);
+ }
+ }
+
+ /**
+ * Generates a random polynomial with <code>numOnes</code> coefficients equal to 1,
+ * <code>numNegOnes</code> coefficients equal to -1, and the rest equal to 0.
+ *
+ * @param N number of coefficients
+ * @param numOnes number of 1's
+ * @param numNegOnes number of -1's
+ * @return
+ */
+ static BigIntPolynomial generateRandomSmall(int N, int numOnes, int numNegOnes)
+ {
+ List coeffs = new ArrayList();
+ for (int i = 0; i < numOnes; i++)
+ {
+ coeffs.add(Constants.BIGINT_ONE);
+ }
+ for (int i = 0; i < numNegOnes; i++)
+ {
+ coeffs.add(BigInteger.valueOf(-1));
+ }
+ while (coeffs.size() < N)
+ {
+ coeffs.add(Constants.BIGINT_ZERO);
+ }
+ Collections.shuffle(coeffs, new SecureRandom());
+
+ BigIntPolynomial poly = new BigIntPolynomial(N);
+ for (int i = 0; i < coeffs.size(); i++)
+ {
+ poly.coeffs[i] = (BigInteger)coeffs.get(i);
+ }
+ return poly;
+ }
+
+ /**
+ * Multiplies the polynomial by another, taking the indices mod N. Does not
+ * change this polynomial but returns the result as a new polynomial.<br/>
+ * Both polynomials must have the same number of coefficients.
+ *
+ * @param poly2 the polynomial to multiply by
+ * @return a new polynomial
+ */
+ public BigIntPolynomial mult(BigIntPolynomial poly2)
+ {
+ int N = coeffs.length;
+ if (poly2.coeffs.length != N)
+ {
+ throw new IllegalArgumentException("Number of coefficients must be the same");
+ }
+
+ BigIntPolynomial c = multRecursive(poly2);
+
+ if (c.coeffs.length > N)
+ {
+ for (int k = N; k < c.coeffs.length; k++)
+ {
+ c.coeffs[k - N] = c.coeffs[k - N].add(c.coeffs[k]);
+ }
+ c.coeffs = Arrays.copyOf(c.coeffs, N);
+ }
+ return c;
+ }
+
+ /**
+ * Karazuba multiplication
+ */
+ private BigIntPolynomial multRecursive(BigIntPolynomial poly2)
+ {
+ BigInteger[] a = coeffs;
+ BigInteger[] b = poly2.coeffs;
+
+ int n = poly2.coeffs.length;
+ if (n <= 1)
+ {
+ BigInteger[] c = Arrays.clone(coeffs);
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ c[i] = c[i].multiply(poly2.coeffs[0]);
+ }
+ return new BigIntPolynomial(c);
+ }
+ else
+ {
+ int n1 = n / 2;
+
+ BigIntPolynomial a1 = new BigIntPolynomial(Arrays.copyOf(a, n1));
+ BigIntPolynomial a2 = new BigIntPolynomial(Arrays.copyOfRange(a, n1, n));
+ BigIntPolynomial b1 = new BigIntPolynomial(Arrays.copyOf(b, n1));
+ BigIntPolynomial b2 = new BigIntPolynomial(Arrays.copyOfRange(b, n1, n));
+
+ BigIntPolynomial A = (BigIntPolynomial)a1.clone();
+ A.add(a2);
+ BigIntPolynomial B = (BigIntPolynomial)b1.clone();
+ B.add(b2);
+
+ BigIntPolynomial c1 = a1.multRecursive(b1);
+ BigIntPolynomial c2 = a2.multRecursive(b2);
+ BigIntPolynomial c3 = A.multRecursive(B);
+ c3.sub(c1);
+ c3.sub(c2);
+
+ BigIntPolynomial c = new BigIntPolynomial(2 * n - 1);
+ for (int i = 0; i < c1.coeffs.length; i++)
+ {
+ c.coeffs[i] = c1.coeffs[i];
+ }
+ for (int i = 0; i < c3.coeffs.length; i++)
+ {
+ c.coeffs[n1 + i] = c.coeffs[n1 + i].add(c3.coeffs[i]);
+ }
+ for (int i = 0; i < c2.coeffs.length; i++)
+ {
+ c.coeffs[2 * n1 + i] = c.coeffs[2 * n1 + i].add(c2.coeffs[i]);
+ }
+ return c;
+ }
+ }
+
+ /**
+ * Adds another polynomial which can have a different number of coefficients,
+ * and takes the coefficient values mod <code>modulus</code>.
+ *
+ * @param b another polynomial
+ */
+ void add(BigIntPolynomial b, BigInteger modulus)
+ {
+ add(b);
+ mod(modulus);
+ }
+
+ /**
+ * Adds another polynomial which can have a different number of coefficients.
+ *
+ * @param b another polynomial
+ */
+ public void add(BigIntPolynomial b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ int N = coeffs.length;
+ coeffs = Arrays.copyOf(coeffs, b.coeffs.length);
+ for (int i = N; i < coeffs.length; i++)
+ {
+ coeffs[i] = Constants.BIGINT_ZERO;
+ }
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].add(b.coeffs[i]);
+ }
+ }
+
+ /**
+ * Subtracts another polynomial which can have a different number of coefficients.
+ *
+ * @param b another polynomial
+ */
+ public void sub(BigIntPolynomial b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ int N = coeffs.length;
+ coeffs = Arrays.copyOf(coeffs, b.coeffs.length);
+ for (int i = N; i < coeffs.length; i++)
+ {
+ coeffs[i] = Constants.BIGINT_ZERO;
+ }
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].subtract(b.coeffs[i]);
+ }
+ }
+
+ /**
+ * Multiplies each coefficient by a <code>BigInteger</code>. Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param factor
+ */
+ public void mult(BigInteger factor)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].multiply(factor);
+ }
+ }
+
+ /**
+ * Multiplies each coefficient by a <code>int</code>. Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param factor
+ */
+ void mult(int factor)
+ {
+ mult(BigInteger.valueOf(factor));
+ }
+
+ /**
+ * Divides each coefficient by a <code>BigInteger</code> and rounds the result to the nearest whole number.<br/>
+ * Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param divisor the number to divide by
+ */
+ public void div(BigInteger divisor)
+ {
+ BigInteger d = divisor.add(Constants.BIGINT_ONE).divide(BigInteger.valueOf(2));
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].compareTo(Constants.BIGINT_ZERO) > 0 ? coeffs[i].add(d) : coeffs[i].add(d.negate());
+ coeffs[i] = coeffs[i].divide(divisor);
+ }
+ }
+
+ /**
+ * Divides each coefficient by a <code>BigDecimal</code> and rounds the result to <code>decimalPlaces</code> places.
+ *
+ * @param divisor the number to divide by
+ * @param decimalPlaces the number of fractional digits to round the result to
+ * @return a new <code>BigDecimalPolynomial</code>
+ */
+ public BigDecimalPolynomial div(BigDecimal divisor, int decimalPlaces)
+ {
+ BigInteger max = maxCoeffAbs();
+ int coeffLength = (int)(max.bitLength() * LOG_10_2) + 1;
+ // factor = 1/divisor
+ BigDecimal factor = Constants.BIGDEC_ONE.divide(divisor, coeffLength + decimalPlaces + 1, BigDecimal.ROUND_HALF_EVEN);
+
+ // multiply each coefficient by factor
+ BigDecimalPolynomial p = new BigDecimalPolynomial(coeffs.length);
+ for (int i = 0; i < coeffs.length; i++)
+ // multiply, then truncate after decimalPlaces so subsequent operations aren't slowed down
+ {
+ p.coeffs[i] = new BigDecimal(coeffs[i]).multiply(factor).setScale(decimalPlaces, BigDecimal.ROUND_HALF_EVEN);
+ }
+
+ return p;
+ }
+
+ /**
+ * Returns the base10 length of the largest coefficient.
+ *
+ * @return length of the longest coefficient
+ */
+ public int getMaxCoeffLength()
+ {
+ return (int)(maxCoeffAbs().bitLength() * LOG_10_2) + 1;
+ }
+
+ private BigInteger maxCoeffAbs()
+ {
+ BigInteger max = coeffs[0].abs();
+ for (int i = 1; i < coeffs.length; i++)
+ {
+ BigInteger coeff = coeffs[i].abs();
+ if (coeff.compareTo(max) > 0)
+ {
+ max = coeff;
+ }
+ }
+ return max;
+ }
+
+ /**
+ * Takes each coefficient modulo a number.
+ *
+ * @param modulus
+ */
+ public void mod(BigInteger modulus)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].mod(modulus);
+ }
+ }
+
+ /**
+ * Returns the sum of all coefficients, i.e. evaluates the polynomial at 0.
+ *
+ * @return the sum of all coefficients
+ */
+ BigInteger sumCoeffs()
+ {
+ BigInteger sum = Constants.BIGINT_ZERO;
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ sum = sum.add(coeffs[i]);
+ }
+ return sum;
+ }
+
+ /**
+ * Makes a copy of the polynomial that is independent of the original.
+ */
+ public Object clone()
+ {
+ return new BigIntPolynomial(coeffs.clone());
+ }
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Arrays.hashCode(coeffs);
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ BigIntPolynomial other = (BigIntPolynomial)obj;
+ if (!Arrays.areEqual(coeffs, other.coeffs))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public BigInteger[] getCoeffs()
+ {
+ return Arrays.clone(coeffs);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/Constants.java b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/Constants.java
new file mode 100644
index 00000000..2b41b19a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/Constants.java
@@ -0,0 +1,12 @@
+package org.bouncycastle.pqc.math.ntru.polynomial;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+public class Constants
+{
+ static final BigInteger BIGINT_ZERO = BigInteger.valueOf(0);
+ static final BigInteger BIGINT_ONE = BigInteger.valueOf(1);
+
+ static final BigDecimal BIGDEC_ONE = BigDecimal.valueOf(1);
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/DenseTernaryPolynomial.java b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/DenseTernaryPolynomial.java
new file mode 100644
index 00000000..85730dab
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/DenseTernaryPolynomial.java
@@ -0,0 +1,142 @@
+package org.bouncycastle.pqc.math.ntru.polynomial;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.pqc.math.ntru.util.Util;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A <code>TernaryPolynomial</code> with a "high" number of nonzero coefficients.
+ */
+public class DenseTernaryPolynomial
+ extends IntegerPolynomial
+ implements TernaryPolynomial
+{
+
+ /**
+ * Constructs a new <code>DenseTernaryPolynomial</code> with <code>N</code> coefficients.
+ *
+ * @param N the number of coefficients
+ */
+ DenseTernaryPolynomial(int N)
+ {
+ super(N);
+ checkTernarity();
+ }
+
+ /**
+ * Constructs a <code>DenseTernaryPolynomial</code> from a <code>IntegerPolynomial</code>. The two polynomials are
+ * independent of each other.
+ *
+ * @param intPoly the original polynomial
+ */
+ public DenseTernaryPolynomial(IntegerPolynomial intPoly)
+ {
+ this(intPoly.coeffs);
+ }
+
+ /**
+ * Constructs a new <code>DenseTernaryPolynomial</code> with a given set of coefficients.
+ *
+ * @param coeffs the coefficients
+ */
+ public DenseTernaryPolynomial(int[] coeffs)
+ {
+ super(coeffs);
+ checkTernarity();
+ }
+
+ private void checkTernarity()
+ {
+ for (int i = 0; i != coeffs.length; i++)
+ {
+ int c = coeffs[i];
+ if (c < -1 || c > 1)
+ {
+ throw new IllegalStateException("Illegal value: " + c + ", must be one of {-1, 0, 1}");
+ }
+ }
+ }
+
+ /**
+ * Generates a random polynomial with <code>numOnes</code> coefficients equal to 1,
+ * <code>numNegOnes</code> coefficients equal to -1, and the rest equal to 0.
+ *
+ * @param N number of coefficients
+ * @param numOnes number of 1's
+ * @param numNegOnes number of -1's
+ */
+ public static DenseTernaryPolynomial generateRandom(int N, int numOnes, int numNegOnes, SecureRandom random)
+ {
+ int[] coeffs = Util.generateRandomTernary(N, numOnes, numNegOnes, random);
+ return new DenseTernaryPolynomial(coeffs);
+ }
+
+ /**
+ * Generates a polynomial with coefficients randomly selected from <code>{-1, 0, 1}</code>.
+ *
+ * @param N number of coefficients
+ */
+ public static DenseTernaryPolynomial generateRandom(int N, SecureRandom random)
+ {
+ DenseTernaryPolynomial poly = new DenseTernaryPolynomial(N);
+ for (int i = 0; i < N; i++)
+ {
+ poly.coeffs[i] = random.nextInt(3) - 1;
+ }
+ return poly;
+ }
+
+ public IntegerPolynomial mult(IntegerPolynomial poly2, int modulus)
+ {
+ // even on 32-bit systems, LongPolynomial5 multiplies faster than IntegerPolynomial
+ if (modulus == 2048)
+ {
+ IntegerPolynomial poly2Pos = (IntegerPolynomial)poly2.clone();
+ poly2Pos.modPositive(2048);
+ LongPolynomial5 poly5 = new LongPolynomial5(poly2Pos);
+ return poly5.mult(this).toIntegerPolynomial();
+ }
+ else
+ {
+ return super.mult(poly2, modulus);
+ }
+ }
+
+ public int[] getOnes()
+ {
+ int N = coeffs.length;
+ int[] ones = new int[N];
+ int onesIdx = 0;
+ for (int i = 0; i < N; i++)
+ {
+ int c = coeffs[i];
+ if (c == 1)
+ {
+ ones[onesIdx++] = i;
+ }
+ }
+ return Arrays.copyOf(ones, onesIdx);
+ }
+
+ public int[] getNegOnes()
+ {
+ int N = coeffs.length;
+ int[] negOnes = new int[N];
+ int negOnesIdx = 0;
+ for (int i = 0; i < N; i++)
+ {
+ int c = coeffs[i];
+ if (c == -1)
+ {
+ negOnes[negOnesIdx++] = i;
+ }
+ }
+ return Arrays.copyOf(negOnes, negOnesIdx);
+ }
+
+ public int size()
+ {
+ return coeffs.length;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/IntegerPolynomial.java b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/IntegerPolynomial.java
new file mode 100644
index 00000000..76ffac6b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/IntegerPolynomial.java
@@ -0,0 +1,1358 @@
+package org.bouncycastle.pqc.math.ntru.polynomial;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import org.bouncycastle.pqc.math.ntru.euclid.BigIntEuclidean;
+import org.bouncycastle.pqc.math.ntru.util.ArrayEncoder;
+import org.bouncycastle.pqc.math.ntru.util.Util;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A polynomial with <code>int</code> coefficients.<br/>
+ * Some methods (like <code>add</code>) change the polynomial, others (like <code>mult</code>) do
+ * not but return the result as a new polynomial.
+ */
+public class IntegerPolynomial
+ implements Polynomial
+{
+ private static final int NUM_EQUAL_RESULTANTS = 3;
+ /**
+ * Prime numbers &gt; 4500 for resultant computation. Starting them below ~4400 causes incorrect results occasionally.
+ * Fortunately, 4500 is about the optimum number for performance.<br/>
+ * This array contains enough prime numbers so primes never have to be computed on-line for any standard {@link org.bouncycastle.pqc.crypto.ntru.NTRUSigningParameters}.
+ */
+ private static final int[] PRIMES = new int[]{
+ 4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583,
+ 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657,
+ 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751,
+ 4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831,
+ 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937,
+ 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003,
+ 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087,
+ 5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179,
+ 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279,
+ 5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387,
+ 5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443,
+ 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521,
+ 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639,
+ 5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693,
+ 5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791,
+ 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857,
+ 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939,
+ 5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053,
+ 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133,
+ 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221,
+ 6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301,
+ 6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367,
+ 6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473,
+ 6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571,
+ 6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673,
+ 6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761,
+ 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833,
+ 6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917,
+ 6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997,
+ 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103,
+ 7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207,
+ 7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297,
+ 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411,
+ 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499,
+ 7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561,
+ 7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643,
+ 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723,
+ 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829,
+ 7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919,
+ 7927, 7933, 7937, 7949, 7951, 7963, 7993, 8009, 8011, 8017,
+ 8039, 8053, 8059, 8069, 8081, 8087, 8089, 8093, 8101, 8111,
+ 8117, 8123, 8147, 8161, 8167, 8171, 8179, 8191, 8209, 8219,
+ 8221, 8231, 8233, 8237, 8243, 8263, 8269, 8273, 8287, 8291,
+ 8293, 8297, 8311, 8317, 8329, 8353, 8363, 8369, 8377, 8387,
+ 8389, 8419, 8423, 8429, 8431, 8443, 8447, 8461, 8467, 8501,
+ 8513, 8521, 8527, 8537, 8539, 8543, 8563, 8573, 8581, 8597,
+ 8599, 8609, 8623, 8627, 8629, 8641, 8647, 8663, 8669, 8677,
+ 8681, 8689, 8693, 8699, 8707, 8713, 8719, 8731, 8737, 8741,
+ 8747, 8753, 8761, 8779, 8783, 8803, 8807, 8819, 8821, 8831,
+ 8837, 8839, 8849, 8861, 8863, 8867, 8887, 8893, 8923, 8929,
+ 8933, 8941, 8951, 8963, 8969, 8971, 8999, 9001, 9007, 9011,
+ 9013, 9029, 9041, 9043, 9049, 9059, 9067, 9091, 9103, 9109,
+ 9127, 9133, 9137, 9151, 9157, 9161, 9173, 9181, 9187, 9199,
+ 9203, 9209, 9221, 9227, 9239, 9241, 9257, 9277, 9281, 9283,
+ 9293, 9311, 9319, 9323, 9337, 9341, 9343, 9349, 9371, 9377,
+ 9391, 9397, 9403, 9413, 9419, 9421, 9431, 9433, 9437, 9439,
+ 9461, 9463, 9467, 9473, 9479, 9491, 9497, 9511, 9521, 9533,
+ 9539, 9547, 9551, 9587, 9601, 9613, 9619, 9623, 9629, 9631,
+ 9643, 9649, 9661, 9677, 9679, 9689, 9697, 9719, 9721, 9733,
+ 9739, 9743, 9749, 9767, 9769, 9781, 9787, 9791, 9803, 9811,
+ 9817, 9829, 9833, 9839, 9851, 9857, 9859, 9871, 9883, 9887,
+ 9901, 9907, 9923, 9929, 9931, 9941, 9949, 9967, 9973};
+ private static final List BIGINT_PRIMES;
+
+ static
+ {
+ BIGINT_PRIMES = new ArrayList();
+ for (int i = 0; i != PRIMES.length; i++)
+ {
+ BIGINT_PRIMES.add(BigInteger.valueOf(PRIMES[i]));
+ }
+ }
+
+ public int[] coeffs;
+
+ /**
+ * Constructs a new polynomial with <code>N</code> coefficients initialized to 0.
+ *
+ * @param N the number of coefficients
+ */
+ public IntegerPolynomial(int N)
+ {
+ coeffs = new int[N];
+ }
+
+ /**
+ * Constructs a new polynomial with a given set of coefficients.
+ *
+ * @param coeffs the coefficients
+ */
+ public IntegerPolynomial(int[] coeffs)
+ {
+ this.coeffs = coeffs;
+ }
+
+ /**
+ * Constructs a <code>IntegerPolynomial</code> from a <code>BigIntPolynomial</code>. The two polynomials are independent of each other.
+ *
+ * @param p the original polynomial
+ */
+ public IntegerPolynomial(BigIntPolynomial p)
+ {
+ coeffs = new int[p.coeffs.length];
+ for (int i = 0; i < p.coeffs.length; i++)
+ {
+ coeffs[i] = p.coeffs[i].intValue();
+ }
+ }
+
+ /**
+ * Decodes a byte array to a polynomial with <code>N</code> ternary coefficients<br/>
+ * Ignores any excess bytes.
+ *
+ * @param data an encoded ternary polynomial
+ * @param N number of coefficients
+ * @return the decoded polynomial
+ */
+ public static IntegerPolynomial fromBinary3Sves(byte[] data, int N)
+ {
+ return new IntegerPolynomial(ArrayEncoder.decodeMod3Sves(data, N));
+ }
+
+ /**
+ * Converts a byte array produced by {@link #toBinary3Tight()} to a polynomial.
+ *
+ * @param b a byte array
+ * @param N number of coefficients
+ * @return the decoded polynomial
+ */
+ public static IntegerPolynomial fromBinary3Tight(byte[] b, int N)
+ {
+ return new IntegerPolynomial(ArrayEncoder.decodeMod3Tight(b, N));
+ }
+
+ /**
+ * Reads data produced by {@link #toBinary3Tight()} from an input stream and converts it to a polynomial.
+ *
+ * @param is an input stream
+ * @param N number of coefficients
+ * @return the decoded polynomial
+ */
+ public static IntegerPolynomial fromBinary3Tight(InputStream is, int N)
+ throws IOException
+ {
+ return new IntegerPolynomial(ArrayEncoder.decodeMod3Tight(is, N));
+ }
+
+ /**
+ * Returns a polynomial with N coefficients between <code>0</code> and <code>q-1</code>.<br/>
+ * <code>q</code> must be a power of 2.<br/>
+ * Ignores any excess bytes.
+ *
+ * @param data an encoded ternary polynomial
+ * @param N number of coefficients
+ * @param q
+ * @return the decoded polynomial
+ */
+ public static IntegerPolynomial fromBinary(byte[] data, int N, int q)
+ {
+ return new IntegerPolynomial(ArrayEncoder.decodeModQ(data, N, q));
+ }
+
+ /**
+ * Returns a polynomial with N coefficients between <code>0</code> and <code>q-1</code>.<br/>
+ * <code>q</code> must be a power of 2.<br/>
+ * Ignores any excess bytes.
+ *
+ * @param is an encoded ternary polynomial
+ * @param N number of coefficients
+ * @param q
+ * @return the decoded polynomial
+ */
+ public static IntegerPolynomial fromBinary(InputStream is, int N, int q)
+ throws IOException
+ {
+ return new IntegerPolynomial(ArrayEncoder.decodeModQ(is, N, q));
+ }
+
+ /**
+ * Encodes a polynomial with ternary coefficients to binary.
+ * <code>coeffs[2*i]</code> and <code>coeffs[2*i+1]</code> must not both equal -1 for any integer </code>i<code>,
+ * so this method is only safe to use with polynomials produced by <code>fromBinary3Sves()</code>.
+ *
+ * @return the encoded polynomial
+ */
+ public byte[] toBinary3Sves()
+ {
+ return ArrayEncoder.encodeMod3Sves(coeffs);
+ }
+
+ /**
+ * Converts a polynomial with ternary coefficients to binary.
+ *
+ * @return the encoded polynomial
+ */
+ public byte[] toBinary3Tight()
+ {
+ BigInteger sum = Constants.BIGINT_ZERO;
+ for (int i = coeffs.length - 1; i >= 0; i--)
+ {
+ sum = sum.multiply(BigInteger.valueOf(3));
+ sum = sum.add(BigInteger.valueOf(coeffs[i] + 1));
+ }
+
+ int size = (BigInteger.valueOf(3).pow(coeffs.length).bitLength() + 7) / 8;
+ byte[] arr = sum.toByteArray();
+
+ if (arr.length < size)
+ {
+ // pad with leading zeros so arr.length==size
+ byte[] arr2 = new byte[size];
+ System.arraycopy(arr, 0, arr2, size - arr.length, arr.length);
+ return arr2;
+ }
+
+ if (arr.length > size)
+ // drop sign bit
+ {
+ arr = Arrays.copyOfRange(arr, 1, arr.length);
+ }
+ return arr;
+ }
+
+ /**
+ * Encodes a polynomial whose coefficients are between 0 and q, to binary. q must be a power of 2.
+ *
+ * @param q
+ * @return the encoded polynomial
+ */
+ public byte[] toBinary(int q)
+ {
+ return ArrayEncoder.encodeModQ(coeffs, q);
+ }
+
+ /**
+ * Multiplies the polynomial with another, taking the values mod modulus and the indices mod N
+ */
+ public IntegerPolynomial mult(IntegerPolynomial poly2, int modulus)
+ {
+ IntegerPolynomial c = mult(poly2);
+ c.mod(modulus);
+ return c;
+ }
+
+ /**
+ * Multiplies the polynomial with another, taking the indices mod N
+ */
+ public IntegerPolynomial mult(IntegerPolynomial poly2)
+ {
+ int N = coeffs.length;
+ if (poly2.coeffs.length != N)
+ {
+ throw new IllegalArgumentException("Number of coefficients must be the same");
+ }
+
+ IntegerPolynomial c = multRecursive(poly2);
+
+ if (c.coeffs.length > N)
+ {
+ for (int k = N; k < c.coeffs.length; k++)
+ {
+ c.coeffs[k - N] += c.coeffs[k];
+ }
+ c.coeffs = Arrays.copyOf(c.coeffs, N);
+ }
+ return c;
+ }
+
+ public BigIntPolynomial mult(BigIntPolynomial poly2)
+ {
+ return new BigIntPolynomial(this).mult(poly2);
+ }
+
+ /**
+ * Karazuba multiplication
+ */
+ private IntegerPolynomial multRecursive(IntegerPolynomial poly2)
+ {
+ int[] a = coeffs;
+ int[] b = poly2.coeffs;
+
+ int n = poly2.coeffs.length;
+ if (n <= 32)
+ {
+ int cn = 2 * n - 1;
+ IntegerPolynomial c = new IntegerPolynomial(new int[cn]);
+ for (int k = 0; k < cn; k++)
+ {
+ for (int i = Math.max(0, k - n + 1); i <= Math.min(k, n - 1); i++)
+ {
+ c.coeffs[k] += b[i] * a[k - i];
+ }
+ }
+ return c;
+ }
+ else
+ {
+ int n1 = n / 2;
+
+ IntegerPolynomial a1 = new IntegerPolynomial(Arrays.copyOf(a, n1));
+ IntegerPolynomial a2 = new IntegerPolynomial(Arrays.copyOfRange(a, n1, n));
+ IntegerPolynomial b1 = new IntegerPolynomial(Arrays.copyOf(b, n1));
+ IntegerPolynomial b2 = new IntegerPolynomial(Arrays.copyOfRange(b, n1, n));
+
+ IntegerPolynomial A = (IntegerPolynomial)a1.clone();
+ A.add(a2);
+ IntegerPolynomial B = (IntegerPolynomial)b1.clone();
+ B.add(b2);
+
+ IntegerPolynomial c1 = a1.multRecursive(b1);
+ IntegerPolynomial c2 = a2.multRecursive(b2);
+ IntegerPolynomial c3 = A.multRecursive(B);
+ c3.sub(c1);
+ c3.sub(c2);
+
+ IntegerPolynomial c = new IntegerPolynomial(2 * n - 1);
+ for (int i = 0; i < c1.coeffs.length; i++)
+ {
+ c.coeffs[i] = c1.coeffs[i];
+ }
+ for (int i = 0; i < c3.coeffs.length; i++)
+ {
+ c.coeffs[n1 + i] += c3.coeffs[i];
+ }
+ for (int i = 0; i < c2.coeffs.length; i++)
+ {
+ c.coeffs[2 * n1 + i] += c2.coeffs[i];
+ }
+ return c;
+ }
+ }
+
+ /**
+ * Computes the inverse mod <code>q; q</code> must be a power of 2.<br/>
+ * Returns <code>null</code> if the polynomial is not invertible.
+ *
+ * @param q the modulus
+ * @return a new polynomial
+ */
+ public IntegerPolynomial invertFq(int q)
+ {
+ int N = coeffs.length;
+ int k = 0;
+ IntegerPolynomial b = new IntegerPolynomial(N + 1);
+ b.coeffs[0] = 1;
+ IntegerPolynomial c = new IntegerPolynomial(N + 1);
+ IntegerPolynomial f = new IntegerPolynomial(N + 1);
+ f.coeffs = Arrays.copyOf(coeffs, N + 1);
+ f.modPositive(2);
+ // set g(x) = x^N − 1
+ IntegerPolynomial g = new IntegerPolynomial(N + 1);
+ g.coeffs[0] = 1;
+ g.coeffs[N] = 1;
+ while (true)
+ {
+ while (f.coeffs[0] == 0)
+ {
+ for (int i = 1; i <= N; i++)
+ {
+ f.coeffs[i - 1] = f.coeffs[i]; // f(x) = f(x) / x
+ c.coeffs[N + 1 - i] = c.coeffs[N - i]; // c(x) = c(x) * x
+ }
+ f.coeffs[N] = 0;
+ c.coeffs[0] = 0;
+ k++;
+ if (f.equalsZero())
+ {
+ return null; // not invertible
+ }
+ }
+ if (f.equalsOne())
+ {
+ break;
+ }
+ if (f.degree() < g.degree())
+ {
+ // exchange f and g
+ IntegerPolynomial temp = f;
+ f = g;
+ g = temp;
+ // exchange b and c
+ temp = b;
+ b = c;
+ c = temp;
+ }
+ f.add(g, 2);
+ b.add(c, 2);
+ }
+
+ if (b.coeffs[N] != 0)
+ {
+ return null;
+ }
+ // Fq(x) = x^(N-k) * b(x)
+ IntegerPolynomial Fq = new IntegerPolynomial(N);
+ int j = 0;
+ k %= N;
+ for (int i = N - 1; i >= 0; i--)
+ {
+ j = i - k;
+ if (j < 0)
+ {
+ j += N;
+ }
+ Fq.coeffs[j] = b.coeffs[i];
+ }
+
+ return mod2ToModq(Fq, q);
+ }
+
+ /**
+ * Computes the inverse mod q from the inverse mod 2
+ *
+ * @param Fq
+ * @param q
+ * @return The inverse of this polynomial mod q
+ */
+ private IntegerPolynomial mod2ToModq(IntegerPolynomial Fq, int q)
+ {
+ if (Util.is64BitJVM() && q == 2048)
+ {
+ LongPolynomial2 thisLong = new LongPolynomial2(this);
+ LongPolynomial2 FqLong = new LongPolynomial2(Fq);
+ int v = 2;
+ while (v < q)
+ {
+ v *= 2;
+ LongPolynomial2 temp = (LongPolynomial2)FqLong.clone();
+ temp.mult2And(v - 1);
+ FqLong = thisLong.mult(FqLong).mult(FqLong);
+ temp.subAnd(FqLong, v - 1);
+ FqLong = temp;
+ }
+ return FqLong.toIntegerPolynomial();
+ }
+ else
+ {
+ int v = 2;
+ while (v < q)
+ {
+ v *= 2;
+ IntegerPolynomial temp = new IntegerPolynomial(Arrays.copyOf(Fq.coeffs, Fq.coeffs.length));
+ temp.mult2(v);
+ Fq = mult(Fq, v).mult(Fq, v);
+ temp.sub(Fq, v);
+ Fq = temp;
+ }
+ return Fq;
+ }
+ }
+
+ /**
+ * Computes the inverse mod 3.
+ * Returns <code>null</code> if the polynomial is not invertible.
+ *
+ * @return a new polynomial
+ */
+ public IntegerPolynomial invertF3()
+ {
+ int N = coeffs.length;
+ int k = 0;
+ IntegerPolynomial b = new IntegerPolynomial(N + 1);
+ b.coeffs[0] = 1;
+ IntegerPolynomial c = new IntegerPolynomial(N + 1);
+ IntegerPolynomial f = new IntegerPolynomial(N + 1);
+ f.coeffs = Arrays.copyOf(coeffs, N + 1);
+ f.modPositive(3);
+ // set g(x) = x^N − 1
+ IntegerPolynomial g = new IntegerPolynomial(N + 1);
+ g.coeffs[0] = -1;
+ g.coeffs[N] = 1;
+ while (true)
+ {
+ while (f.coeffs[0] == 0)
+ {
+ for (int i = 1; i <= N; i++)
+ {
+ f.coeffs[i - 1] = f.coeffs[i]; // f(x) = f(x) / x
+ c.coeffs[N + 1 - i] = c.coeffs[N - i]; // c(x) = c(x) * x
+ }
+ f.coeffs[N] = 0;
+ c.coeffs[0] = 0;
+ k++;
+ if (f.equalsZero())
+ {
+ return null; // not invertible
+ }
+ }
+ if (f.equalsAbsOne())
+ {
+ break;
+ }
+ if (f.degree() < g.degree())
+ {
+ // exchange f and g
+ IntegerPolynomial temp = f;
+ f = g;
+ g = temp;
+ // exchange b and c
+ temp = b;
+ b = c;
+ c = temp;
+ }
+ if (f.coeffs[0] == g.coeffs[0])
+ {
+ f.sub(g, 3);
+ b.sub(c, 3);
+ }
+ else
+ {
+ f.add(g, 3);
+ b.add(c, 3);
+ }
+ }
+
+ if (b.coeffs[N] != 0)
+ {
+ return null;
+ }
+ // Fp(x) = [+-] x^(N-k) * b(x)
+ IntegerPolynomial Fp = new IntegerPolynomial(N);
+ int j = 0;
+ k %= N;
+ for (int i = N - 1; i >= 0; i--)
+ {
+ j = i - k;
+ if (j < 0)
+ {
+ j += N;
+ }
+ Fp.coeffs[j] = f.coeffs[0] * b.coeffs[i];
+ }
+
+ Fp.ensurePositive(3);
+ return Fp;
+ }
+
+ /**
+ * Resultant of this polynomial with <code>x^n-1</code> using a probabilistic algorithm.
+ * <p/>
+ * Unlike EESS, this implementation does not compute all resultants modulo primes
+ * such that their product exceeds the maximum possible resultant, but rather stops
+ * when <code>NUM_EQUAL_RESULTANTS</code> consecutive modular resultants are equal.<br/>
+ * This means the return value may be incorrect. Experiments show this happens in
+ * about 1 out of 100 cases when <code>N=439</code> and <code>NUM_EQUAL_RESULTANTS=2</code>,
+ * so the likelyhood of leaving the loop too early is <code>(1/100)^(NUM_EQUAL_RESULTANTS-1)</code>.
+ * <p/>
+ * Because of the above, callers must verify the output and try a different polynomial if necessary.
+ *
+ * @return <code>(rho, res)</code> satisfying <code>res = rho*this + t*(x^n-1)</code> for some integer <code>t</code>.
+ */
+ public Resultant resultant()
+ {
+ int N = coeffs.length;
+
+ // Compute resultants modulo prime numbers. Continue until NUM_EQUAL_RESULTANTS consecutive modular resultants are equal.
+ LinkedList<ModularResultant> modResultants = new LinkedList<ModularResultant>();
+ BigInteger prime = null;
+ BigInteger pProd = Constants.BIGINT_ONE;
+ BigInteger res = Constants.BIGINT_ONE;
+ int numEqual = 1; // number of consecutive modular resultants equal to each other
+ Iterator<BigInteger> primes = BIGINT_PRIMES.iterator();
+ while (true)
+ {
+ prime = primes.hasNext() ? primes.next() : prime.nextProbablePrime();
+ ModularResultant crr = resultant(prime.intValue());
+ modResultants.add(crr);
+
+ BigInteger temp = pProd.multiply(prime);
+ BigIntEuclidean er = BigIntEuclidean.calculate(prime, pProd);
+ BigInteger resPrev = res;
+ res = res.multiply(er.x.multiply(prime));
+ BigInteger res2 = crr.res.multiply(er.y.multiply(pProd));
+ res = res.add(res2).mod(temp);
+ pProd = temp;
+
+ BigInteger pProd2 = pProd.divide(BigInteger.valueOf(2));
+ BigInteger pProd2n = pProd2.negate();
+ if (res.compareTo(pProd2) > 0)
+ {
+ res = res.subtract(pProd);
+ }
+ else if (res.compareTo(pProd2n) < 0)
+ {
+ res = res.add(pProd);
+ }
+
+ if (res.equals(resPrev))
+ {
+ numEqual++;
+ if (numEqual >= NUM_EQUAL_RESULTANTS)
+ {
+ break;
+ }
+ }
+ else
+ {
+ numEqual = 1;
+ }
+ }
+
+ // Combine modular rho's to obtain the final rho.
+ // For efficiency, first combine all pairs of small resultants to bigger resultants,
+ // then combine pairs of those, etc. until only one is left.
+ while (modResultants.size() > 1)
+ {
+ ModularResultant modRes1 = modResultants.removeFirst();
+ ModularResultant modRes2 = modResultants.removeFirst();
+ ModularResultant modRes3 = ModularResultant.combineRho(modRes1, modRes2);
+ modResultants.addLast(modRes3);
+ }
+ BigIntPolynomial rhoP = modResultants.getFirst().rho;
+
+ BigInteger pProd2 = pProd.divide(BigInteger.valueOf(2));
+ BigInteger pProd2n = pProd2.negate();
+ if (res.compareTo(pProd2) > 0)
+ {
+ res = res.subtract(pProd);
+ }
+ if (res.compareTo(pProd2n) < 0)
+ {
+ res = res.add(pProd);
+ }
+
+ for (int i = 0; i < N; i++)
+ {
+ BigInteger c = rhoP.coeffs[i];
+ if (c.compareTo(pProd2) > 0)
+ {
+ rhoP.coeffs[i] = c.subtract(pProd);
+ }
+ if (c.compareTo(pProd2n) < 0)
+ {
+ rhoP.coeffs[i] = c.add(pProd);
+ }
+ }
+
+ return new Resultant(rhoP, res);
+ }
+
+ /**
+ * Multithreaded version of {@link #resultant()}.
+ *
+ * @return <code>(rho, res)</code> satisfying <code>res = rho*this + t*(x^n-1)</code> for some integer <code>t</code>.
+ */
+ public Resultant resultantMultiThread()
+ {
+ int N = coeffs.length;
+
+ // upper bound for resultant(f, g) = ||f, 2||^deg(g) * ||g, 2||^deg(f) = squaresum(f)^(N/2) * 2^(deg(f)/2) because g(x)=x^N-1
+ // see http://jondalon.mathematik.uni-osnabrueck.de/staff/phpages/brunsw/CompAlg.pdf chapter 3
+ BigInteger max = squareSum().pow((N + 1) / 2);
+ max = max.multiply(BigInteger.valueOf(2).pow((degree() + 1) / 2));
+ BigInteger max2 = max.multiply(BigInteger.valueOf(2));
+
+ // compute resultants modulo prime numbers
+ BigInteger prime = BigInteger.valueOf(10000);
+ BigInteger pProd = Constants.BIGINT_ONE;
+ LinkedBlockingQueue<Future<ModularResultant>> resultantTasks = new LinkedBlockingQueue<Future<ModularResultant>>();
+ Iterator<BigInteger> primes = BIGINT_PRIMES.iterator();
+ ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
+ while (pProd.compareTo(max2) < 0)
+ {
+ if (primes.hasNext())
+ {
+ prime = primes.next();
+ }
+ else
+ {
+ prime = prime.nextProbablePrime();
+ }
+ Future<ModularResultant> task = executor.submit(new ModResultantTask(prime.intValue()));
+ resultantTasks.add(task);
+ pProd = pProd.multiply(prime);
+ }
+
+ // Combine modular resultants to obtain the resultant.
+ // For efficiency, first combine all pairs of small resultants to bigger resultants,
+ // then combine pairs of those, etc. until only one is left.
+ ModularResultant overallResultant = null;
+ while (!resultantTasks.isEmpty())
+ {
+ try
+ {
+ Future<ModularResultant> modRes1 = resultantTasks.take();
+ Future<ModularResultant> modRes2 = resultantTasks.poll();
+ if (modRes2 == null)
+ {
+ // modRes1 is the only one left
+ overallResultant = modRes1.get();
+ break;
+ }
+ Future<ModularResultant> newTask = executor.submit(new CombineTask(modRes1.get(), modRes2.get()));
+ resultantTasks.add(newTask);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalStateException(e.toString());
+ }
+ }
+ executor.shutdown();
+ BigInteger res = overallResultant.res;
+ BigIntPolynomial rhoP = overallResultant.rho;
+
+ BigInteger pProd2 = pProd.divide(BigInteger.valueOf(2));
+ BigInteger pProd2n = pProd2.negate();
+
+ if (res.compareTo(pProd2) > 0)
+ {
+ res = res.subtract(pProd);
+ }
+ if (res.compareTo(pProd2n) < 0)
+ {
+ res = res.add(pProd);
+ }
+
+ for (int i = 0; i < N; i++)
+ {
+ BigInteger c = rhoP.coeffs[i];
+ if (c.compareTo(pProd2) > 0)
+ {
+ rhoP.coeffs[i] = c.subtract(pProd);
+ }
+ if (c.compareTo(pProd2n) < 0)
+ {
+ rhoP.coeffs[i] = c.add(pProd);
+ }
+ }
+
+ return new Resultant(rhoP, res);
+ }
+
+ /**
+ * Resultant of this polynomial with <code>x^n-1 mod p</code>.<br/>
+ *
+ * @return <code>(rho, res)</code> satisfying <code>res = rho*this + t*(x^n-1) mod p</code> for some integer <code>t</code>.
+ */
+ public ModularResultant resultant(int p)
+ {
+ // Add a coefficient as the following operations involve polynomials of degree deg(f)+1
+ int[] fcoeffs = Arrays.copyOf(coeffs, coeffs.length + 1);
+ IntegerPolynomial f = new IntegerPolynomial(fcoeffs);
+ int N = fcoeffs.length;
+
+ IntegerPolynomial a = new IntegerPolynomial(N);
+ a.coeffs[0] = -1;
+ a.coeffs[N - 1] = 1;
+ IntegerPolynomial b = new IntegerPolynomial(f.coeffs);
+ IntegerPolynomial v1 = new IntegerPolynomial(N);
+ IntegerPolynomial v2 = new IntegerPolynomial(N);
+ v2.coeffs[0] = 1;
+ int da = N - 1;
+ int db = b.degree();
+ int ta = da;
+ int c = 0;
+ int r = 1;
+ while (db > 0)
+ {
+ c = Util.invert(b.coeffs[db], p);
+ c = (c * a.coeffs[da]) % p;
+ a.multShiftSub(b, c, da - db, p);
+ v1.multShiftSub(v2, c, da - db, p);
+
+ da = a.degree();
+ if (da < db)
+ {
+ r *= Util.pow(b.coeffs[db], ta - da, p);
+ r %= p;
+ if (ta % 2 == 1 && db % 2 == 1)
+ {
+ r = (-r) % p;
+ }
+ IntegerPolynomial temp = a;
+ a = b;
+ b = temp;
+ int tempdeg = da;
+ da = db;
+ temp = v1;
+ v1 = v2;
+ v2 = temp;
+ ta = db;
+ db = tempdeg;
+ }
+ }
+ r *= Util.pow(b.coeffs[0], da, p);
+ r %= p;
+ c = Util.invert(b.coeffs[0], p);
+ v2.mult(c);
+ v2.mod(p);
+ v2.mult(r);
+ v2.mod(p);
+
+ // drop the highest coefficient so #coeffs matches the original input
+ v2.coeffs = Arrays.copyOf(v2.coeffs, v2.coeffs.length - 1);
+ return new ModularResultant(new BigIntPolynomial(v2), BigInteger.valueOf(r), BigInteger.valueOf(p));
+ }
+
+ /**
+ * Computes <code>this-b*c*(x^k) mod p</code> and stores the result in this polynomial.<br/>
+ * See steps 4a,4b in EESS algorithm 2.2.7.1.
+ *
+ * @param b
+ * @param c
+ * @param k
+ * @param p
+ */
+ private void multShiftSub(IntegerPolynomial b, int c, int k, int p)
+ {
+ int N = coeffs.length;
+ for (int i = k; i < N; i++)
+ {
+ coeffs[i] = (coeffs[i] - b.coeffs[i - k] * c) % p;
+ }
+ }
+
+ /**
+ * Adds the squares of all coefficients.
+ *
+ * @return the sum of squares
+ */
+ private BigInteger squareSum()
+ {
+ BigInteger sum = Constants.BIGINT_ZERO;
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ sum = sum.add(BigInteger.valueOf(coeffs[i] * coeffs[i]));
+ }
+ return sum;
+ }
+
+ /**
+ * Returns the degree of the polynomial
+ *
+ * @return the degree
+ */
+ int degree()
+ {
+ int degree = coeffs.length - 1;
+ while (degree > 0 && coeffs[degree] == 0)
+ {
+ degree--;
+ }
+ return degree;
+ }
+
+ /**
+ * Adds another polynomial which can have a different number of coefficients,
+ * and takes the coefficient values mod <code>modulus</code>.
+ *
+ * @param b another polynomial
+ */
+ public void add(IntegerPolynomial b, int modulus)
+ {
+ add(b);
+ mod(modulus);
+ }
+
+ /**
+ * Adds another polynomial which can have a different number of coefficients.
+ *
+ * @param b another polynomial
+ */
+ public void add(IntegerPolynomial b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ coeffs = Arrays.copyOf(coeffs, b.coeffs.length);
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] += b.coeffs[i];
+ }
+ }
+
+ /**
+ * Subtracts another polynomial which can have a different number of coefficients,
+ * and takes the coefficient values mod <code>modulus</code>.
+ *
+ * @param b another polynomial
+ */
+ public void sub(IntegerPolynomial b, int modulus)
+ {
+ sub(b);
+ mod(modulus);
+ }
+
+ /**
+ * Subtracts another polynomial which can have a different number of coefficients.
+ *
+ * @param b another polynomial
+ */
+ public void sub(IntegerPolynomial b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ coeffs = Arrays.copyOf(coeffs, b.coeffs.length);
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] -= b.coeffs[i];
+ }
+ }
+
+ /**
+ * Subtracts a <code>int</code> from each coefficient. Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param b
+ */
+ void sub(int b)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] -= b;
+ }
+ }
+
+ /**
+ * Multiplies each coefficient by a <code>int</code>. Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param factor
+ */
+ public void mult(int factor)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] *= factor;
+ }
+ }
+
+ /**
+ * Multiplies each coefficient by a 2 and applies a modulus. Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param modulus a modulus
+ */
+ private void mult2(int modulus)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] *= 2;
+ coeffs[i] %= modulus;
+ }
+ }
+
+ /**
+ * Multiplies each coefficient by a 2 and applies a modulus. Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param modulus a modulus
+ */
+ public void mult3(int modulus)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] *= 3;
+ coeffs[i] %= modulus;
+ }
+ }
+
+ /**
+ * Divides each coefficient by <code>k</code> and rounds to the nearest integer. Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param k the divisor
+ */
+ public void div(int k)
+ {
+ int k2 = (k + 1) / 2;
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] += coeffs[i] > 0 ? k2 : -k2;
+ coeffs[i] /= k;
+ }
+ }
+
+ /**
+ * Takes each coefficient modulo 3 such that all coefficients are ternary.
+ */
+ public void mod3()
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] %= 3;
+ if (coeffs[i] > 1)
+ {
+ coeffs[i] -= 3;
+ }
+ if (coeffs[i] < -1)
+ {
+ coeffs[i] += 3;
+ }
+ }
+ }
+
+ /**
+ * Ensures all coefficients are between 0 and <code>modulus-1</code>
+ *
+ * @param modulus a modulus
+ */
+ public void modPositive(int modulus)
+ {
+ mod(modulus);
+ ensurePositive(modulus);
+ }
+
+ /**
+ * Reduces all coefficients to the interval [-modulus/2, modulus/2)
+ */
+ void modCenter(int modulus)
+ {
+ mod(modulus);
+ for (int j = 0; j < coeffs.length; j++)
+ {
+ while (coeffs[j] < modulus / 2)
+ {
+ coeffs[j] += modulus;
+ }
+ while (coeffs[j] >= modulus / 2)
+ {
+ coeffs[j] -= modulus;
+ }
+ }
+ }
+
+ /**
+ * Takes each coefficient modulo <code>modulus</code>.
+ */
+ public void mod(int modulus)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] %= modulus;
+ }
+ }
+
+ /**
+ * Adds <code>modulus</code> until all coefficients are above 0.
+ *
+ * @param modulus a modulus
+ */
+ public void ensurePositive(int modulus)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ while (coeffs[i] < 0)
+ {
+ coeffs[i] += modulus;
+ }
+ }
+ }
+
+ /**
+ * Computes the centered euclidean norm of the polynomial.
+ *
+ * @param q a modulus
+ * @return the centered norm
+ */
+ public long centeredNormSq(int q)
+ {
+ int N = coeffs.length;
+ IntegerPolynomial p = (IntegerPolynomial)clone();
+ p.shiftGap(q);
+
+ long sum = 0;
+ long sqSum = 0;
+ for (int i = 0; i != p.coeffs.length; i++)
+ {
+ int c = p.coeffs[i];
+ sum += c;
+ sqSum += c * c;
+ }
+
+ long centeredNormSq = sqSum - sum * sum / N;
+ return centeredNormSq;
+ }
+
+ /**
+ * Shifts all coefficients so the largest gap is centered around <code>-q/2</code>.
+ *
+ * @param q a modulus
+ */
+ void shiftGap(int q)
+ {
+ modCenter(q);
+
+ int[] sorted = Arrays.clone(coeffs);
+
+ sort(sorted);
+
+ int maxrange = 0;
+ int maxrangeStart = 0;
+ for (int i = 0; i < sorted.length - 1; i++)
+ {
+ int range = sorted[i + 1] - sorted[i];
+ if (range > maxrange)
+ {
+ maxrange = range;
+ maxrangeStart = sorted[i];
+ }
+ }
+
+ int pmin = sorted[0];
+ int pmax = sorted[sorted.length - 1];
+
+ int j = q - pmax + pmin;
+ int shift;
+ if (j > maxrange)
+ {
+ shift = (pmax + pmin) / 2;
+ }
+ else
+ {
+ shift = maxrangeStart + maxrange / 2 + q / 2;
+ }
+
+ sub(shift);
+ }
+
+ private void sort(int[] ints)
+ {
+ boolean swap = true;
+
+ while (swap)
+ {
+ swap = false;
+ for (int i = 0; i != ints.length - 1; i++)
+ {
+ if (ints[i] > ints[i+1])
+ {
+ int tmp = ints[i];
+ ints[i] = ints[i+1];
+ ints[i+1] = tmp;
+ swap = true;
+ }
+ }
+ }
+ }
+
+ /**
+ * Shifts the values of all coefficients to the interval <code>[-q/2, q/2]</code>.
+ *
+ * @param q a modulus
+ */
+ public void center0(int q)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ while (coeffs[i] < -q / 2)
+ {
+ coeffs[i] += q;
+ }
+ while (coeffs[i] > q / 2)
+ {
+ coeffs[i] -= q;
+ }
+ }
+ }
+
+ /**
+ * Returns the sum of all coefficients, i.e. evaluates the polynomial at 0.
+ *
+ * @return the sum of all coefficients
+ */
+ public int sumCoeffs()
+ {
+ int sum = 0;
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ sum += coeffs[i];
+ }
+ return sum;
+ }
+
+ /**
+ * Tests if <code>p(x) = 0</code>.
+ *
+ * @return true iff all coefficients are zeros
+ */
+ private boolean equalsZero()
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ if (coeffs[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Tests if <code>p(x) = 1</code>.
+ *
+ * @return true iff all coefficients are equal to zero, except for the lowest coefficient which must equal 1
+ */
+ public boolean equalsOne()
+ {
+ for (int i = 1; i < coeffs.length; i++)
+ {
+ if (coeffs[i] != 0)
+ {
+ return false;
+ }
+ }
+ return coeffs[0] == 1;
+ }
+
+ /**
+ * Tests if <code>|p(x)| = 1</code>.
+ *
+ * @return true iff all coefficients are equal to zero, except for the lowest coefficient which must equal 1 or -1
+ */
+ private boolean equalsAbsOne()
+ {
+ for (int i = 1; i < coeffs.length; i++)
+ {
+ if (coeffs[i] != 0)
+ {
+ return false;
+ }
+ }
+ return Math.abs(coeffs[0]) == 1;
+ }
+
+ /**
+ * Counts the number of coefficients equal to an integer
+ *
+ * @param value an integer
+ * @return the number of coefficients equal to <code>value</code>
+ */
+ public int count(int value)
+ {
+ int count = 0;
+ for (int i = 0; i != coeffs.length; i++)
+ {
+ if (coeffs[i] == value)
+ {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Multiplication by <code>X</code> in <code>Z[X]/Z[X^n-1]</code>.
+ */
+ public void rotate1()
+ {
+ int clast = coeffs[coeffs.length - 1];
+ for (int i = coeffs.length - 1; i > 0; i--)
+ {
+ coeffs[i] = coeffs[i - 1];
+ }
+ coeffs[0] = clast;
+ }
+
+ public void clear()
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] = 0;
+ }
+ }
+
+ public IntegerPolynomial toIntegerPolynomial()
+ {
+ return (IntegerPolynomial)clone();
+ }
+
+ public Object clone()
+ {
+ return new IntegerPolynomial(coeffs.clone());
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (obj instanceof IntegerPolynomial)
+ {
+ return Arrays.areEqual(coeffs, ((IntegerPolynomial)obj).coeffs);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Calls {@link IntegerPolynomial#resultant(int)
+ */
+ private class ModResultantTask
+ implements Callable<ModularResultant>
+ {
+ private int modulus;
+
+ private ModResultantTask(int modulus)
+ {
+ this.modulus = modulus;
+ }
+
+ public ModularResultant call()
+ {
+ return resultant(modulus);
+ }
+ }
+
+ /**
+ * Calls {@link ModularResultant#combineRho(ModularResultant, ModularResultant)
+ */
+ private class CombineTask
+ implements Callable<ModularResultant>
+ {
+ private ModularResultant modRes1;
+ private ModularResultant modRes2;
+
+ private CombineTask(ModularResultant modRes1, ModularResultant modRes2)
+ {
+ this.modRes1 = modRes1;
+ this.modRes2 = modRes2;
+ }
+
+ public ModularResultant call()
+ {
+ return ModularResultant.combineRho(modRes1, modRes2);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/LongPolynomial2.java b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/LongPolynomial2.java
new file mode 100644
index 00000000..c7ae56c3
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/LongPolynomial2.java
@@ -0,0 +1,255 @@
+package org.bouncycastle.pqc.math.ntru.polynomial;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A polynomial class that combines two coefficients into one <code>long</code> value for
+ * faster multiplication in 64 bit environments.<br/>
+ * Coefficients can be between 0 and 2047 and are stored in pairs in the bits 0..10 and 24..34 of a <code>long</code> number.
+ */
+public class LongPolynomial2
+{
+ private long[] coeffs; // each representing two coefficients in the original IntegerPolynomial
+ private int numCoeffs;
+
+ /**
+ * Constructs a <code>LongPolynomial2</code> from a <code>IntegerPolynomial</code>. The two polynomials are independent of each other.
+ *
+ * @param p the original polynomial. Coefficients must be between 0 and 2047.
+ */
+ public LongPolynomial2(IntegerPolynomial p)
+ {
+ numCoeffs = p.coeffs.length;
+ coeffs = new long[(numCoeffs + 1) / 2];
+ int idx = 0;
+ for (int pIdx = 0; pIdx < numCoeffs; )
+ {
+ int c0 = p.coeffs[pIdx++];
+ while (c0 < 0)
+ {
+ c0 += 2048;
+ }
+ long c1 = pIdx < numCoeffs ? p.coeffs[pIdx++] : 0;
+ while (c1 < 0)
+ {
+ c1 += 2048;
+ }
+ coeffs[idx] = c0 + (c1 << 24);
+ idx++;
+ }
+ }
+
+ private LongPolynomial2(long[] coeffs)
+ {
+ this.coeffs = coeffs;
+ }
+
+ private LongPolynomial2(int N)
+ {
+ coeffs = new long[N];
+ }
+
+ /**
+ * Multiplies the polynomial with another, taking the indices mod N and the values mod 2048.
+ */
+ public LongPolynomial2 mult(LongPolynomial2 poly2)
+ {
+ int N = coeffs.length;
+ if (poly2.coeffs.length != N || numCoeffs != poly2.numCoeffs)
+ {
+ throw new IllegalArgumentException("Number of coefficients must be the same");
+ }
+
+ LongPolynomial2 c = multRecursive(poly2);
+
+ if (c.coeffs.length > N)
+ {
+ if (numCoeffs % 2 == 0)
+ {
+ for (int k = N; k < c.coeffs.length; k++)
+ {
+ c.coeffs[k - N] = (c.coeffs[k - N] + c.coeffs[k]) & 0x7FF0007FFL;
+ }
+ c.coeffs = Arrays.copyOf(c.coeffs, N);
+ }
+ else
+ {
+ for (int k = N; k < c.coeffs.length; k++)
+ {
+ c.coeffs[k - N] = c.coeffs[k - N] + (c.coeffs[k - 1] >> 24);
+ c.coeffs[k - N] = c.coeffs[k - N] + ((c.coeffs[k] & 2047) << 24);
+ c.coeffs[k - N] &= 0x7FF0007FFL;
+ }
+ c.coeffs = Arrays.copyOf(c.coeffs, N);
+ c.coeffs[c.coeffs.length - 1] &= 2047;
+ }
+ }
+
+ c = new LongPolynomial2(c.coeffs);
+ c.numCoeffs = numCoeffs;
+ return c;
+ }
+
+ public IntegerPolynomial toIntegerPolynomial()
+ {
+ int[] intCoeffs = new int[numCoeffs];
+ int uIdx = 0;
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ intCoeffs[uIdx++] = (int)(coeffs[i] & 2047);
+ if (uIdx < numCoeffs)
+ {
+ intCoeffs[uIdx++] = (int)((coeffs[i] >> 24) & 2047);
+ }
+ }
+ return new IntegerPolynomial(intCoeffs);
+ }
+
+ /**
+ * Karazuba multiplication
+ */
+ private LongPolynomial2 multRecursive(LongPolynomial2 poly2)
+ {
+ long[] a = coeffs;
+ long[] b = poly2.coeffs;
+
+ int n = poly2.coeffs.length;
+ if (n <= 32)
+ {
+ int cn = 2 * n;
+ LongPolynomial2 c = new LongPolynomial2(new long[cn]);
+ for (int k = 0; k < cn; k++)
+ {
+ for (int i = Math.max(0, k - n + 1); i <= Math.min(k, n - 1); i++)
+ {
+ long c0 = a[k - i] * b[i];
+ long cu = c0 & 0x7FF000000L + (c0 & 2047);
+ long co = (c0 >>> 48) & 2047;
+
+ c.coeffs[k] = (c.coeffs[k] + cu) & 0x7FF0007FFL;
+ c.coeffs[k + 1] = (c.coeffs[k + 1] + co) & 0x7FF0007FFL;
+ }
+ }
+ return c;
+ }
+ else
+ {
+ int n1 = n / 2;
+
+ LongPolynomial2 a1 = new LongPolynomial2(Arrays.copyOf(a, n1));
+ LongPolynomial2 a2 = new LongPolynomial2(Arrays.copyOfRange(a, n1, n));
+ LongPolynomial2 b1 = new LongPolynomial2(Arrays.copyOf(b, n1));
+ LongPolynomial2 b2 = new LongPolynomial2(Arrays.copyOfRange(b, n1, n));
+
+ LongPolynomial2 A = (LongPolynomial2)a1.clone();
+ A.add(a2);
+ LongPolynomial2 B = (LongPolynomial2)b1.clone();
+ B.add(b2);
+
+ LongPolynomial2 c1 = a1.multRecursive(b1);
+ LongPolynomial2 c2 = a2.multRecursive(b2);
+ LongPolynomial2 c3 = A.multRecursive(B);
+ c3.sub(c1);
+ c3.sub(c2);
+
+ LongPolynomial2 c = new LongPolynomial2(2 * n);
+ for (int i = 0; i < c1.coeffs.length; i++)
+ {
+ c.coeffs[i] = c1.coeffs[i] & 0x7FF0007FFL;
+ }
+ for (int i = 0; i < c3.coeffs.length; i++)
+ {
+ c.coeffs[n1 + i] = (c.coeffs[n1 + i] + c3.coeffs[i]) & 0x7FF0007FFL;
+ }
+ for (int i = 0; i < c2.coeffs.length; i++)
+ {
+ c.coeffs[2 * n1 + i] = (c.coeffs[2 * n1 + i] + c2.coeffs[i]) & 0x7FF0007FFL;
+ }
+ return c;
+ }
+ }
+
+ /**
+ * Adds another polynomial which can have a different number of coefficients.
+ *
+ * @param b another polynomial
+ */
+ private void add(LongPolynomial2 b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ coeffs = Arrays.copyOf(coeffs, b.coeffs.length);
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] = (coeffs[i] + b.coeffs[i]) & 0x7FF0007FFL;
+ }
+ }
+
+ /**
+ * Subtracts another polynomial which can have a different number of coefficients.
+ *
+ * @param b another polynomial
+ */
+ private void sub(LongPolynomial2 b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ coeffs = Arrays.copyOf(coeffs, b.coeffs.length);
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] = (0x0800000800000L + coeffs[i] - b.coeffs[i]) & 0x7FF0007FFL;
+ }
+ }
+
+ /**
+ * Subtracts another polynomial which must have the same number of coefficients,
+ * and applies an AND mask to the upper and lower halves of each coefficients.
+ *
+ * @param b another polynomial
+ * @param mask a bit mask less than 2048 to apply to each 11-bit coefficient
+ */
+ public void subAnd(LongPolynomial2 b, int mask)
+ {
+ long longMask = (((long)mask) << 24) + mask;
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] = (0x0800000800000L + coeffs[i] - b.coeffs[i]) & longMask;
+ }
+ }
+
+ /**
+ * Multiplies this polynomial by 2 and applies an AND mask to the upper and
+ * lower halves of each coefficients.
+ *
+ * @param mask a bit mask less than 2048 to apply to each 11-bit coefficient
+ */
+ public void mult2And(int mask)
+ {
+ long longMask = (((long)mask) << 24) + mask;
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] = (coeffs[i] << 1) & longMask;
+ }
+ }
+
+ public Object clone()
+ {
+ LongPolynomial2 p = new LongPolynomial2(coeffs.clone());
+ p.numCoeffs = numCoeffs;
+ return p;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (obj instanceof LongPolynomial2)
+ {
+ return Arrays.areEqual(coeffs, ((LongPolynomial2)obj).coeffs);
+ }
+ else
+ {
+ return false;
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/LongPolynomial5.java b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/LongPolynomial5.java
new file mode 100644
index 00000000..69801e90
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/LongPolynomial5.java
@@ -0,0 +1,149 @@
+package org.bouncycastle.pqc.math.ntru.polynomial;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A polynomial class that combines five coefficients into one <code>long</code> value for
+ * faster multiplication by a ternary polynomial.<br/>
+ * Coefficients can be between 0 and 2047 and are stored in bits 0..11, 12..23, ..., 48..59 of a <code>long</code> number.
+ */
+public class LongPolynomial5
+{
+ private long[] coeffs; // groups of 5 coefficients
+ private int numCoeffs;
+
+ /**
+ * Constructs a <code>LongPolynomial5</code> from a <code>IntegerPolynomial</code>. The two polynomials are independent of each other.
+ *
+ * @param p the original polynomial. Coefficients must be between 0 and 2047.
+ */
+ public LongPolynomial5(IntegerPolynomial p)
+ {
+ numCoeffs = p.coeffs.length;
+
+ coeffs = new long[(numCoeffs + 4) / 5];
+ int cIdx = 0;
+ int shift = 0;
+ for (int i = 0; i < numCoeffs; i++)
+ {
+ coeffs[cIdx] |= ((long)p.coeffs[i]) << shift;
+ shift += 12;
+ if (shift >= 60)
+ {
+ shift = 0;
+ cIdx++;
+ }
+ }
+ }
+
+ private LongPolynomial5(long[] coeffs, int numCoeffs)
+ {
+ this.coeffs = coeffs;
+ this.numCoeffs = numCoeffs;
+ }
+
+ /**
+ * Multiplies the polynomial with a <code>TernaryPolynomial</code>, taking the indices mod N and the values mod 2048.
+ */
+ public LongPolynomial5 mult(TernaryPolynomial poly2)
+ {
+ long[][] prod = new long[5][coeffs.length + (poly2.size() + 4) / 5 - 1]; // intermediate results, the subarrays are shifted by 0,...,4 coefficients
+
+ // multiply ones
+ int[] ones = poly2.getOnes();
+ for (int idx = 0; idx != ones.length; idx++)
+ {
+ int pIdx = ones[idx];
+ int cIdx = pIdx / 5;
+ int m = pIdx - cIdx * 5; // m = pIdx % 5
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ prod[m][cIdx] = (prod[m][cIdx] + coeffs[i]) & 0x7FF7FF7FF7FF7FFL;
+ cIdx++;
+ }
+ }
+
+ // multiply negative ones
+ int[] negOnes = poly2.getNegOnes();
+ for (int idx = 0; idx != negOnes.length; idx++)
+ {
+ int pIdx = negOnes[idx];
+ int cIdx = pIdx / 5;
+ int m = pIdx - cIdx * 5; // m = pIdx % 5
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ prod[m][cIdx] = (0x800800800800800L + prod[m][cIdx] - coeffs[i]) & 0x7FF7FF7FF7FF7FFL;
+ cIdx++;
+ }
+ }
+
+ // combine shifted coefficients (5 arrays) into a single array of length prod[*].length+1
+ long[] cCoeffs = Arrays.copyOf(prod[0], prod[0].length + 1);
+ for (int m = 1; m <= 4; m++)
+ {
+ int shift = m * 12;
+ int shift60 = 60 - shift;
+ long mask = (1L << shift60) - 1;
+ int pLen = prod[m].length;
+ for (int i = 0; i < pLen; i++)
+ {
+ long upper, lower;
+ upper = prod[m][i] >> shift60;
+ lower = prod[m][i] & mask;
+
+ cCoeffs[i] = (cCoeffs[i] + (lower << shift)) & 0x7FF7FF7FF7FF7FFL;
+ int nextIdx = i + 1;
+ cCoeffs[nextIdx] = (cCoeffs[nextIdx] + upper) & 0x7FF7FF7FF7FF7FFL;
+ }
+ }
+
+ // reduce indices of cCoeffs modulo numCoeffs
+ int shift = 12 * (numCoeffs % 5);
+ for (int cIdx = coeffs.length - 1; cIdx < cCoeffs.length; cIdx++)
+ {
+ long iCoeff; // coefficient to shift into the [0..numCoeffs-1] range
+ int newIdx;
+ if (cIdx == coeffs.length - 1)
+ {
+ iCoeff = numCoeffs == 5 ? 0 : cCoeffs[cIdx] >> shift;
+ newIdx = 0;
+ }
+ else
+ {
+ iCoeff = cCoeffs[cIdx];
+ newIdx = cIdx * 5 - numCoeffs;
+ }
+
+ int base = newIdx / 5;
+ int m = newIdx - base * 5; // m = newIdx % 5
+ long lower = iCoeff << (12 * m);
+ long upper = iCoeff >> (12 * (5 - m));
+ cCoeffs[base] = (cCoeffs[base] + lower) & 0x7FF7FF7FF7FF7FFL;
+ int base1 = base + 1;
+ if (base1 < coeffs.length)
+ {
+ cCoeffs[base1] = (cCoeffs[base1] + upper) & 0x7FF7FF7FF7FF7FFL;
+ }
+ }
+
+ return new LongPolynomial5(cCoeffs, numCoeffs);
+ }
+
+ public IntegerPolynomial toIntegerPolynomial()
+ {
+ int[] intCoeffs = new int[numCoeffs];
+ int cIdx = 0;
+ int shift = 0;
+ for (int i = 0; i < numCoeffs; i++)
+ {
+ intCoeffs[i] = (int)((coeffs[cIdx] >> shift) & 2047);
+ shift += 12;
+ if (shift >= 60)
+ {
+ shift = 0;
+ cIdx++;
+ }
+ }
+ return new IntegerPolynomial(intCoeffs);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/ModularResultant.java b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/ModularResultant.java
new file mode 100644
index 00000000..5f77192d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/ModularResultant.java
@@ -0,0 +1,46 @@
+package org.bouncycastle.pqc.math.ntru.polynomial;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.pqc.math.ntru.euclid.BigIntEuclidean;
+
+/**
+ * A resultant modulo a <code>BigInteger</code>
+ */
+public class ModularResultant
+ extends Resultant
+{
+ BigInteger modulus;
+
+ ModularResultant(BigIntPolynomial rho, BigInteger res, BigInteger modulus)
+ {
+ super(rho, res);
+ this.modulus = modulus;
+ }
+
+ /**
+ * Calculates a <code>rho</code> modulo <code>m1*m2</code> from
+ * two resultants whose <code>rho</code>s are modulo <code>m1</code> and <code>m2</code>.<br/>
+ * </code>res</code> is set to <code>null</code>.
+ *
+ * @param modRes1
+ * @param modRes2
+ * @return <code>rho</code> modulo <code>modRes1.modulus * modRes2.modulus</code>, and <code>null</code> for </code>res</code>.
+ */
+ static ModularResultant combineRho(ModularResultant modRes1, ModularResultant modRes2)
+ {
+ BigInteger mod1 = modRes1.modulus;
+ BigInteger mod2 = modRes2.modulus;
+ BigInteger prod = mod1.multiply(mod2);
+ BigIntEuclidean er = BigIntEuclidean.calculate(mod2, mod1);
+
+ BigIntPolynomial rho1 = (BigIntPolynomial)modRes1.rho.clone();
+ rho1.mult(er.x.multiply(mod2));
+ BigIntPolynomial rho2 = (BigIntPolynomial)modRes2.rho.clone();
+ rho2.mult(er.y.multiply(mod1));
+ rho1.add(rho2);
+ rho1.mod(prod);
+
+ return new ModularResultant(rho1, null, prod);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/Polynomial.java b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/Polynomial.java
new file mode 100644
index 00000000..7a7237c2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/Polynomial.java
@@ -0,0 +1,42 @@
+package org.bouncycastle.pqc.math.ntru.polynomial;
+
+public interface Polynomial
+{
+
+ /**
+ * Multiplies the polynomial by an <code>IntegerPolynomial</code>,
+ * taking the indices mod <code>N</code>.
+ *
+ * @param poly2 a polynomial
+ * @return the product of the two polynomials
+ */
+ IntegerPolynomial mult(IntegerPolynomial poly2);
+
+ /**
+ * Multiplies the polynomial by an <code>IntegerPolynomial</code>,
+ * taking the coefficient values mod <code>modulus</code> and the indices mod <code>N</code>.
+ *
+ * @param poly2 a polynomial
+ * @param modulus a modulus to apply
+ * @return the product of the two polynomials
+ */
+ IntegerPolynomial mult(IntegerPolynomial poly2, int modulus);
+
+ /**
+ * Returns a polynomial that is equal to this polynomial (in the sense that {@link #mult(IntegerPolynomial, int)}
+ * returns equal <code>IntegerPolynomial</code>s). The new polynomial is guaranteed to be independent of the original.
+ *
+ * @return a new <code>IntegerPolynomial</code>.
+ */
+ IntegerPolynomial toIntegerPolynomial();
+
+ /**
+ * Multiplies the polynomial by a <code>BigIntPolynomial</code>, taking the indices mod N. Does not
+ * change this polynomial but returns the result as a new polynomial.<br/>
+ * Both polynomials must have the same number of coefficients.
+ *
+ * @param poly2 the polynomial to multiply by
+ * @return a new polynomial
+ */
+ BigIntPolynomial mult(BigIntPolynomial poly2);
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/ProductFormPolynomial.java b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/ProductFormPolynomial.java
new file mode 100644
index 00000000..dd18902e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/ProductFormPolynomial.java
@@ -0,0 +1,153 @@
+package org.bouncycastle.pqc.math.ntru.polynomial;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.SecureRandom;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A polynomial of the form <code>f1*f2+f3</code>, where
+ * <code>f1,f2,f3</code> are very sparsely populated ternary polynomials.
+ */
+public class ProductFormPolynomial
+ implements Polynomial
+{
+ private SparseTernaryPolynomial f1, f2, f3;
+
+ public ProductFormPolynomial(SparseTernaryPolynomial f1, SparseTernaryPolynomial f2, SparseTernaryPolynomial f3)
+ {
+ this.f1 = f1;
+ this.f2 = f2;
+ this.f3 = f3;
+ }
+
+ public static ProductFormPolynomial generateRandom(int N, int df1, int df2, int df3Ones, int df3NegOnes, SecureRandom random)
+ {
+ SparseTernaryPolynomial f1 = SparseTernaryPolynomial.generateRandom(N, df1, df1, random);
+ SparseTernaryPolynomial f2 = SparseTernaryPolynomial.generateRandom(N, df2, df2, random);
+ SparseTernaryPolynomial f3 = SparseTernaryPolynomial.generateRandom(N, df3Ones, df3NegOnes, random);
+ return new ProductFormPolynomial(f1, f2, f3);
+ }
+
+ public static ProductFormPolynomial fromBinary(byte[] data, int N, int df1, int df2, int df3Ones, int df3NegOnes)
+ throws IOException
+ {
+ return fromBinary(new ByteArrayInputStream(data), N, df1, df2, df3Ones, df3NegOnes);
+ }
+
+ public static ProductFormPolynomial fromBinary(InputStream is, int N, int df1, int df2, int df3Ones, int df3NegOnes)
+ throws IOException
+ {
+ SparseTernaryPolynomial f1;
+
+ f1 = SparseTernaryPolynomial.fromBinary(is, N, df1, df1);
+ SparseTernaryPolynomial f2 = SparseTernaryPolynomial.fromBinary(is, N, df2, df2);
+ SparseTernaryPolynomial f3 = SparseTernaryPolynomial.fromBinary(is, N, df3Ones, df3NegOnes);
+ return new ProductFormPolynomial(f1, f2, f3);
+ }
+
+ public byte[] toBinary()
+ {
+ byte[] f1Bin = f1.toBinary();
+ byte[] f2Bin = f2.toBinary();
+ byte[] f3Bin = f3.toBinary();
+
+ byte[] all = Arrays.copyOf(f1Bin, f1Bin.length + f2Bin.length + f3Bin.length);
+ System.arraycopy(f2Bin, 0, all, f1Bin.length, f2Bin.length);
+ System.arraycopy(f3Bin, 0, all, f1Bin.length + f2Bin.length, f3Bin.length);
+ return all;
+ }
+
+ public IntegerPolynomial mult(IntegerPolynomial b)
+ {
+ IntegerPolynomial c = f1.mult(b);
+ c = f2.mult(c);
+ c.add(f3.mult(b));
+ return c;
+ }
+
+ public BigIntPolynomial mult(BigIntPolynomial b)
+ {
+ BigIntPolynomial c = f1.mult(b);
+ c = f2.mult(c);
+ c.add(f3.mult(b));
+ return c;
+ }
+
+ public IntegerPolynomial toIntegerPolynomial()
+ {
+ IntegerPolynomial i = f1.mult(f2.toIntegerPolynomial());
+ i.add(f3.toIntegerPolynomial());
+ return i;
+ }
+
+ public IntegerPolynomial mult(IntegerPolynomial poly2, int modulus)
+ {
+ IntegerPolynomial c = mult(poly2);
+ c.mod(modulus);
+ return c;
+ }
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((f1 == null) ? 0 : f1.hashCode());
+ result = prime * result + ((f2 == null) ? 0 : f2.hashCode());
+ result = prime * result + ((f3 == null) ? 0 : f3.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ ProductFormPolynomial other = (ProductFormPolynomial)obj;
+ if (f1 == null)
+ {
+ if (other.f1 != null)
+ {
+ return false;
+ }
+ }
+ else if (!f1.equals(other.f1))
+ {
+ return false;
+ }
+ if (f2 == null)
+ {
+ if (other.f2 != null)
+ {
+ return false;
+ }
+ }
+ else if (!f2.equals(other.f2))
+ {
+ return false;
+ }
+ if (f3 == null)
+ {
+ if (other.f3 != null)
+ {
+ return false;
+ }
+ }
+ else if (!f3.equals(other.f3))
+ {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/Resultant.java b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/Resultant.java
new file mode 100644
index 00000000..ec585779
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/Resultant.java
@@ -0,0 +1,28 @@
+package org.bouncycastle.pqc.math.ntru.polynomial;
+
+import java.math.BigInteger;
+
+/**
+ * Contains a resultant and a polynomial <code>rho</code> such that
+ * <code>res = rho*this + t*(x^n-1) for some integer t</code>.
+ *
+ * @see IntegerPolynomial#resultant()
+ * @see IntegerPolynomial#resultant(int)
+ */
+public class Resultant
+{
+ /**
+ * A polynomial such that <code>res = rho*this + t*(x^n-1) for some integer t</code>
+ */
+ public BigIntPolynomial rho;
+ /**
+ * Resultant of a polynomial with <code>x^n-1</code>
+ */
+ public BigInteger res;
+
+ Resultant(BigIntPolynomial rho, BigInteger res)
+ {
+ this.rho = rho;
+ this.res = res;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/SparseTernaryPolynomial.java b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/SparseTernaryPolynomial.java
new file mode 100644
index 00000000..3c913397
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/SparseTernaryPolynomial.java
@@ -0,0 +1,320 @@
+package org.bouncycastle.pqc.math.ntru.polynomial;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.pqc.math.ntru.util.ArrayEncoder;
+import org.bouncycastle.pqc.math.ntru.util.Util;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A <code>TernaryPolynomial</code> with a "low" number of nonzero coefficients.
+ */
+public class SparseTernaryPolynomial
+ implements TernaryPolynomial
+{
+ /**
+ * Number of bits to use for each coefficient. Determines the upper bound for <code>N</code>.
+ */
+ private static final int BITS_PER_INDEX = 11;
+
+ private int N;
+ private int[] ones;
+ private int[] negOnes;
+
+ /**
+ * Constructs a new polynomial.
+ *
+ * @param N total number of coefficients including zeros
+ * @param ones indices of coefficients equal to 1
+ * @param negOnes indices of coefficients equal to -1
+ */
+ SparseTernaryPolynomial(int N, int[] ones, int[] negOnes)
+ {
+ this.N = N;
+ this.ones = ones;
+ this.negOnes = negOnes;
+ }
+
+ /**
+ * Constructs a <code>DenseTernaryPolynomial</code> from a <code>IntegerPolynomial</code>. The two polynomials are
+ * independent of each other.
+ *
+ * @param intPoly the original polynomial
+ */
+ public SparseTernaryPolynomial(IntegerPolynomial intPoly)
+ {
+ this(intPoly.coeffs);
+ }
+
+ /**
+ * Constructs a new <code>SparseTernaryPolynomial</code> with a given set of coefficients.
+ *
+ * @param coeffs the coefficients
+ */
+ public SparseTernaryPolynomial(int[] coeffs)
+ {
+ N = coeffs.length;
+ ones = new int[N];
+ negOnes = new int[N];
+ int onesIdx = 0;
+ int negOnesIdx = 0;
+ for (int i = 0; i < N; i++)
+ {
+ int c = coeffs[i];
+ switch (c)
+ {
+ case 1:
+ ones[onesIdx++] = i;
+ break;
+ case -1:
+ negOnes[negOnesIdx++] = i;
+ break;
+ case 0:
+ break;
+ default:
+ throw new IllegalArgumentException("Illegal value: " + c + ", must be one of {-1, 0, 1}");
+ }
+ }
+ ones = Arrays.copyOf(ones, onesIdx);
+ negOnes = Arrays.copyOf(negOnes, negOnesIdx);
+ }
+
+ /**
+ * Decodes a byte array encoded with {@link #toBinary()} to a ploynomial.
+ *
+ * @param is an input stream containing an encoded polynomial
+ * @param N number of coefficients including zeros
+ * @param numOnes number of coefficients equal to 1
+ * @param numNegOnes number of coefficients equal to -1
+ * @return the decoded polynomial
+ * @throws IOException
+ */
+ public static SparseTernaryPolynomial fromBinary(InputStream is, int N, int numOnes, int numNegOnes)
+ throws IOException
+ {
+ int maxIndex = 1 << BITS_PER_INDEX;
+ int bitsPerIndex = 32 - Integer.numberOfLeadingZeros(maxIndex - 1);
+
+ int data1Len = (numOnes * bitsPerIndex + 7) / 8;
+ byte[] data1 = Util.readFullLength(is, data1Len);
+ int[] ones = ArrayEncoder.decodeModQ(data1, numOnes, maxIndex);
+
+ int data2Len = (numNegOnes * bitsPerIndex + 7) / 8;
+ byte[] data2 = Util.readFullLength(is, data2Len);
+ int[] negOnes = ArrayEncoder.decodeModQ(data2, numNegOnes, maxIndex);
+
+ return new SparseTernaryPolynomial(N, ones, negOnes);
+ }
+
+ /**
+ * Generates a random polynomial with <code>numOnes</code> coefficients equal to 1,
+ * <code>numNegOnes</code> coefficients equal to -1, and the rest equal to 0.
+ *
+ * @param N number of coefficients
+ * @param numOnes number of 1's
+ * @param numNegOnes number of -1's
+ */
+ public static SparseTernaryPolynomial generateRandom(int N, int numOnes, int numNegOnes, SecureRandom random)
+ {
+ int[] coeffs = Util.generateRandomTernary(N, numOnes, numNegOnes, random);
+ return new SparseTernaryPolynomial(coeffs);
+ }
+
+ public IntegerPolynomial mult(IntegerPolynomial poly2)
+ {
+ int[] b = poly2.coeffs;
+ if (b.length != N)
+ {
+ throw new IllegalArgumentException("Number of coefficients must be the same");
+ }
+
+ int[] c = new int[N];
+ for (int idx = 0; idx != ones.length; idx++)
+ {
+ int i = ones[idx];
+ int j = N - 1 - i;
+ for (int k = N - 1; k >= 0; k--)
+ {
+ c[k] += b[j];
+ j--;
+ if (j < 0)
+ {
+ j = N - 1;
+ }
+ }
+ }
+
+ for (int idx = 0; idx != negOnes.length; idx++)
+ {
+ int i = negOnes[idx];
+ int j = N - 1 - i;
+ for (int k = N - 1; k >= 0; k--)
+ {
+ c[k] -= b[j];
+ j--;
+ if (j < 0)
+ {
+ j = N - 1;
+ }
+ }
+ }
+
+ return new IntegerPolynomial(c);
+ }
+
+ public IntegerPolynomial mult(IntegerPolynomial poly2, int modulus)
+ {
+ IntegerPolynomial c = mult(poly2);
+ c.mod(modulus);
+ return c;
+ }
+
+ public BigIntPolynomial mult(BigIntPolynomial poly2)
+ {
+ BigInteger[] b = poly2.coeffs;
+ if (b.length != N)
+ {
+ throw new IllegalArgumentException("Number of coefficients must be the same");
+ }
+
+ BigInteger[] c = new BigInteger[N];
+ for (int i = 0; i < N; i++)
+ {
+ c[i] = BigInteger.ZERO;
+ }
+
+ for (int idx = 0; idx != ones.length; idx++)
+ {
+ int i = ones[idx];
+ int j = N - 1 - i;
+ for (int k = N - 1; k >= 0; k--)
+ {
+ c[k] = c[k].add(b[j]);
+ j--;
+ if (j < 0)
+ {
+ j = N - 1;
+ }
+ }
+ }
+
+ for (int idx = 0; idx != negOnes.length; idx++)
+ {
+ int i = negOnes[idx];
+ int j = N - 1 - i;
+ for (int k = N - 1; k >= 0; k--)
+ {
+ c[k] = c[k].subtract(b[j]);
+ j--;
+ if (j < 0)
+ {
+ j = N - 1;
+ }
+ }
+ }
+
+ return new BigIntPolynomial(c);
+ }
+
+ public int[] getOnes()
+ {
+ return ones;
+ }
+
+ public int[] getNegOnes()
+ {
+ return negOnes;
+ }
+
+ /**
+ * Encodes the polynomial to a byte array writing <code>BITS_PER_INDEX</code> bits for each coefficient.
+ *
+ * @return the encoded polynomial
+ */
+ public byte[] toBinary()
+ {
+ int maxIndex = 1 << BITS_PER_INDEX;
+ byte[] bin1 = ArrayEncoder.encodeModQ(ones, maxIndex);
+ byte[] bin2 = ArrayEncoder.encodeModQ(negOnes, maxIndex);
+
+ byte[] bin = Arrays.copyOf(bin1, bin1.length + bin2.length);
+ System.arraycopy(bin2, 0, bin, bin1.length, bin2.length);
+ return bin;
+ }
+
+ public IntegerPolynomial toIntegerPolynomial()
+ {
+ int[] coeffs = new int[N];
+ for (int idx = 0; idx != ones.length; idx++)
+ {
+ int i = ones[idx];
+ coeffs[i] = 1;
+ }
+ for (int idx = 0; idx != negOnes.length; idx++)
+ {
+ int i = negOnes[idx];
+ coeffs[i] = -1;
+ }
+ return new IntegerPolynomial(coeffs);
+ }
+
+ public int size()
+ {
+ return N;
+ }
+
+ public void clear()
+ {
+ for (int i = 0; i < ones.length; i++)
+ {
+ ones[i] = 0;
+ }
+ for (int i = 0; i < negOnes.length; i++)
+ {
+ negOnes[i] = 0;
+ }
+ }
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + N;
+ result = prime * result + Arrays.hashCode(negOnes);
+ result = prime * result + Arrays.hashCode(ones);
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ SparseTernaryPolynomial other = (SparseTernaryPolynomial)obj;
+ if (N != other.N)
+ {
+ return false;
+ }
+ if (!Arrays.areEqual(negOnes, other.negOnes))
+ {
+ return false;
+ }
+ if (!Arrays.areEqual(ones, other.ones))
+ {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/TernaryPolynomial.java b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/TernaryPolynomial.java
new file mode 100644
index 00000000..822b64b4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/TernaryPolynomial.java
@@ -0,0 +1,25 @@
+package org.bouncycastle.pqc.math.ntru.polynomial;
+
+/**
+ * A polynomial whose coefficients are all equal to -1, 0, or 1
+ */
+public interface TernaryPolynomial
+ extends Polynomial
+{
+
+ /**
+ * Multiplies the polynomial by an <code>IntegerPolynomial</code>, taking the indices mod N
+ */
+ IntegerPolynomial mult(IntegerPolynomial poly2);
+
+ int[] getOnes();
+
+ int[] getNegOnes();
+
+ /**
+ * Returns the maximum number of coefficients the polynomial can have
+ */
+ int size();
+
+ void clear();
+}
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/ntru/util/ArrayEncoder.java b/core/src/main/java/org/bouncycastle/pqc/math/ntru/util/ArrayEncoder.java
new file mode 100644
index 00000000..0c8f5abf
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/ntru/util/ArrayEncoder.java
@@ -0,0 +1,292 @@
+package org.bouncycastle.pqc.math.ntru.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Converts a coefficient array to a compact byte array and vice versa.
+ */
+public class ArrayEncoder
+{
+ /**
+ * Bit string to coefficient conversion table from P1363.1. Also found at
+ * {@link http://stackoverflow.com/questions/1562548/how-to-make-a-message-into-a-polynomial}
+ * <p/>
+ * Convert each three-bit quantity to two ternary coefficients as follows, and concatenate the resulting
+ * ternary quantities to obtain [the output].
+ * <p/>
+ * <code>
+ * {0, 0, 0} -> {0, 0}<br/>
+ * {0, 0, 1} -> {0, 1}<br/>
+ * {0, 1, 0} -> {0, -1}<br/>
+ * {0, 1, 1} -> {1, 0}<br/>
+ * {1, 0, 0} -> {1, 1}<br/>
+ * {1, 0, 1} -> {1, -1}<br/>
+ * {1, 1, 0} -> {-1, 0}<br/>
+ * {1, 1, 1} -> {-1, 1}<br/>
+ * </code>
+ */
+ private static final int[] COEFF1_TABLE = {0, 0, 0, 1, 1, 1, -1, -1};
+ private static final int[] COEFF2_TABLE = {0, 1, -1, 0, 1, -1, 0, 1};
+ /**
+ * Coefficient to bit string conversion table from P1363.1. Also found at
+ * {@link http://stackoverflow.com/questions/1562548/how-to-make-a-message-into-a-polynomial}
+ * <p/>
+ * Convert each set of two ternary coefficients to three bits as follows, and concatenate the resulting bit
+ * quantities to obtain [the output]:
+ * <p/>
+ * <code>
+ * {-1, -1} -> set "fail" to 1 and set bit string to {1, 1, 1}
+ * {-1, 0} -> {1, 1, 0}<br/>
+ * {-1, 1} -> {1, 1, 1}<br/>
+ * {0, -1} -> {0, 1, 0}<br/>
+ * {0, 0} -> {0, 0, 0}<br/>
+ * {0, 1} -> {0, 0, 1}<br/>
+ * {1, -1} -> {1, 0, 1}<br/>
+ * {1, 0} -> {0, 1, 1}<br/>
+ * {1, 1} -> {1, 0, 0}<br/>
+ * </code>
+ */
+ private static final int[] BIT1_TABLE = {1, 1, 1, 0, 0, 0, 1, 0, 1};
+ private static final int[] BIT2_TABLE = {1, 1, 1, 1, 0, 0, 0, 1, 0};
+ private static final int[] BIT3_TABLE = {1, 0, 1, 0, 0, 1, 1, 1, 0};
+
+ /**
+ * Encodes an int array whose elements are between 0 and <code>q</code>,
+ * to a byte array leaving no gaps between bits.<br/>
+ * <code>q</code> must be a power of 2.
+ *
+ * @param a the input array
+ * @param q the modulus
+ * @return the encoded array
+ */
+ public static byte[] encodeModQ(int[] a, int q)
+ {
+ int bitsPerCoeff = 31 - Integer.numberOfLeadingZeros(q);
+ int numBits = a.length * bitsPerCoeff;
+ int numBytes = (numBits + 7) / 8;
+ byte[] data = new byte[numBytes];
+ int bitIndex = 0;
+ int byteIndex = 0;
+ for (int i = 0; i < a.length; i++)
+ {
+ for (int j = 0; j < bitsPerCoeff; j++)
+ {
+ int currentBit = (a[i] >> j) & 1;
+ data[byteIndex] |= currentBit << bitIndex;
+ if (bitIndex == 7)
+ {
+ bitIndex = 0;
+ byteIndex++;
+ }
+ else
+ {
+ bitIndex++;
+ }
+ }
+ }
+ return data;
+ }
+
+ /**
+ * Decodes a <code>byte</code> array encoded with {@link #encodeModQ(int[], int)} back to an <code>int</code> array.<br/>
+ * <code>N</code> is the number of coefficients. <code>q</code> must be a power of <code>2</code>.<br/>
+ * Ignores any excess bytes.
+ *
+ * @param data an encoded ternary polynomial
+ * @param N number of coefficients
+ * @param q
+ * @return an array containing <code>N</code> coefficients between <code>0</code> and <code>q-1</code>
+ */
+ public static int[] decodeModQ(byte[] data, int N, int q)
+ {
+ int[] coeffs = new int[N];
+ int bitsPerCoeff = 31 - Integer.numberOfLeadingZeros(q);
+ int numBits = N * bitsPerCoeff;
+ int coeffIndex = 0;
+ for (int bitIndex = 0; bitIndex < numBits; bitIndex++)
+ {
+ if (bitIndex > 0 && bitIndex % bitsPerCoeff == 0)
+ {
+ coeffIndex++;
+ }
+ int bit = getBit(data, bitIndex);
+ coeffs[coeffIndex] += bit << (bitIndex % bitsPerCoeff);
+ }
+ return coeffs;
+ }
+
+ /**
+ * Decodes data encoded with {@link #encodeModQ(int[], int)} back to an <code>int</code> array.<br/>
+ * <code>N</code> is the number of coefficients. <code>q</code> must be a power of <code>2</code>.<br/>
+ * Ignores any excess bytes.
+ *
+ * @param is an encoded ternary polynomial
+ * @param N number of coefficients
+ * @param q
+ * @return the decoded polynomial
+ */
+ public static int[] decodeModQ(InputStream is, int N, int q)
+ throws IOException
+ {
+ int qBits = 31 - Integer.numberOfLeadingZeros(q);
+ int size = (N * qBits + 7) / 8;
+ byte[] arr = Util.readFullLength(is, size);
+ return decodeModQ(arr, N, q);
+ }
+
+ /**
+ * Decodes a <code>byte</code> array encoded with {@link #encodeMod3Sves(int[])} back to an <code>int</code> array
+ * with <code>N</code> coefficients between <code>-1</code> and <code>1</code>.<br/>
+ * Ignores any excess bytes.<br/>
+ * See P1363.1 section 9.2.2.
+ *
+ * @param data an encoded ternary polynomial
+ * @param N number of coefficients
+ * @return the decoded coefficients
+ */
+ public static int[] decodeMod3Sves(byte[] data, int N)
+ {
+ int[] coeffs = new int[N];
+ int coeffIndex = 0;
+ for (int bitIndex = 0; bitIndex < data.length * 8; )
+ {
+ int bit1 = getBit(data, bitIndex++);
+ int bit2 = getBit(data, bitIndex++);
+ int bit3 = getBit(data, bitIndex++);
+ int coeffTableIndex = bit1 * 4 + bit2 * 2 + bit3;
+ coeffs[coeffIndex++] = COEFF1_TABLE[coeffTableIndex];
+ coeffs[coeffIndex++] = COEFF2_TABLE[coeffTableIndex];
+ // ignore bytes that can't fit
+ if (coeffIndex > N - 2)
+ {
+ break;
+ }
+ }
+ return coeffs;
+ }
+
+ /**
+ * Encodes an <code>int</code> array whose elements are between <code>-1</code> and <code>1</code>, to a byte array.
+ * <code>coeffs[2*i]</code> and <code>coeffs[2*i+1]</code> must not both equal -1 for any integer </code>i<code>,
+ * so this method is only safe to use with arrays produced by {@link #decodeMod3Sves(byte[], int)}.<br/>
+ * See P1363.1 section 9.2.3.
+ *
+ * @param arr
+ * @return the encoded array
+ */
+ public static byte[] encodeMod3Sves(int[] arr)
+ {
+ int numBits = (arr.length * 3 + 1) / 2;
+ int numBytes = (numBits + 7) / 8;
+ byte[] data = new byte[numBytes];
+ int bitIndex = 0;
+ int byteIndex = 0;
+ for (int i = 0; i < arr.length / 2 * 2; )
+ { // if length is an odd number, throw away the highest coeff
+ int coeff1 = arr[i++] + 1;
+ int coeff2 = arr[i++] + 1;
+ if (coeff1 == 0 && coeff2 == 0)
+ {
+ throw new IllegalStateException("Illegal encoding!");
+ }
+ int bitTableIndex = coeff1 * 3 + coeff2;
+ int[] bits = new int[]{BIT1_TABLE[bitTableIndex], BIT2_TABLE[bitTableIndex], BIT3_TABLE[bitTableIndex]};
+ for (int j = 0; j < 3; j++)
+ {
+ data[byteIndex] |= bits[j] << bitIndex;
+ if (bitIndex == 7)
+ {
+ bitIndex = 0;
+ byteIndex++;
+ }
+ else
+ {
+ bitIndex++;
+ }
+ }
+ }
+ return data;
+ }
+
+ /**
+ * Encodes an <code>int</code> array whose elements are between <code>-1</code> and <code>1</code>, to a byte array.
+ *
+ * @return the encoded array
+ */
+ public static byte[] encodeMod3Tight(int[] intArray)
+ {
+ BigInteger sum = BigInteger.ZERO;
+ for (int i = intArray.length - 1; i >= 0; i--)
+ {
+ sum = sum.multiply(BigInteger.valueOf(3));
+ sum = sum.add(BigInteger.valueOf(intArray[i] + 1));
+ }
+
+ int size = (BigInteger.valueOf(3).pow(intArray.length).bitLength() + 7) / 8;
+ byte[] arr = sum.toByteArray();
+
+ if (arr.length < size)
+ {
+ // pad with leading zeros so arr.length==size
+ byte[] arr2 = new byte[size];
+ System.arraycopy(arr, 0, arr2, size - arr.length, arr.length);
+ return arr2;
+ }
+
+ if (arr.length > size)
+ // drop sign bit
+ {
+ arr = Arrays.copyOfRange(arr, 1, arr.length);
+ }
+ return arr;
+ }
+
+ /**
+ * Converts a byte array produced by {@link #encodeMod3Tight(int[])} back to an <code>int</code> array.
+ *
+ * @param b a byte array
+ * @param N number of coefficients
+ * @return the decoded array
+ */
+ public static int[] decodeMod3Tight(byte[] b, int N)
+ {
+ BigInteger sum = new BigInteger(1, b);
+ int[] coeffs = new int[N];
+ for (int i = 0; i < N; i++)
+ {
+ coeffs[i] = sum.mod(BigInteger.valueOf(3)).intValue() - 1;
+ if (coeffs[i] > 1)
+ {
+ coeffs[i] -= 3;
+ }
+ sum = sum.divide(BigInteger.valueOf(3));
+ }
+ return coeffs;
+ }
+
+ /**
+ * Converts data produced by {@link #encodeMod3Tight(int[])} back to an <code>int</code> array.
+ *
+ * @param is an input stream containing the data to decode
+ * @param N number of coefficients
+ * @return the decoded array
+ */
+ public static int[] decodeMod3Tight(InputStream is, int N)
+ throws IOException
+ {
+ int size = (int)Math.ceil(N * Math.log(3) / Math.log(2) / 8);
+ byte[] arr = Util.readFullLength(is, size);
+ return decodeMod3Tight(arr, N);
+ }
+
+ private static int getBit(byte[] arr, int bitIndex)
+ {
+ int byteIndex = bitIndex / 8;
+ int arrElem = arr[byteIndex] & 0xFF;
+ return (arrElem >> (bitIndex % 8)) & 1;
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/ntru/util/Util.java b/core/src/main/java/org/bouncycastle/pqc/math/ntru/util/Util.java
new file mode 100644
index 00000000..92c2ed4d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/pqc/math/ntru/util/Util.java
@@ -0,0 +1,158 @@
+package org.bouncycastle.pqc.math.ntru.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.bouncycastle.pqc.math.ntru.euclid.IntEuclidean;
+import org.bouncycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial;
+import org.bouncycastle.pqc.math.ntru.polynomial.TernaryPolynomial;
+import org.bouncycastle.util.Integers;
+
+public class Util
+{
+ private static volatile boolean IS_64_BITNESS_KNOWN;
+ private static volatile boolean IS_64_BIT_JVM;
+
+ /**
+ * Calculates the inverse of n mod modulus
+ */
+ public static int invert(int n, int modulus)
+ {
+ n %= modulus;
+ if (n < 0)
+ {
+ n += modulus;
+ }
+ return IntEuclidean.calculate(n, modulus).x;
+ }
+
+ /**
+ * Calculates a^b mod modulus
+ */
+ public static int pow(int a, int b, int modulus)
+ {
+ int p = 1;
+ for (int i = 0; i < b; i++)
+ {
+ p = (p * a) % modulus;
+ }
+ return p;
+ }
+
+ /**
+ * Calculates a^b mod modulus
+ */
+ public static long pow(long a, int b, long modulus)
+ {
+ long p = 1;
+ for (int i = 0; i < b; i++)
+ {
+ p = (p * a) % modulus;
+ }
+ return p;
+ }
+
+ /**
+ * Generates a "sparse" or "dense" polynomial containing numOnes ints equal to 1,
+ * numNegOnes int equal to -1, and the rest equal to 0.
+ *
+ * @param N
+ * @param numOnes
+ * @param numNegOnes
+ * @param sparse whether to create a {@link SparseTernaryPolynomial} or {@link DenseTernaryPolynomial}
+ * @return a ternary polynomial
+ */
+ public static TernaryPolynomial generateRandomTernary(int N, int numOnes, int numNegOnes, boolean sparse, SecureRandom random)
+ {
+ if (sparse)
+ {
+ return SparseTernaryPolynomial.generateRandom(N, numOnes, numNegOnes, random);
+ }
+ else
+ {
+ return DenseTernaryPolynomial.generateRandom(N, numOnes, numNegOnes, random);
+ }
+ }
+
+ /**
+ * Generates an array containing numOnes ints equal to 1,
+ * numNegOnes int equal to -1, and the rest equal to 0.
+ *
+ * @param N
+ * @param numOnes
+ * @param numNegOnes
+ * @return an array of integers
+ */
+ public static int[] generateRandomTernary(int N, int numOnes, int numNegOnes, SecureRandom random)
+ {
+ Integer one = Integers.valueOf(1);
+ Integer minusOne = Integers.valueOf(-1);
+ Integer zero = Integers.valueOf(0);
+
+ List list = new ArrayList();
+ for (int i = 0; i < numOnes; i++)
+ {
+ list.add(one);
+ }
+ for (int i = 0; i < numNegOnes; i++)
+ {
+ list.add(minusOne);
+ }
+ while (list.size() < N)
+ {
+ list.add(zero);
+ }
+
+ Collections.shuffle(list, random);
+
+ int[] arr = new int[N];
+ for (int i = 0; i < N; i++)
+ {
+ arr[i] = ((Integer)list.get(i)).intValue();
+ }
+ return arr;
+ }
+
+ /**
+ * Takes an educated guess as to whether 64 bits are supported by the JVM.
+ *
+ * @return <code>true</code> if 64-bit support detected, <code>false</code> otherwise
+ */
+ public static boolean is64BitJVM()
+ {
+ if (!IS_64_BITNESS_KNOWN)
+ {
+ String arch = System.getProperty("os.arch");
+ String sunModel = System.getProperty("sun.arch.data.model");
+ IS_64_BIT_JVM = "amd64".equals(arch) || "x86_64".equals(arch) || "ppc64".equals(arch) || "64".equals(sunModel);
+ IS_64_BITNESS_KNOWN = true;
+ }
+ return IS_64_BIT_JVM;
+ }
+
+ /**
+ * Reads a given number of bytes from an <code>InputStream</code>.
+ * If there are not enough bytes in the stream, an <code>IOException</code>
+ * is thrown.
+ *
+ * @param is
+ * @param length
+ * @return an array of length <code>length</code>
+ * @throws IOException
+ */
+ public static byte[] readFullLength(InputStream is, int length)
+ throws IOException
+ {
+ byte[] arr = new byte[length];
+ if (is.read(arr) != arr.length)
+ {
+ throw new IOException("Not enough bytes to read.");
+ }
+ return arr;
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/bouncycastle/util/Arrays.java b/core/src/main/java/org/bouncycastle/util/Arrays.java
new file mode 100644
index 00000000..457320eb
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/Arrays.java
@@ -0,0 +1,736 @@
+package org.bouncycastle.util;
+
+import java.math.BigInteger;
+
+/**
+ * General array utilities.
+ */
+public final class Arrays
+{
+ private Arrays()
+ {
+ // static class, hide constructor
+ }
+
+ public static boolean areEqual(
+ boolean[] a,
+ boolean[] b)
+ {
+ if (a == b)
+ {
+ return true;
+ }
+
+ if (a == null || b == null)
+ {
+ return false;
+ }
+
+ if (a.length != b.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != a.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static boolean areEqual(
+ char[] a,
+ char[] b)
+ {
+ if (a == b)
+ {
+ return true;
+ }
+
+ if (a == null || b == null)
+ {
+ return false;
+ }
+
+ if (a.length != b.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != a.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static boolean areEqual(
+ byte[] a,
+ byte[] b)
+ {
+ if (a == b)
+ {
+ return true;
+ }
+
+ if (a == null || b == null)
+ {
+ return false;
+ }
+
+ if (a.length != b.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != a.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * A constant time equals comparison - does not terminate early if
+ * test will fail.
+ *
+ * @param a first array
+ * @param b second array
+ * @return true if arrays equal, false otherwise.
+ */
+ public static boolean constantTimeAreEqual(
+ byte[] a,
+ byte[] b)
+ {
+ if (a == b)
+ {
+ return true;
+ }
+
+ if (a == null || b == null)
+ {
+ return false;
+ }
+
+ if (a.length != b.length)
+ {
+ return false;
+ }
+
+ int nonEqual = 0;
+
+ for (int i = 0; i != a.length; i++)
+ {
+ nonEqual |= (a[i] ^ b[i]);
+ }
+
+ return nonEqual == 0;
+ }
+
+ public static boolean areEqual(
+ int[] a,
+ int[] b)
+ {
+ if (a == b)
+ {
+ return true;
+ }
+
+ if (a == null || b == null)
+ {
+ return false;
+ }
+
+ if (a.length != b.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != a.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static boolean areEqual(
+ long[] a,
+ long[] b)
+ {
+ if (a == b)
+ {
+ return true;
+ }
+
+ if (a == null || b == null)
+ {
+ return false;
+ }
+
+ if (a.length != b.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != a.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static boolean areEqual(
+ BigInteger[] a,
+ BigInteger[] b)
+ {
+ if (a == b)
+ {
+ return true;
+ }
+
+ if (a == null || b == null)
+ {
+ return false;
+ }
+
+ if (a.length != b.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != a.length; i++)
+ {
+ if (!a[i].equals(b[i]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static void fill(
+ byte[] array,
+ byte value)
+ {
+ for (int i = 0; i < array.length; i++)
+ {
+ array[i] = value;
+ }
+ }
+
+ public static void fill(
+ char[] array,
+ char value)
+ {
+ for (int i = 0; i < array.length; i++)
+ {
+ array[i] = value;
+ }
+ }
+
+ public static void fill(
+ long[] array,
+ long value)
+ {
+ for (int i = 0; i < array.length; i++)
+ {
+ array[i] = value;
+ }
+ }
+
+ public static void fill(
+ short[] array,
+ short value)
+ {
+ for (int i = 0; i < array.length; i++)
+ {
+ array[i] = value;
+ }
+ }
+
+ public static void fill(
+ int[] array,
+ int value)
+ {
+ for (int i = 0; i < array.length; i++)
+ {
+ array[i] = value;
+ }
+ }
+
+ public static int hashCode(byte[] data)
+ {
+ if (data == null)
+ {
+ return 0;
+ }
+
+ int i = data.length;
+ int hc = i + 1;
+
+ while (--i >= 0)
+ {
+ hc *= 257;
+ hc ^= data[i];
+ }
+
+ return hc;
+ }
+
+ public static int hashCode(char[] data)
+ {
+ if (data == null)
+ {
+ return 0;
+ }
+
+ int i = data.length;
+ int hc = i + 1;
+
+ while (--i >= 0)
+ {
+ hc *= 257;
+ hc ^= data[i];
+ }
+
+ return hc;
+ }
+
+ public static int hashCode(int[][] ints)
+ {
+ int hc = 0;
+
+ for (int i = 0; i != ints.length; i++)
+ {
+ hc = hc * 257 + hashCode(ints[i]);
+ }
+
+ return hc;
+ }
+
+ public static int hashCode(int[] data)
+ {
+ if (data == null)
+ {
+ return 0;
+ }
+
+ int i = data.length;
+ int hc = i + 1;
+
+ while (--i >= 0)
+ {
+ hc *= 257;
+ hc ^= data[i];
+ }
+
+ return hc;
+ }
+
+ public static int hashCode(short[][][] shorts)
+ {
+ int hc = 0;
+
+ for (int i = 0; i != shorts.length; i++)
+ {
+ hc = hc * 257 + hashCode(shorts[i]);
+ }
+
+ return hc;
+ }
+
+ public static int hashCode(short[][] shorts)
+ {
+ int hc = 0;
+
+ for (int i = 0; i != shorts.length; i++)
+ {
+ hc = hc * 257 + hashCode(shorts[i]);
+ }
+
+ return hc;
+ }
+
+ public static int hashCode(short[] data)
+ {
+ if (data == null)
+ {
+ return 0;
+ }
+
+ int i = data.length;
+ int hc = i + 1;
+
+ while (--i >= 0)
+ {
+ hc *= 257;
+ hc ^= (data[i] & 0xff);
+ }
+
+ return hc;
+ }
+
+ public static int hashCode(BigInteger[] data)
+ {
+ if (data == null)
+ {
+ return 0;
+ }
+
+ int i = data.length;
+ int hc = i + 1;
+
+ while (--i >= 0)
+ {
+ hc *= 257;
+ hc ^= data[i].hashCode();
+ }
+
+ return hc;
+ }
+
+ public static byte[] clone(byte[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ byte[] copy = new byte[data.length];
+
+ System.arraycopy(data, 0, copy, 0, data.length);
+
+ return copy;
+ }
+
+ public static byte[][] clone(byte[][] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+
+ byte[][] copy = new byte[data.length][];
+
+ for (int i = 0; i != copy.length; i++)
+ {
+ copy[i] = clone(data[i]);
+ }
+
+ return copy;
+ }
+
+ public static byte[][][] clone(byte[][][] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+
+ byte[][][] copy = new byte[data.length][][];
+
+ for (int i = 0; i != copy.length; i++)
+ {
+ copy[i] = clone(data[i]);
+ }
+
+ return copy;
+ }
+
+ public static int[] clone(int[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ int[] copy = new int[data.length];
+
+ System.arraycopy(data, 0, copy, 0, data.length);
+
+ return copy;
+ }
+
+ public static short[] clone(short[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ short[] copy = new short[data.length];
+
+ System.arraycopy(data, 0, copy, 0, data.length);
+
+ return copy;
+ }
+
+ public static BigInteger[] clone(BigInteger[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ BigInteger[] copy = new BigInteger[data.length];
+
+ System.arraycopy(data, 0, copy, 0, data.length);
+
+ return copy;
+ }
+
+ public static byte[] copyOf(byte[] data, int newLength)
+ {
+ byte[] tmp = new byte[newLength];
+
+ if (newLength < data.length)
+ {
+ System.arraycopy(data, 0, tmp, 0, newLength);
+ }
+ else
+ {
+ System.arraycopy(data, 0, tmp, 0, data.length);
+ }
+
+ return tmp;
+ }
+
+ public static char[] copyOf(char[] data, int newLength)
+ {
+ char[] tmp = new char[newLength];
+
+ if (newLength < data.length)
+ {
+ System.arraycopy(data, 0, tmp, 0, newLength);
+ }
+ else
+ {
+ System.arraycopy(data, 0, tmp, 0, data.length);
+ }
+
+ return tmp;
+ }
+
+ public static int[] copyOf(int[] data, int newLength)
+ {
+ int[] tmp = new int[newLength];
+
+ if (newLength < data.length)
+ {
+ System.arraycopy(data, 0, tmp, 0, newLength);
+ }
+ else
+ {
+ System.arraycopy(data, 0, tmp, 0, data.length);
+ }
+
+ return tmp;
+ }
+
+ public static long[] copyOf(long[] data, int newLength)
+ {
+ long[] tmp = new long[newLength];
+
+ if (newLength < data.length)
+ {
+ System.arraycopy(data, 0, tmp, 0, newLength);
+ }
+ else
+ {
+ System.arraycopy(data, 0, tmp, 0, data.length);
+ }
+
+ return tmp;
+ }
+
+ public static BigInteger[] copyOf(BigInteger[] data, int newLength)
+ {
+ BigInteger[] tmp = new BigInteger[newLength];
+
+ if (newLength < data.length)
+ {
+ System.arraycopy(data, 0, tmp, 0, newLength);
+ }
+ else
+ {
+ System.arraycopy(data, 0, tmp, 0, data.length);
+ }
+
+ return tmp;
+ }
+
+ public static byte[] copyOfRange(byte[] data, int from, int to)
+ {
+ int newLength = getLength(from, to);
+
+ byte[] tmp = new byte[newLength];
+
+ if (data.length - from < newLength)
+ {
+ System.arraycopy(data, from, tmp, 0, data.length - from);
+ }
+ else
+ {
+ System.arraycopy(data, from, tmp, 0, newLength);
+ }
+
+ return tmp;
+ }
+
+ public static int[] copyOfRange(int[] data, int from, int to)
+ {
+ int newLength = getLength(from, to);
+
+ int[] tmp = new int[newLength];
+
+ if (data.length - from < newLength)
+ {
+ System.arraycopy(data, from, tmp, 0, data.length - from);
+ }
+ else
+ {
+ System.arraycopy(data, from, tmp, 0, newLength);
+ }
+
+ return tmp;
+ }
+
+ public static long[] copyOfRange(long[] data, int from, int to)
+ {
+ int newLength = getLength(from, to);
+
+ long[] tmp = new long[newLength];
+
+ if (data.length - from < newLength)
+ {
+ System.arraycopy(data, from, tmp, 0, data.length - from);
+ }
+ else
+ {
+ System.arraycopy(data, from, tmp, 0, newLength);
+ }
+
+ return tmp;
+ }
+
+ public static BigInteger[] copyOfRange(BigInteger[] data, int from, int to)
+ {
+ int newLength = getLength(from, to);
+
+ BigInteger[] tmp = new BigInteger[newLength];
+
+ if (data.length - from < newLength)
+ {
+ System.arraycopy(data, from, tmp, 0, data.length - from);
+ }
+ else
+ {
+ System.arraycopy(data, from, tmp, 0, newLength);
+ }
+
+ return tmp;
+ }
+
+ private static int getLength(int from, int to)
+ {
+ int newLength = to - from;
+ if (newLength < 0)
+ {
+ StringBuffer sb = new StringBuffer(from);
+ sb.append(" > ").append(to);
+ throw new IllegalArgumentException(sb.toString());
+ }
+ return newLength;
+ }
+
+ public static byte[] concatenate(byte[] a, byte[] b)
+ {
+ if (a != null && b != null)
+ {
+ byte[] rv = new byte[a.length + b.length];
+
+ System.arraycopy(a, 0, rv, 0, a.length);
+ System.arraycopy(b, 0, rv, a.length, b.length);
+
+ return rv;
+ }
+ else if (b != null)
+ {
+ return clone(b);
+ }
+ else
+ {
+ return clone(a);
+ }
+ }
+
+ public static byte[] concatenate(byte[] a, byte[] b, byte[] c)
+ {
+ if (a != null && b != null && c != null)
+ {
+ byte[] rv = new byte[a.length + b.length + c.length];
+
+ System.arraycopy(a, 0, rv, 0, a.length);
+ System.arraycopy(b, 0, rv, a.length, b.length);
+ System.arraycopy(c, 0, rv, a.length + b.length, c.length);
+
+ return rv;
+ }
+ else if (b == null)
+ {
+ return concatenate(a, c);
+ }
+ else
+ {
+ return concatenate(a, b);
+ }
+ }
+
+ public static byte[] concatenate(byte[] a, byte[] b, byte[] c, byte[] d)
+ {
+ if (a != null && b != null && c != null && d != null)
+ {
+ byte[] rv = new byte[a.length + b.length + c.length + d.length];
+
+ System.arraycopy(a, 0, rv, 0, a.length);
+ System.arraycopy(b, 0, rv, a.length, b.length);
+ System.arraycopy(c, 0, rv, a.length + b.length, c.length);
+ System.arraycopy(d, 0, rv, a.length + b.length + c.length, d.length);
+
+ 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(b, c, d);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/BigIntegers.java b/core/src/main/java/org/bouncycastle/util/BigIntegers.java
new file mode 100644
index 00000000..e2fe590a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/BigIntegers.java
@@ -0,0 +1,123 @@
+package org.bouncycastle.util;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/**
+ * BigInteger utilities.
+ */
+public final class BigIntegers
+{
+ private static final int MAX_ITERATIONS = 1000;
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+
+ /**
+ * Return the passed in value as an unsigned byte array.
+ *
+ * @param value value to be converted.
+ * @return a byte array without a leading zero byte if present in the signed encoding.
+ */
+ public static byte[] asUnsignedByteArray(
+ BigInteger value)
+ {
+ byte[] bytes = value.toByteArray();
+
+ if (bytes[0] == 0)
+ {
+ byte[] tmp = new byte[bytes.length - 1];
+
+ System.arraycopy(bytes, 1, tmp, 0, tmp.length);
+
+ return tmp;
+ }
+
+ return bytes;
+ }
+
+ /**
+ * Return the passed in value as an unsigned byte array.
+ *
+ * @param value value to be converted.
+ * @return a byte array without a leading zero byte if present in the signed encoding.
+ */
+ public static byte[] asUnsignedByteArray(
+ int length,
+ BigInteger value)
+ {
+ byte[] bytes = value.toByteArray();
+
+ if (bytes[0] == 0)
+ {
+ if (bytes.length - 1 > length)
+ {
+ throw new IllegalArgumentException("standard length exceeded for value");
+ }
+
+ byte[] tmp = new byte[length];
+
+ System.arraycopy(bytes, 1, tmp, tmp.length - (bytes.length - 1), bytes.length - 1);
+
+ return tmp;
+ }
+ else
+ {
+ if (bytes.length == length)
+ {
+ return bytes;
+ }
+
+ if (bytes.length > length)
+ {
+ throw new IllegalArgumentException("standard length exceeded for value");
+ }
+
+ byte[] tmp = new byte[length];
+
+ System.arraycopy(bytes, 0, tmp, tmp.length - bytes.length, bytes.length);
+
+ return tmp;
+ }
+ }
+
+ /**
+ * Return a random BigInteger not less than 'min' and not greater than 'max'
+ *
+ * @param min the least value that may be generated
+ * @param max the greatest value that may be generated
+ * @param random the source of randomness
+ * @return a random BigInteger value in the range [min,max]
+ */
+ public static BigInteger createRandomInRange(
+ BigInteger min,
+ BigInteger max,
+ SecureRandom random)
+ {
+ int cmp = min.compareTo(max);
+ if (cmp >= 0)
+ {
+ if (cmp > 0)
+ {
+ throw new IllegalArgumentException("'min' may not be greater than 'max'");
+ }
+
+ return min;
+ }
+
+ if (min.bitLength() > max.bitLength() / 2)
+ {
+ return createRandomInRange(ZERO, max.subtract(min), random).add(min);
+ }
+
+ for (int i = 0; i < MAX_ITERATIONS; ++i)
+ {
+ BigInteger x = new BigInteger(max.bitLength(), random);
+ if (x.compareTo(min) >= 0 && x.compareTo(max) <= 0)
+ {
+ return x;
+ }
+ }
+
+ // fall back to a faster (restricted) method
+ return new BigInteger(max.subtract(min).bitLength() - 1, random).add(min);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/CollectionStore.java b/core/src/main/java/org/bouncycastle/util/CollectionStore.java
new file mode 100644
index 00000000..91aba146
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/CollectionStore.java
@@ -0,0 +1,57 @@
+package org.bouncycastle.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A simple collection backed store.
+ */
+public class CollectionStore
+ implements Store
+{
+ private Collection _local;
+
+ /**
+ * Basic constructor.
+ *
+ * @param collection - initial contents for the store, this is copied.
+ */
+ public CollectionStore(
+ Collection collection)
+ {
+ _local = new ArrayList(collection);
+ }
+
+ /**
+ * Return the matches in the collection for the passed in selector.
+ *
+ * @param selector the selector to match against.
+ * @return a possibly empty collection of matching objects.
+ */
+ public Collection getMatches(Selector selector)
+ {
+ if (selector == null)
+ {
+ return new ArrayList(_local);
+ }
+ else
+ {
+ List col = new ArrayList();
+ Iterator iter = _local.iterator();
+
+ while (iter.hasNext())
+ {
+ Object obj = iter.next();
+
+ if (selector.match(obj))
+ {
+ col.add(obj);
+ }
+ }
+
+ return col;
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/IPAddress.java b/core/src/main/java/org/bouncycastle/util/IPAddress.java
new file mode 100644
index 00000000..9f5d1cbc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/IPAddress.java
@@ -0,0 +1,188 @@
+package org.bouncycastle.util;
+
+public class IPAddress
+{
+ /**
+ * Validate the given IPv4 or IPv6 address.
+ *
+ * @param address the IP address as a String.
+ *
+ * @return true if a valid address, false otherwise
+ */
+ public static boolean isValid(
+ String address)
+ {
+ return isValidIPv4(address) || isValidIPv6(address);
+ }
+
+ /**
+ * Validate the given IPv4 or IPv6 address and netmask.
+ *
+ * @param address the IP address as a String.
+ *
+ * @return true if a valid address with netmask, false otherwise
+ */
+ public static boolean isValidWithNetMask(
+ String address)
+ {
+ return isValidIPv4WithNetmask(address) || isValidIPv6WithNetmask(address);
+ }
+
+ /**
+ * Validate the given IPv4 address.
+ *
+ * @param address the IP address as a String.
+ *
+ * @return true if a valid IPv4 address, false otherwise
+ */
+ public static boolean isValidIPv4(
+ String address)
+ {
+ if (address.length() == 0)
+ {
+ return false;
+ }
+
+ int octet;
+ int octets = 0;
+
+ String temp = address+".";
+
+ int pos;
+ int start = 0;
+ while (start < temp.length()
+ && (pos = temp.indexOf('.', start)) > start)
+ {
+ if (octets == 4)
+ {
+ return false;
+ }
+ try
+ {
+ octet = Integer.parseInt(temp.substring(start, pos));
+ }
+ catch (NumberFormatException ex)
+ {
+ return false;
+ }
+ if (octet < 0 || octet > 255)
+ {
+ return false;
+ }
+ start = pos + 1;
+ octets++;
+ }
+
+ return octets == 4;
+ }
+
+ public static boolean isValidIPv4WithNetmask(
+ String address)
+ {
+ int index = address.indexOf("/");
+ String mask = address.substring(index + 1);
+
+ return (index > 0) && isValidIPv4(address.substring(0, index))
+ && (isValidIPv4(mask) || isMaskValue(mask, 32));
+ }
+
+ public static boolean isValidIPv6WithNetmask(
+ String address)
+ {
+ int index = address.indexOf("/");
+ String mask = address.substring(index + 1);
+
+ return (index > 0) && (isValidIPv6(address.substring(0, index))
+ && (isValidIPv6(mask) || isMaskValue(mask, 128)));
+ }
+
+ private static boolean isMaskValue(String component, int size)
+ {
+ try
+ {
+ int value = Integer.parseInt(component);
+
+ return value >= 0 && value <= size;
+ }
+ catch (NumberFormatException e)
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Validate the given IPv6 address.
+ *
+ * @param address the IP address as a String.
+ *
+ * @return true if a valid IPv4 address, false otherwise
+ */
+ public static boolean isValidIPv6(
+ String address)
+ {
+ if (address.length() == 0)
+ {
+ return false;
+ }
+
+ int octet;
+ int octets = 0;
+
+ String temp = address + ":";
+ boolean doubleColonFound = false;
+ int pos;
+ int start = 0;
+ while (start < temp.length()
+ && (pos = temp.indexOf(':', start)) >= start)
+ {
+ if (octets == 8)
+ {
+ return false;
+ }
+
+ if (start != pos)
+ {
+ String value = temp.substring(start, pos);
+
+ if (pos == (temp.length() - 1) && value.indexOf('.') > 0)
+ {
+ if (!isValidIPv4(value))
+ {
+ return false;
+ }
+
+ octets++; // add an extra one as address covers 2 words.
+ }
+ else
+ {
+ try
+ {
+ octet = Integer.parseInt(temp.substring(start, pos), 16);
+ }
+ catch (NumberFormatException ex)
+ {
+ return false;
+ }
+ if (octet < 0 || octet > 0xffff)
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ if (pos != 1 && pos != temp.length() - 1 && doubleColonFound)
+ {
+ return false;
+ }
+ doubleColonFound = true;
+ }
+ start = pos + 1;
+ octets++;
+ }
+
+ return octets == 8 || doubleColonFound;
+ }
+}
+
+
diff --git a/core/src/main/java/org/bouncycastle/util/Integers.java b/core/src/main/java/org/bouncycastle/util/Integers.java
new file mode 100644
index 00000000..599a9e0c
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/Integers.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.util;
+
+public class Integers
+{
+ public static Integer valueOf(int value)
+ {
+ return Integer.valueOf(value);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/Memoable.java b/core/src/main/java/org/bouncycastle/util/Memoable.java
new file mode 100644
index 00000000..0be91711
--- /dev/null
+++ b/core/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/core/src/main/java/org/bouncycastle/util/MemoableResetException.java b/core/src/main/java/org/bouncycastle/util/MemoableResetException.java
new file mode 100644
index 00000000..6552bd49
--- /dev/null
+++ b/core/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/core/src/main/java/org/bouncycastle/util/Selector.java b/core/src/main/java/org/bouncycastle/util/Selector.java
new file mode 100644
index 00000000..7ad86bf4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/Selector.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.util;
+
+public interface Selector
+ extends Cloneable
+{
+ boolean match(Object obj);
+
+ Object clone();
+}
diff --git a/core/src/main/java/org/bouncycastle/util/Store.java b/core/src/main/java/org/bouncycastle/util/Store.java
new file mode 100644
index 00000000..b994c926
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/Store.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.util;
+
+import java.util.Collection;
+
+public interface Store
+{
+ Collection getMatches(Selector selector)
+ throws StoreException;
+}
diff --git a/core/src/main/java/org/bouncycastle/util/StoreException.java b/core/src/main/java/org/bouncycastle/util/StoreException.java
new file mode 100644
index 00000000..5ea09e80
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/StoreException.java
@@ -0,0 +1,18 @@
+package org.bouncycastle.util;
+
+public class StoreException
+ extends RuntimeException
+{
+ private Throwable _e;
+
+ public StoreException(String s, Throwable e)
+ {
+ super(s);
+ _e = e;
+ }
+
+ public Throwable getCause()
+ {
+ return _e;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/StreamParser.java b/core/src/main/java/org/bouncycastle/util/StreamParser.java
new file mode 100644
index 00000000..13f2b163
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/StreamParser.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.util;
+
+import java.util.Collection;
+
+public interface StreamParser
+{
+ Object read() throws StreamParsingException;
+
+ Collection readAll() throws StreamParsingException;
+}
diff --git a/core/src/main/java/org/bouncycastle/util/StreamParsingException.java b/core/src/main/java/org/bouncycastle/util/StreamParsingException.java
new file mode 100644
index 00000000..0a76378a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/StreamParsingException.java
@@ -0,0 +1,18 @@
+package org.bouncycastle.util;
+
+public class StreamParsingException
+ extends Exception
+{
+ Throwable _e;
+
+ public StreamParsingException(String message, Throwable e)
+ {
+ super(message);
+ _e = e;
+ }
+
+ public Throwable getCause()
+ {
+ return _e;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/Strings.java b/core/src/main/java/org/bouncycastle/util/Strings.java
new file mode 100644
index 00000000..7f674046
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/Strings.java
@@ -0,0 +1,303 @@
+package org.bouncycastle.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Vector;
+
+public final class Strings
+{
+ public static String fromUTF8ByteArray(byte[] bytes)
+ {
+ int i = 0;
+ int length = 0;
+
+ while (i < bytes.length)
+ {
+ length++;
+ if ((bytes[i] & 0xf0) == 0xf0)
+ {
+ // surrogate pair
+ length++;
+ i += 4;
+ }
+ else if ((bytes[i] & 0xe0) == 0xe0)
+ {
+ i += 3;
+ }
+ else if ((bytes[i] & 0xc0) == 0xc0)
+ {
+ i += 2;
+ }
+ else
+ {
+ i += 1;
+ }
+ }
+
+ char[] cs = new char[length];
+
+ i = 0;
+ length = 0;
+
+ while (i < bytes.length)
+ {
+ char ch;
+
+ if ((bytes[i] & 0xf0) == 0xf0)
+ {
+ int codePoint = ((bytes[i] & 0x03) << 18) | ((bytes[i+1] & 0x3F) << 12) | ((bytes[i+2] & 0x3F) << 6) | (bytes[i+3] & 0x3F);
+ int U = codePoint - 0x10000;
+ char W1 = (char)(0xD800 | (U >> 10));
+ char W2 = (char)(0xDC00 | (U & 0x3FF));
+ cs[length++] = W1;
+ ch = W2;
+ i += 4;
+ }
+ else if ((bytes[i] & 0xe0) == 0xe0)
+ {
+ ch = (char)(((bytes[i] & 0x0f) << 12)
+ | ((bytes[i + 1] & 0x3f) << 6) | (bytes[i + 2] & 0x3f));
+ i += 3;
+ }
+ else if ((bytes[i] & 0xd0) == 0xd0)
+ {
+ ch = (char)(((bytes[i] & 0x1f) << 6) | (bytes[i + 1] & 0x3f));
+ i += 2;
+ }
+ else if ((bytes[i] & 0xc0) == 0xc0)
+ {
+ ch = (char)(((bytes[i] & 0x1f) << 6) | (bytes[i + 1] & 0x3f));
+ i += 2;
+ }
+ else
+ {
+ ch = (char)(bytes[i] & 0xff);
+ i += 1;
+ }
+
+ cs[length++] = ch;
+ }
+
+ return new String(cs);
+ }
+
+ public static byte[] toUTF8ByteArray(String string)
+ {
+ return toUTF8ByteArray(string.toCharArray());
+ }
+
+ public static byte[] toUTF8ByteArray(char[] string)
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ try
+ {
+ toUTF8ByteArray(string, bOut);
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("cannot encode string to byte array!");
+ }
+
+ return bOut.toByteArray();
+ }
+
+ public static void toUTF8ByteArray(char[] string, OutputStream sOut)
+ throws IOException
+ {
+ char[] c = string;
+ int i = 0;
+
+ while (i < c.length)
+ {
+ char ch = c[i];
+
+ if (ch < 0x0080)
+ {
+ sOut.write(ch);
+ }
+ else if (ch < 0x0800)
+ {
+ sOut.write(0xc0 | (ch >> 6));
+ sOut.write(0x80 | (ch & 0x3f));
+ }
+ // surrogate pair
+ else if (ch >= 0xD800 && ch <= 0xDFFF)
+ {
+ // in error - can only happen, if the Java String class has a
+ // bug.
+ if (i + 1 >= c.length)
+ {
+ throw new IllegalStateException("invalid UTF-16 codepoint");
+ }
+ char W1 = ch;
+ ch = c[++i];
+ char W2 = ch;
+ // in error - can only happen, if the Java String class has a
+ // bug.
+ if (W1 > 0xDBFF)
+ {
+ throw new IllegalStateException("invalid UTF-16 codepoint");
+ }
+ int codePoint = (((W1 & 0x03FF) << 10) | (W2 & 0x03FF)) + 0x10000;
+ sOut.write(0xf0 | (codePoint >> 18));
+ sOut.write(0x80 | ((codePoint >> 12) & 0x3F));
+ sOut.write(0x80 | ((codePoint >> 6) & 0x3F));
+ sOut.write(0x80 | (codePoint & 0x3F));
+ }
+ else
+ {
+ sOut.write(0xe0 | (ch >> 12));
+ sOut.write(0x80 | ((ch >> 6) & 0x3F));
+ sOut.write(0x80 | (ch & 0x3F));
+ }
+
+ i++;
+ }
+ }
+
+ /**
+ * A locale independent version of toUpperCase.
+ *
+ * @param string input to be converted
+ * @return a US Ascii uppercase version
+ */
+ public static String toUpperCase(String string)
+ {
+ boolean changed = false;
+ char[] chars = string.toCharArray();
+
+ for (int i = 0; i != chars.length; i++)
+ {
+ char ch = chars[i];
+ if ('a' <= ch && 'z' >= ch)
+ {
+ changed = true;
+ chars[i] = (char)(ch - 'a' + 'A');
+ }
+ }
+
+ if (changed)
+ {
+ return new String(chars);
+ }
+
+ return string;
+ }
+
+ /**
+ * A locale independent version of toLowerCase.
+ *
+ * @param string input to be converted
+ * @return a US ASCII lowercase version
+ */
+ public static String toLowerCase(String string)
+ {
+ boolean changed = false;
+ char[] chars = string.toCharArray();
+
+ for (int i = 0; i != chars.length; i++)
+ {
+ char ch = chars[i];
+ if ('A' <= ch && 'Z' >= ch)
+ {
+ changed = true;
+ chars[i] = (char)(ch - 'A' + 'a');
+ }
+ }
+
+ if (changed)
+ {
+ return new String(chars);
+ }
+
+ return string;
+ }
+
+ public static byte[] toByteArray(char[] chars)
+ {
+ byte[] bytes = new byte[chars.length];
+
+ for (int i = 0; i != bytes.length; i++)
+ {
+ bytes[i] = (byte)chars[i];
+ }
+
+ return bytes;
+ }
+
+ public static byte[] toByteArray(String string)
+ {
+ byte[] bytes = new byte[string.length()];
+
+ for (int i = 0; i != bytes.length; i++)
+ {
+ char ch = string.charAt(i);
+
+ bytes[i] = (byte)ch;
+ }
+
+ return bytes;
+ }
+
+ /**
+ * Convert an array of 8 bit characters into a string.
+ *
+ * @param bytes 8 bit characters.
+ * @return resulting String.
+ */
+ public static String fromByteArray(byte[] bytes)
+ {
+ return new String(asCharArray(bytes));
+ }
+
+ /**
+ * Do a simple conversion of an array of 8 bit characters into a string.
+ *
+ * @param bytes 8 bit characters.
+ * @return resulting String.
+ */
+ public static char[] asCharArray(byte[] bytes)
+ {
+ char[] chars = new char[bytes.length];
+
+ for (int i = 0; i != chars.length; i++)
+ {
+ chars[i] = (char)(bytes[i] & 0xff);
+ }
+
+ return chars;
+ }
+
+ public static String[] split(String input, char delimiter)
+ {
+ Vector v = new Vector();
+ boolean moreTokens = true;
+ String subString;
+
+ while (moreTokens)
+ {
+ int tokenLocation = input.indexOf(delimiter);
+ if (tokenLocation > 0)
+ {
+ subString = input.substring(0, tokenLocation);
+ v.addElement(subString);
+ input = input.substring(tokenLocation + 1);
+ }
+ else
+ {
+ moreTokens = false;
+ v.addElement(input);
+ }
+ }
+
+ String[] res = new String[v.size()];
+
+ for (int i = 0; i != res.length; i++)
+ {
+ res[i] = (String)v.elementAt(i);
+ }
+ return res;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/encoders/Base64.java b/core/src/main/java/org/bouncycastle/util/encoders/Base64.java
new file mode 100644
index 00000000..83806295
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/encoders/Base64.java
@@ -0,0 +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);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/encoders/Base64Encoder.java b/core/src/main/java/org/bouncycastle/util/encoders/Base64Encoder.java
new file mode 100644
index 00000000..1ef8f51b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/encoders/Base64Encoder.java
@@ -0,0 +1,328 @@
+package org.bouncycastle.util.encoders;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class Base64Encoder
+ implements Encoder
+{
+ protected final byte[] encodingTable =
+ {
+ (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+ (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+ (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
+ (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+ (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
+ (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+ (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
+ (byte)'v',
+ (byte)'w', (byte)'x', (byte)'y', (byte)'z',
+ (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6',
+ (byte)'7', (byte)'8', (byte)'9',
+ (byte)'+', (byte)'/'
+ };
+
+ protected byte padding = (byte)'=';
+
+ /*
+ * set up the decoding table.
+ */
+ protected final byte[] decodingTable = new byte[128];
+
+ protected void initialiseDecodingTable()
+ {
+ for (int i = 0; i < decodingTable.length; i++)
+ {
+ decodingTable[i] = (byte)0xff;
+ }
+
+ for (int i = 0; i < encodingTable.length; i++)
+ {
+ decodingTable[encodingTable[i]] = (byte)i;
+ }
+ }
+
+ public Base64Encoder()
+ {
+ initialiseDecodingTable();
+ }
+
+ /**
+ * encode the input data producing a base 64 output stream.
+ *
+ * @return the number of bytes produced.
+ */
+ public int encode(
+ byte[] data,
+ int off,
+ int length,
+ OutputStream out)
+ throws IOException
+ {
+ int modulus = length % 3;
+ int dataLength = (length - modulus);
+ int a1, a2, a3;
+
+ for (int i = off; i < off + dataLength; i += 3)
+ {
+ a1 = data[i] & 0xff;
+ a2 = data[i + 1] & 0xff;
+ a3 = data[i + 2] & 0xff;
+
+ out.write(encodingTable[(a1 >>> 2) & 0x3f]);
+ out.write(encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]);
+ out.write(encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]);
+ out.write(encodingTable[a3 & 0x3f]);
+ }
+
+ /*
+ * process the tail end.
+ */
+ int b1, b2, b3;
+ int d1, d2;
+
+ switch (modulus)
+ {
+ case 0: /* nothing left to do */
+ break;
+ case 1:
+ d1 = data[off + dataLength] & 0xff;
+ b1 = (d1 >>> 2) & 0x3f;
+ b2 = (d1 << 4) & 0x3f;
+
+ out.write(encodingTable[b1]);
+ out.write(encodingTable[b2]);
+ out.write(padding);
+ out.write(padding);
+ break;
+ case 2:
+ d1 = data[off + dataLength] & 0xff;
+ d2 = data[off + dataLength + 1] & 0xff;
+
+ b1 = (d1 >>> 2) & 0x3f;
+ b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f;
+ b3 = (d2 << 2) & 0x3f;
+
+ out.write(encodingTable[b1]);
+ out.write(encodingTable[b2]);
+ out.write(encodingTable[b3]);
+ out.write(padding);
+ break;
+ }
+
+ return (dataLength / 3) * 4 + ((modulus == 0) ? 0 : 4);
+ }
+
+ private boolean ignore(
+ char c)
+ {
+ return (c == '\n' || c =='\r' || c == '\t' || c == ' ');
+ }
+
+ /**
+ * decode the base 64 encoded byte data writing it to the given output stream,
+ * whitespace characters will be ignored.
+ *
+ * @return the number of bytes produced.
+ */
+ public int decode(
+ byte[] data,
+ int off,
+ int length,
+ OutputStream out)
+ throws IOException
+ {
+ byte b1, b2, b3, b4;
+ int outLen = 0;
+
+ int end = off + length;
+
+ while (end > off)
+ {
+ if (!ignore((char)data[end - 1]))
+ {
+ break;
+ }
+
+ end--;
+ }
+
+ int i = off;
+ int finish = end - 4;
+
+ i = nextI(data, i, finish);
+
+ while (i < finish)
+ {
+ b1 = decodingTable[data[i++]];
+
+ i = nextI(data, i, finish);
+
+ b2 = decodingTable[data[i++]];
+
+ i = nextI(data, i, finish);
+
+ b3 = decodingTable[data[i++]];
+
+ i = nextI(data, i, finish);
+
+ b4 = decodingTable[data[i++]];
+
+ if ((b1 | b2 | b3 | b4) < 0)
+ {
+ throw new IOException("invalid characters encountered in base64 data");
+ }
+
+ out.write((b1 << 2) | (b2 >> 4));
+ out.write((b2 << 4) | (b3 >> 2));
+ out.write((b3 << 6) | b4);
+
+ outLen += 3;
+
+ i = nextI(data, i, finish);
+ }
+
+ outLen += decodeLastBlock(out, (char)data[end - 4], (char)data[end - 3], (char)data[end - 2], (char)data[end - 1]);
+
+ return outLen;
+ }
+
+ private int nextI(byte[] data, int i, int finish)
+ {
+ while ((i < finish) && ignore((char)data[i]))
+ {
+ i++;
+ }
+ return i;
+ }
+
+ /**
+ * 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 int decode(
+ String data,
+ OutputStream out)
+ throws IOException
+ {
+ byte b1, b2, b3, b4;
+ int length = 0;
+
+ int end = data.length();
+
+ while (end > 0)
+ {
+ if (!ignore(data.charAt(end - 1)))
+ {
+ break;
+ }
+
+ end--;
+ }
+
+ int i = 0;
+ int finish = end - 4;
+
+ i = nextI(data, i, finish);
+
+ while (i < finish)
+ {
+ b1 = decodingTable[data.charAt(i++)];
+
+ i = nextI(data, i, finish);
+
+ b2 = decodingTable[data.charAt(i++)];
+
+ i = nextI(data, i, finish);
+
+ b3 = decodingTable[data.charAt(i++)];
+
+ i = nextI(data, i, finish);
+
+ b4 = decodingTable[data.charAt(i++)];
+
+ if ((b1 | b2 | b3 | b4) < 0)
+ {
+ throw new IOException("invalid characters encountered in base64 data");
+ }
+
+ out.write((b1 << 2) | (b2 >> 4));
+ out.write((b2 << 4) | (b3 >> 2));
+ out.write((b3 << 6) | b4);
+
+ length += 3;
+
+ i = nextI(data, i, finish);
+ }
+
+ length += decodeLastBlock(out, data.charAt(end - 4), data.charAt(end - 3), data.charAt(end - 2), data.charAt(end - 1));
+
+ return length;
+ }
+
+ private int decodeLastBlock(OutputStream out, char c1, char c2, char c3, char c4)
+ throws IOException
+ {
+ byte b1, b2, b3, b4;
+
+ if (c3 == padding)
+ {
+ b1 = decodingTable[c1];
+ b2 = decodingTable[c2];
+
+ if ((b1 | b2) < 0)
+ {
+ throw new IOException("invalid characters encountered at end of base64 data");
+ }
+
+ out.write((b1 << 2) | (b2 >> 4));
+
+ return 1;
+ }
+ else if (c4 == padding)
+ {
+ b1 = decodingTable[c1];
+ b2 = decodingTable[c2];
+ b3 = decodingTable[c3];
+
+ if ((b1 | b2 | b3) < 0)
+ {
+ throw new IOException("invalid characters encountered at end of base64 data");
+ }
+
+ out.write((b1 << 2) | (b2 >> 4));
+ out.write((b2 << 4) | (b3 >> 2));
+
+ return 2;
+ }
+ else
+ {
+ b1 = decodingTable[c1];
+ b2 = decodingTable[c2];
+ b3 = decodingTable[c3];
+ b4 = decodingTable[c4];
+
+ if ((b1 | b2 | b3 | b4) < 0)
+ {
+ throw new IOException("invalid characters encountered at end of base64 data");
+ }
+
+ out.write((b1 << 2) | (b2 >> 4));
+ out.write((b2 << 4) | (b3 >> 2));
+ out.write((b3 << 6) | b4);
+
+ return 3;
+ }
+ }
+
+ private int nextI(String data, int i, int finish)
+ {
+ while ((i < finish) && ignore(data.charAt(i)))
+ {
+ i++;
+ }
+ return i;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/encoders/BufferedDecoder.java b/core/src/main/java/org/bouncycastle/util/encoders/BufferedDecoder.java
new file mode 100644
index 00000000..672430ac
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/encoders/BufferedDecoder.java
@@ -0,0 +1,96 @@
+package org.bouncycastle.util.encoders;
+
+
+/**
+ * a buffering class to allow translation from one format to another to
+ * be done in discrete chunks.
+ */
+public class BufferedDecoder
+{
+ protected byte[] buf;
+ protected int bufOff;
+
+ protected Translator translator;
+
+ /**
+ * @param translator the translator to use.
+ * @param bufSize amount of input to buffer for each chunk.
+ */
+ public BufferedDecoder(
+ Translator translator,
+ int bufSize)
+ {
+ this.translator = translator;
+
+ if ((bufSize % translator.getEncodedBlockSize()) != 0)
+ {
+ throw new IllegalArgumentException("buffer size not multiple of input block size");
+ }
+
+ buf = new byte[bufSize];
+ bufOff = 0;
+ }
+
+ public int processByte(
+ byte in,
+ byte[] out,
+ int outOff)
+ {
+ int resultLen = 0;
+
+ buf[bufOff++] = in;
+
+ if (bufOff == buf.length)
+ {
+ resultLen = translator.decode(buf, 0, buf.length, out, outOff);
+ bufOff = 0;
+ }
+
+ return resultLen;
+ }
+
+ public int processBytes(
+ byte[] in,
+ int inOff,
+ int len,
+ byte[] out,
+ int outOff)
+ {
+ if (len < 0)
+ {
+ throw new IllegalArgumentException("Can't have a negative input length!");
+ }
+
+ int resultLen = 0;
+ int gapLen = buf.length - bufOff;
+
+ if (len > gapLen)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+ resultLen += translator.decode(buf, 0, buf.length, out, outOff);
+
+ bufOff = 0;
+
+ len -= gapLen;
+ inOff += gapLen;
+ outOff += resultLen;
+
+ int chunkSize = len - (len % buf.length);
+
+ resultLen += translator.decode(in, inOff, chunkSize, out, outOff);
+
+ len -= chunkSize;
+ inOff += chunkSize;
+ }
+
+ if (len != 0)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, len);
+
+ bufOff += len;
+ }
+
+ return resultLen;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/encoders/BufferedEncoder.java b/core/src/main/java/org/bouncycastle/util/encoders/BufferedEncoder.java
new file mode 100644
index 00000000..107eee89
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/encoders/BufferedEncoder.java
@@ -0,0 +1,96 @@
+package org.bouncycastle.util.encoders;
+
+
+/**
+ * a buffering class to allow translation from one format to another to
+ * be done in discrete chunks.
+ */
+public class BufferedEncoder
+{
+ protected byte[] buf;
+ protected int bufOff;
+
+ protected Translator translator;
+
+ /**
+ * @param translator the translator to use.
+ * @param bufSize amount of input to buffer for each chunk.
+ */
+ public BufferedEncoder(
+ Translator translator,
+ int bufSize)
+ {
+ this.translator = translator;
+
+ if ((bufSize % translator.getEncodedBlockSize()) != 0)
+ {
+ throw new IllegalArgumentException("buffer size not multiple of input block size");
+ }
+
+ buf = new byte[bufSize];
+ bufOff = 0;
+ }
+
+ public int processByte(
+ byte in,
+ byte[] out,
+ int outOff)
+ {
+ int resultLen = 0;
+
+ buf[bufOff++] = in;
+
+ if (bufOff == buf.length)
+ {
+ resultLen = translator.encode(buf, 0, buf.length, out, outOff);
+ bufOff = 0;
+ }
+
+ return resultLen;
+ }
+
+ public int processBytes(
+ byte[] in,
+ int inOff,
+ int len,
+ byte[] out,
+ int outOff)
+ {
+ if (len < 0)
+ {
+ throw new IllegalArgumentException("Can't have a negative input length!");
+ }
+
+ int resultLen = 0;
+ int gapLen = buf.length - bufOff;
+
+ if (len > gapLen)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+ resultLen += translator.encode(buf, 0, buf.length, out, outOff);
+
+ bufOff = 0;
+
+ len -= gapLen;
+ inOff += gapLen;
+ outOff += resultLen;
+
+ int chunkSize = len - (len % buf.length);
+
+ resultLen += translator.encode(in, inOff, chunkSize, out, outOff);
+
+ len -= chunkSize;
+ inOff += chunkSize;
+ }
+
+ if (len != 0)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, len);
+
+ bufOff += len;
+ }
+
+ return resultLen;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/encoders/DecoderException.java b/core/src/main/java/org/bouncycastle/util/encoders/DecoderException.java
new file mode 100644
index 00000000..d9914a22
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/encoders/DecoderException.java
@@ -0,0 +1,19 @@
+package org.bouncycastle.util.encoders;
+
+public class DecoderException
+ extends IllegalStateException
+{
+ private Throwable cause;
+
+ DecoderException(String msg, Throwable cause)
+ {
+ super(msg);
+
+ this.cause = cause;
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/encoders/Encoder.java b/core/src/main/java/org/bouncycastle/util/encoders/Encoder.java
new file mode 100644
index 00000000..b0661210
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/encoders/Encoder.java
@@ -0,0 +1,17 @@
+package org.bouncycastle.util.encoders;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Encode and decode byte arrays (typically from binary to 7-bit ASCII
+ * encodings).
+ */
+public interface Encoder
+{
+ int encode(byte[] data, int off, int length, OutputStream out) throws IOException;
+
+ int decode(byte[] data, int off, int length, OutputStream out) throws IOException;
+
+ int decode(String data, OutputStream out) throws IOException;
+}
diff --git a/core/src/main/java/org/bouncycastle/util/encoders/EncoderException.java b/core/src/main/java/org/bouncycastle/util/encoders/EncoderException.java
new file mode 100644
index 00000000..2d09a63a
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/encoders/EncoderException.java
@@ -0,0 +1,19 @@
+package org.bouncycastle.util.encoders;
+
+public class EncoderException
+ extends IllegalStateException
+{
+ private Throwable cause;
+
+ EncoderException(String msg, Throwable cause)
+ {
+ super(msg);
+
+ this.cause = cause;
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/encoders/Hex.java b/core/src/main/java/org/bouncycastle/util/encoders/Hex.java
new file mode 100644
index 00000000..d49f1ef4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/encoders/Hex.java
@@ -0,0 +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);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/encoders/HexEncoder.java b/core/src/main/java/org/bouncycastle/util/encoders/HexEncoder.java
new file mode 100644
index 00000000..3bb594b0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/encoders/HexEncoder.java
@@ -0,0 +1,187 @@
+package org.bouncycastle.util.encoders;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class HexEncoder
+ implements Encoder
+{
+ protected final byte[] encodingTable =
+ {
+ (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7',
+ (byte)'8', (byte)'9', (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f'
+ };
+
+ /*
+ * set up the decoding table.
+ */
+ protected final byte[] decodingTable = new byte[128];
+
+ protected void initialiseDecodingTable()
+ {
+ for (int i = 0; i < decodingTable.length; i++)
+ {
+ decodingTable[i] = (byte)0xff;
+ }
+
+ for (int i = 0; i < encodingTable.length; i++)
+ {
+ decodingTable[encodingTable[i]] = (byte)i;
+ }
+
+ decodingTable['A'] = decodingTable['a'];
+ decodingTable['B'] = decodingTable['b'];
+ decodingTable['C'] = decodingTable['c'];
+ decodingTable['D'] = decodingTable['d'];
+ decodingTable['E'] = decodingTable['e'];
+ decodingTable['F'] = decodingTable['f'];
+ }
+
+ public HexEncoder()
+ {
+ initialiseDecodingTable();
+ }
+
+ /**
+ * encode the input data producing a Hex output stream.
+ *
+ * @return the number of bytes produced.
+ */
+ public int encode(
+ byte[] data,
+ int off,
+ int length,
+ OutputStream out)
+ throws IOException
+ {
+ for (int i = off; i < (off + length); i++)
+ {
+ int v = data[i] & 0xff;
+
+ out.write(encodingTable[(v >>> 4)]);
+ out.write(encodingTable[v & 0xf]);
+ }
+
+ return length * 2;
+ }
+
+ private static boolean ignore(
+ char c)
+ {
+ return c == '\n' || c =='\r' || c == '\t' || c == ' ';
+ }
+
+ /**
+ * decode the Hex encoded byte data writing it to the given output stream,
+ * whitespace characters will be ignored.
+ *
+ * @return the number of bytes produced.
+ */
+ public int decode(
+ byte[] data,
+ int off,
+ int length,
+ OutputStream out)
+ throws IOException
+ {
+ byte b1, b2;
+ int outLen = 0;
+
+ int end = off + length;
+
+ while (end > off)
+ {
+ if (!ignore((char)data[end - 1]))
+ {
+ break;
+ }
+
+ end--;
+ }
+
+ int i = off;
+ while (i < end)
+ {
+ while (i < end && ignore((char)data[i]))
+ {
+ i++;
+ }
+
+ b1 = decodingTable[data[i++]];
+
+ while (i < end && ignore((char)data[i]))
+ {
+ i++;
+ }
+
+ b2 = decodingTable[data[i++]];
+
+ if ((b1 | b2) < 0)
+ {
+ throw new IOException("invalid characters encountered in Hex data");
+ }
+
+ out.write((b1 << 4) | b2);
+
+ outLen++;
+ }
+
+ return outLen;
+ }
+
+ /**
+ * 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 int decode(
+ String data,
+ OutputStream out)
+ throws IOException
+ {
+ byte b1, b2;
+ int length = 0;
+
+ int end = data.length();
+
+ while (end > 0)
+ {
+ if (!ignore(data.charAt(end - 1)))
+ {
+ break;
+ }
+
+ end--;
+ }
+
+ int i = 0;
+ while (i < end)
+ {
+ while (i < end && ignore(data.charAt(i)))
+ {
+ i++;
+ }
+
+ b1 = decodingTable[data.charAt(i++)];
+
+ while (i < end && ignore(data.charAt(i)))
+ {
+ i++;
+ }
+
+ b2 = decodingTable[data.charAt(i++)];
+
+ if ((b1 | b2) < 0)
+ {
+ throw new IOException("invalid characters encountered in Hex string");
+ }
+
+ out.write((b1 << 4) | b2);
+
+ length++;
+ }
+
+ return length;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/encoders/HexTranslator.java b/core/src/main/java/org/bouncycastle/util/encoders/HexTranslator.java
new file mode 100644
index 00000000..3fff65a6
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/encoders/HexTranslator.java
@@ -0,0 +1,87 @@
+package org.bouncycastle.util.encoders;
+
+/**
+ * Converters for going from hex to binary and back. Note: this class assumes ASCII processing.
+ */
+public class HexTranslator
+ implements Translator
+{
+ private static final byte[] hexTable =
+ {
+ (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7',
+ (byte)'8', (byte)'9', (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f'
+ };
+
+ /**
+ * size of the output block on encoding produced by getDecodedBlockSize()
+ * bytes.
+ */
+ public int getEncodedBlockSize()
+ {
+ return 2;
+ }
+
+ public int encode(
+ byte[] in,
+ int inOff,
+ int length,
+ byte[] out,
+ int outOff)
+ {
+ for (int i = 0, j = 0; i < length; i++, j += 2)
+ {
+ out[outOff + j] = hexTable[(in[inOff] >> 4) & 0x0f];
+ out[outOff + j + 1] = hexTable[in[inOff] & 0x0f];
+
+ inOff++;
+ }
+
+ return length * 2;
+ }
+
+ /**
+ * size of the output block on decoding produced by getEncodedBlockSize()
+ * bytes.
+ */
+ public int getDecodedBlockSize()
+ {
+ return 1;
+ }
+
+ public int decode(
+ byte[] in,
+ int inOff,
+ int length,
+ byte[] out,
+ int outOff)
+ {
+ int halfLength = length / 2;
+ byte left, right;
+ for (int i = 0; i < halfLength; i++)
+ {
+ left = in[inOff + i * 2];
+ right = in[inOff + i * 2 + 1];
+
+ if (left < (byte)'a')
+ {
+ out[outOff] = (byte)((left - '0') << 4);
+ }
+ else
+ {
+ out[outOff] = (byte)((left - 'a' + 10) << 4);
+ }
+ if (right < (byte)'a')
+ {
+ out[outOff] += (byte)(right - '0');
+ }
+ else
+ {
+ out[outOff] += (byte)(right - 'a' + 10);
+ }
+
+ outOff++;
+ }
+
+ return halfLength;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/encoders/Translator.java b/core/src/main/java/org/bouncycastle/util/encoders/Translator.java
new file mode 100644
index 00000000..a3a0cb8e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/encoders/Translator.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.util.encoders;
+
+/**
+ * general interface for an translator.
+ */
+public interface Translator
+{
+ /**
+ * size of the output block on encoding produced by getDecodedBlockSize()
+ * bytes.
+ */
+ public int getEncodedBlockSize();
+
+ public int encode(byte[] in, int inOff, int length, byte[] out, int outOff);
+
+ /**
+ * size of the output block on decoding produced by getEncodedBlockSize()
+ * bytes.
+ */
+ public int getDecodedBlockSize();
+
+ public int decode(byte[] in, int inOff, int length, byte[] out, int outOff);
+}
diff --git a/core/src/main/java/org/bouncycastle/util/encoders/UrlBase64.java b/core/src/main/java/org/bouncycastle/util/encoders/UrlBase64.java
new file mode 100644
index 00000000..3b83e956
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/encoders/UrlBase64.java
@@ -0,0 +1,129 @@
+package org.bouncycastle.util.encoders;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Convert binary data to and from UrlBase64 encoding. This is identical to
+ * Base64 encoding, except that the padding character is "." and the other
+ * non-alphanumeric characters are "-" and "_" instead of "+" and "/".
+ * <p>
+ * The purpose of UrlBase64 encoding is to provide a compact encoding of binary
+ * data that is safe for use as an URL parameter. Base64 encoding does not
+ * produce encoded values that are safe for use in URLs, since "/" can be
+ * interpreted as a path delimiter; "+" is the encoded form of a space; and
+ * "=" is used to separate a name from the corresponding value in an URL
+ * parameter.
+ */
+public class UrlBase64
+{
+ private static final Encoder encoder = new UrlBase64Encoder();
+
+ /**
+ * Encode the input data producing a URL safe base 64 encoded byte array.
+ *
+ * @return a byte array containing the URL safe base 64 encoded data.
+ */
+ public static byte[] encode(
+ byte[] data)
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ try
+ {
+ encoder.encode(data, 0, data.length, bOut);
+ }
+ catch (Exception e)
+ {
+ throw new EncoderException("exception encoding URL safe base64 data: " + e.getMessage(), e);
+ }
+
+ return bOut.toByteArray();
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Decode the URL safe base 64 encoded input data - white space will be ignored.
+ *
+ * @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 URL safe base64 string: " + e.getMessage(), e);
+ }
+
+ return bOut.toByteArray();
+ }
+
+ /**
+ * decode the URL safe base 64 encoded byte data writing it to the given output stream,
+ * whitespace characters will be ignored.
+ *
+ * @return the number of bytes produced.
+ */
+ public static int decode(
+ byte[] data,
+ OutputStream out)
+ throws IOException
+ {
+ return encoder.decode(data, 0, data.length, out);
+ }
+
+ /**
+ * decode the URL safe base 64 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 URL safe base64 string: " + e.getMessage(), e);
+ }
+
+ return bOut.toByteArray();
+ }
+
+ /**
+ * Decode the URL safe 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/core/src/main/java/org/bouncycastle/util/encoders/UrlBase64Encoder.java b/core/src/main/java/org/bouncycastle/util/encoders/UrlBase64Encoder.java
new file mode 100644
index 00000000..a5fff5ec
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/encoders/UrlBase64Encoder.java
@@ -0,0 +1,25 @@
+package org.bouncycastle.util.encoders;
+
+/**
+ * Convert binary data to and from UrlBase64 encoding. This is identical to
+ * Base64 encoding, except that the padding character is "." and the other
+ * non-alphanumeric characters are "-" and "_" instead of "+" and "/".
+ * <p>
+ * The purpose of UrlBase64 encoding is to provide a compact encoding of binary
+ * data that is safe for use as an URL parameter. Base64 encoding does not
+ * produce encoded values that are safe for use in URLs, since "/" can be
+ * interpreted as a path delimiter; "+" is the encoded form of a space; and
+ * "=" is used to separate a name from the corresponding value in an URL
+ * parameter.
+ */
+public class UrlBase64Encoder extends Base64Encoder
+{
+ public UrlBase64Encoder()
+ {
+ encodingTable[encodingTable.length - 2] = (byte) '-';
+ encodingTable[encodingTable.length - 1] = (byte) '_';
+ padding = (byte) '.';
+ // we must re-create the decoding table with the new encoded values.
+ initialiseDecodingTable();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/io/StreamOverflowException.java b/core/src/main/java/org/bouncycastle/util/io/StreamOverflowException.java
new file mode 100644
index 00000000..01af8da9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/io/StreamOverflowException.java
@@ -0,0 +1,12 @@
+package org.bouncycastle.util.io;
+
+import java.io.IOException;
+
+public class StreamOverflowException
+ extends IOException
+{
+ public StreamOverflowException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/io/Streams.java b/core/src/main/java/org/bouncycastle/util/io/Streams.java
new file mode 100644
index 00000000..41560b5d
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/io/Streams.java
@@ -0,0 +1,87 @@
+package org.bouncycastle.util.io;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public final class Streams
+{
+ private static int BUFFER_SIZE = 512;
+
+ public static void drain(InputStream inStr)
+ throws IOException
+ {
+ byte[] bs = new byte[BUFFER_SIZE];
+ while (inStr.read(bs, 0, bs.length) >= 0)
+ {
+ }
+ }
+
+ public static byte[] readAll(InputStream inStr)
+ throws IOException
+ {
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ pipeAll(inStr, buf);
+ return buf.toByteArray();
+ }
+
+ public static byte[] readAllLimited(InputStream inStr, int limit)
+ throws IOException
+ {
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ pipeAllLimited(inStr, limit, buf);
+ return buf.toByteArray();
+ }
+
+ public static int readFully(InputStream inStr, byte[] buf)
+ throws IOException
+ {
+ return readFully(inStr, buf, 0, buf.length);
+ }
+
+ public static int readFully(InputStream inStr, byte[] buf, int off, int len)
+ throws IOException
+ {
+ int totalRead = 0;
+ while (totalRead < len)
+ {
+ int numRead = inStr.read(buf, off + totalRead, len - totalRead);
+ if (numRead < 0)
+ {
+ break;
+ }
+ totalRead += numRead;
+ }
+ return totalRead;
+ }
+
+ public static void pipeAll(InputStream inStr, OutputStream outStr)
+ throws IOException
+ {
+ byte[] bs = new byte[BUFFER_SIZE];
+ int numRead;
+ while ((numRead = inStr.read(bs, 0, bs.length)) >= 0)
+ {
+ outStr.write(bs, 0, numRead);
+ }
+ }
+
+ public static long pipeAllLimited(InputStream inStr, long limit, OutputStream outStr)
+ throws IOException
+ {
+ long total = 0;
+ byte[] bs = new byte[BUFFER_SIZE];
+ int numRead;
+ while ((numRead = inStr.read(bs, 0, bs.length)) >= 0)
+ {
+ total += numRead;
+ if (total > limit)
+ {
+ throw new StreamOverflowException("Data Overflow");
+ }
+ outStr.write(bs, 0, numRead);
+ }
+ return total;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/io/TeeInputStream.java b/core/src/main/java/org/bouncycastle/util/io/TeeInputStream.java
new file mode 100644
index 00000000..91542469
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/io/TeeInputStream.java
@@ -0,0 +1,62 @@
+package org.bouncycastle.util.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class TeeInputStream
+ extends InputStream
+{
+ private final InputStream input;
+ private final OutputStream output;
+
+ public TeeInputStream(InputStream input, OutputStream output)
+ {
+ this.input = input;
+ this.output = output;
+ }
+
+ public int read(byte[] buf)
+ throws IOException
+ {
+ return read(buf, 0, buf.length);
+ }
+
+ public int read(byte[] buf, int off, int len)
+ throws IOException
+ {
+ int i = input.read(buf, off, len);
+
+ if (i > 0)
+ {
+ output.write(buf, off, i);
+ }
+
+ return i;
+ }
+
+ public int read()
+ throws IOException
+ {
+ int i = input.read();
+
+ if (i >= 0)
+ {
+ output.write(i);
+ }
+
+ return i;
+ }
+
+ public void close()
+ throws IOException
+ {
+ this.input.close();
+ this.output.close();
+ }
+
+ public OutputStream getOutputStream()
+ {
+ return output;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/io/TeeOutputStream.java b/core/src/main/java/org/bouncycastle/util/io/TeeOutputStream.java
new file mode 100644
index 00000000..a4919cd8
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/io/TeeOutputStream.java
@@ -0,0 +1,52 @@
+package org.bouncycastle.util.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class TeeOutputStream
+ extends OutputStream
+{
+ private OutputStream output1;
+ private OutputStream output2;
+
+ public TeeOutputStream(OutputStream output1, OutputStream output2)
+ {
+ this.output1 = output1;
+ this.output2 = output2;
+ }
+
+ public void write(byte[] buf)
+ throws IOException
+ {
+ this.output1.write(buf);
+ this.output2.write(buf);
+ }
+
+ public void write(byte[] buf, int off, int len)
+ throws IOException
+ {
+ this.output1.write(buf, off, len);
+ this.output2.write(buf, off, len);
+ }
+
+ public void write(int b)
+ throws IOException
+ {
+ this.output1.write(b);
+ this.output2.write(b);
+ }
+
+ public void flush()
+ throws IOException
+ {
+ this.output1.flush();
+ this.output2.flush();
+ }
+
+ public void close()
+ throws IOException
+ {
+ this.output1.close();
+ this.output2.close();
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/bouncycastle/util/io/pem/PemGenerationException.java b/core/src/main/java/org/bouncycastle/util/io/pem/PemGenerationException.java
new file mode 100644
index 00000000..69a773e7
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/io/pem/PemGenerationException.java
@@ -0,0 +1,25 @@
+package org.bouncycastle.util.io.pem;
+
+import java.io.IOException;
+
+public class PemGenerationException
+ extends IOException
+{
+ private Throwable cause;
+
+ public PemGenerationException(String message, Throwable cause)
+ {
+ super(message);
+ this.cause = cause;
+ }
+
+ public PemGenerationException(String message)
+ {
+ super(message);
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/io/pem/PemHeader.java b/core/src/main/java/org/bouncycastle/util/io/pem/PemHeader.java
new file mode 100644
index 00000000..b201c134
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/io/pem/PemHeader.java
@@ -0,0 +1,66 @@
+package org.bouncycastle.util.io.pem;
+
+public class PemHeader
+{
+ private String name;
+ private String value;
+
+ public PemHeader(String name, String value)
+ {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public String getValue()
+ {
+ return value;
+ }
+
+ public int hashCode()
+ {
+ return getHashCode(this.name) + 31 * getHashCode(this.value);
+ }
+
+ public boolean equals(Object o)
+ {
+ if (!(o instanceof PemHeader))
+ {
+ return false;
+ }
+
+ PemHeader other = (PemHeader)o;
+
+ return other == this || (isEqual(this.name, other.name) && isEqual(this.value, other.value));
+ }
+
+ private int getHashCode(String s)
+ {
+ if (s == null)
+ {
+ return 1;
+ }
+
+ return s.hashCode();
+ }
+
+ private boolean isEqual(String s1, String s2)
+ {
+ if (s1 == s2)
+ {
+ return true;
+ }
+
+ if (s1 == null || s2 == null)
+ {
+ return false;
+ }
+
+ return s1.equals(s2);
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/util/io/pem/PemObject.java b/core/src/main/java/org/bouncycastle/util/io/pem/PemObject.java
new file mode 100644
index 00000000..2199520b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/io/pem/PemObject.java
@@ -0,0 +1,61 @@
+package org.bouncycastle.util.io.pem;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class PemObject
+ implements PemObjectGenerator
+{
+ private static final List EMPTY_LIST = Collections.unmodifiableList(new ArrayList());
+
+ private String type;
+ private List headers;
+ private byte[] content;
+
+ /**
+ * Generic constructor for object without headers.
+ *
+ * @param type pem object type.
+ * @param content the binary content of the object.
+ */
+ public PemObject(String type, byte[] content)
+ {
+ this(type, EMPTY_LIST, content);
+ }
+
+ /**
+ * Generic constructor for object with headers.
+ *
+ * @param type pem object type.
+ * @param headers a list of PemHeader objects.
+ * @param content the binary content of the object.
+ */
+ public PemObject(String type, List headers, byte[] content)
+ {
+ this.type = type;
+ this.headers = Collections.unmodifiableList(headers);
+ this.content = content;
+ }
+
+ public String getType()
+ {
+ return type;
+ }
+
+ public List getHeaders()
+ {
+ return headers;
+ }
+
+ public byte[] getContent()
+ {
+ return content;
+ }
+
+ public PemObject generate()
+ throws PemGenerationException
+ {
+ return this;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/io/pem/PemObjectGenerator.java b/core/src/main/java/org/bouncycastle/util/io/pem/PemObjectGenerator.java
new file mode 100644
index 00000000..6fffdc58
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/io/pem/PemObjectGenerator.java
@@ -0,0 +1,7 @@
+package org.bouncycastle.util.io.pem;
+
+public interface PemObjectGenerator
+{
+ PemObject generate()
+ throws PemGenerationException;
+}
diff --git a/core/src/main/java/org/bouncycastle/util/io/pem/PemObjectParser.java b/core/src/main/java/org/bouncycastle/util/io/pem/PemObjectParser.java
new file mode 100644
index 00000000..b18b5505
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/io/pem/PemObjectParser.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.util.io.pem;
+
+import java.io.IOException;
+
+public interface PemObjectParser
+{
+ Object parseObject(PemObject obj)
+ throws IOException;
+}
diff --git a/core/src/main/java/org/bouncycastle/util/io/pem/PemReader.java b/core/src/main/java/org/bouncycastle/util/io/pem/PemReader.java
new file mode 100644
index 00000000..76647257
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/io/pem/PemReader.java
@@ -0,0 +1,84 @@
+package org.bouncycastle.util.io.pem;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.bouncycastle.util.encoders.Base64;
+
+public class PemReader
+ extends BufferedReader
+{
+ private static final String BEGIN = "-----BEGIN ";
+ private static final String END = "-----END ";
+
+ public PemReader(Reader reader)
+ {
+ super(reader);
+ }
+
+ public PemObject readPemObject()
+ throws IOException
+ {
+ String line = readLine();
+
+ while (line != null && !line.startsWith(BEGIN))
+ {
+ line = readLine();
+ }
+
+ if (line != null)
+ {
+ line = line.substring(BEGIN.length());
+ int index = line.indexOf('-');
+ String type = line.substring(0, index);
+
+ if (index > 0)
+ {
+ return loadObject(type);
+ }
+ }
+
+ return null;
+ }
+
+ private PemObject loadObject(String type)
+ throws IOException
+ {
+ String line;
+ String endMarker = END + type;
+ StringBuffer buf = new StringBuffer();
+ List headers = new ArrayList();
+
+ while ((line = readLine()) != null)
+ {
+ if (line.indexOf(":") >= 0)
+ {
+ int index = line.indexOf(':');
+ String hdr = line.substring(0, index);
+ String value = line.substring(index + 1).trim();
+
+ headers.add(new PemHeader(hdr, value));
+
+ continue;
+ }
+
+ if (line.indexOf(endMarker) != -1)
+ {
+ break;
+ }
+
+ buf.append(line.trim());
+ }
+
+ if (line == null)
+ {
+ throw new IOException(endMarker + " not found");
+ }
+
+ return new PemObject(type, headers, Base64.decode(buf.toString()));
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/util/io/pem/PemWriter.java b/core/src/main/java/org/bouncycastle/util/io/pem/PemWriter.java
new file mode 100644
index 00000000..ccefa36e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/io/pem/PemWriter.java
@@ -0,0 +1,137 @@
+package org.bouncycastle.util.io.pem;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Iterator;
+
+import org.bouncycastle.util.encoders.Base64;
+
+/**
+ * A generic PEM writer, based on RFC 1421
+ */
+public class PemWriter
+ extends BufferedWriter
+{
+ private static final int LINE_LENGTH = 64;
+
+ private final int nlLength;
+ private char[] buf = new char[LINE_LENGTH];
+
+ /**
+ * Base constructor.
+ *
+ * @param out output stream to use.
+ */
+ public PemWriter(Writer out)
+ {
+ super(out);
+
+ String nl = System.getProperty("line.separator");
+ if (nl != null)
+ {
+ nlLength = nl.length();
+ }
+ else
+ {
+ nlLength = 2;
+ }
+ }
+
+ /**
+ * Return the number of bytes or characters required to contain the
+ * passed in object if it is PEM encoded.
+ *
+ * @param obj pem object to be output
+ * @return an estimate of the number of bytes
+ */
+ public int getOutputSize(PemObject obj)
+ {
+ // BEGIN and END boundaries.
+ int size = (2 * (obj.getType().length() + 10 + nlLength)) + 6 + 4;
+
+ if (!obj.getHeaders().isEmpty())
+ {
+ for (Iterator it = obj.getHeaders().iterator(); it.hasNext();)
+ {
+ PemHeader hdr = (PemHeader)it.next();
+
+ size += hdr.getName().length() + ": ".length() + hdr.getValue().length() + nlLength;
+ }
+
+ size += nlLength;
+ }
+
+ // base64 encoding
+ int dataLen = ((obj.getContent().length + 2) / 3) * 4;
+
+ size += dataLen + (((dataLen + LINE_LENGTH - 1) / LINE_LENGTH) * nlLength);
+
+ return size;
+ }
+
+ public void writeObject(PemObjectGenerator objGen)
+ throws IOException
+ {
+ PemObject obj = objGen.generate();
+
+ writePreEncapsulationBoundary(obj.getType());
+
+ if (!obj.getHeaders().isEmpty())
+ {
+ for (Iterator it = obj.getHeaders().iterator(); it.hasNext();)
+ {
+ PemHeader hdr = (PemHeader)it.next();
+
+ this.write(hdr.getName());
+ this.write(": ");
+ this.write(hdr.getValue());
+ this.newLine();
+ }
+
+ this.newLine();
+ }
+
+ writeEncoded(obj.getContent());
+ writePostEncapsulationBoundary(obj.getType());
+ }
+
+ private void writeEncoded(byte[] bytes)
+ throws IOException
+ {
+ bytes = Base64.encode(bytes);
+
+ for (int i = 0; i < bytes.length; i += buf.length)
+ {
+ int index = 0;
+
+ while (index != buf.length)
+ {
+ if ((i + index) >= bytes.length)
+ {
+ break;
+ }
+ buf[index] = (char)bytes[i + index];
+ index++;
+ }
+ this.write(buf, 0, index);
+ this.newLine();
+ }
+ }
+
+ private void writePreEncapsulationBoundary(
+ String type)
+ throws IOException
+ {
+ this.write("-----BEGIN " + type + "-----");
+ this.newLine();
+ }
+
+ private void writePostEncapsulationBoundary(
+ String type)
+ throws IOException
+ {
+ this.write("-----END " + type + "-----");
+ this.newLine();
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/test/FixedSecureRandom.java b/core/src/main/java/org/bouncycastle/util/test/FixedSecureRandom.java
new file mode 100644
index 00000000..49bd7301
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/test/FixedSecureRandom.java
@@ -0,0 +1,135 @@
+package org.bouncycastle.util.test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.SecureRandom;
+
+public class FixedSecureRandom
+ extends SecureRandom
+{
+ private byte[] _data;
+
+ private int _index;
+ private int _intPad;
+
+ public FixedSecureRandom(byte[] value)
+ {
+ this(false, new byte[][] { value });
+ }
+
+ public FixedSecureRandom(
+ byte[][] values)
+ {
+ this(false, values);
+ }
+
+ /**
+ * Pad the data on integer boundaries. This is necessary for the classpath project's BigInteger
+ * implementation.
+ */
+ public FixedSecureRandom(
+ boolean intPad,
+ byte[] value)
+ {
+ this(intPad, new byte[][] { value });
+ }
+
+ /**
+ * Pad the data on integer boundaries. This is necessary for the classpath project's BigInteger
+ * implementation.
+ */
+ public FixedSecureRandom(
+ boolean intPad,
+ byte[][] values)
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ for (int i = 0; i != values.length; i++)
+ {
+ try
+ {
+ bOut.write(values[i]);
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("can't save value array.");
+ }
+ }
+
+ _data = bOut.toByteArray();
+
+ if (intPad)
+ {
+ _intPad = _data.length % 4;
+ }
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ System.arraycopy(_data, _index, bytes, 0, bytes.length);
+
+ _index += bytes.length;
+ }
+
+ //
+ // classpath's implementation of SecureRandom doesn't currently go back to nextBytes
+ // when next is called. We can't override next as it's a final method.
+ //
+ public int nextInt()
+ {
+ int val = 0;
+
+ val |= nextValue() << 24;
+ val |= nextValue() << 16;
+
+ if (_intPad == 2)
+ {
+ _intPad--;
+ }
+ else
+ {
+ val |= nextValue() << 8;
+ }
+
+ if (_intPad == 1)
+ {
+ _intPad--;
+ }
+ else
+ {
+ val |= nextValue();
+ }
+
+ return val;
+ }
+
+ //
+ // classpath's implementation of SecureRandom doesn't currently go back to nextBytes
+ // when next is called. We can't override next as it's a final method.
+ //
+ public long nextLong()
+ {
+ long val = 0;
+
+ val |= (long)nextValue() << 56;
+ val |= (long)nextValue() << 48;
+ val |= (long)nextValue() << 40;
+ val |= (long)nextValue() << 32;
+ val |= (long)nextValue() << 24;
+ val |= (long)nextValue() << 16;
+ val |= (long)nextValue() << 8;
+ val |= (long)nextValue();
+
+ return val;
+ }
+
+ public boolean isExhausted()
+ {
+ return _index == _data.length;
+ }
+
+ private int nextValue()
+ {
+ return _data[_index++] & 0xff;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/test/NumberParsing.java b/core/src/main/java/org/bouncycastle/util/test/NumberParsing.java
new file mode 100644
index 00000000..a060dd8e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/test/NumberParsing.java
@@ -0,0 +1,34 @@
+package org.bouncycastle.util.test;
+
+/**
+ * Parsing
+ */
+public final class NumberParsing
+{
+ private NumberParsing()
+ {
+ // Hide constructor
+ }
+
+ public static long decodeLongFromHex(String longAsString)
+ {
+ if ((longAsString.charAt(1) == 'x')
+ || (longAsString.charAt(1) == 'X'))
+ {
+ return Long.parseLong(longAsString.substring(2), 16);
+ }
+
+ return Long.parseLong(longAsString, 16);
+ }
+
+ public static int decodeIntFromHex(String intAsString)
+ {
+ if ((intAsString.charAt(1) == 'x')
+ || (intAsString.charAt(1) == 'X'))
+ {
+ return Integer.parseInt(intAsString.substring(2), 16);
+ }
+
+ return Integer.parseInt(intAsString, 16);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/test/SimpleTest.java b/core/src/main/java/org/bouncycastle/util/test/SimpleTest.java
new file mode 100644
index 00000000..ef8ee617
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/test/SimpleTest.java
@@ -0,0 +1,84 @@
+package org.bouncycastle.util.test;
+
+import java.io.PrintStream;
+
+import org.bouncycastle.util.Arrays;
+
+public abstract class SimpleTest
+ implements Test
+{
+ public abstract String getName();
+
+ private TestResult success()
+ {
+ return SimpleTestResult.successful(this, "Okay");
+ }
+
+ protected void fail(
+ String message)
+ {
+ throw new TestFailedException(SimpleTestResult.failed(this, message));
+ }
+
+ protected void fail(
+ String message,
+ Throwable throwable)
+ {
+ throw new TestFailedException(SimpleTestResult.failed(this, message, throwable));
+ }
+
+ protected void fail(
+ String message,
+ Object expected,
+ Object found)
+ {
+ throw new TestFailedException(SimpleTestResult.failed(this, message, expected, found));
+ }
+
+ protected boolean areEqual(
+ byte[] a,
+ byte[] b)
+ {
+ return Arrays.areEqual(a, b);
+ }
+
+ public TestResult perform()
+ {
+ try
+ {
+ performTest();
+
+ return success();
+ }
+ catch (TestFailedException e)
+ {
+ return e.getResult();
+ }
+ catch (Exception e)
+ {
+ return SimpleTestResult.failed(this, "Exception: " + e, e);
+ }
+ }
+
+ protected static void runTest(
+ Test test)
+ {
+ runTest(test, System.out);
+ }
+
+ protected static void runTest(
+ Test test,
+ PrintStream out)
+ {
+ TestResult result = test.perform();
+
+ out.println(result.toString());
+ if (result.getException() != null)
+ {
+ result.getException().printStackTrace(out);
+ }
+ }
+
+ public abstract void performTest()
+ throws Exception;
+}
diff --git a/core/src/main/java/org/bouncycastle/util/test/SimpleTestResult.java b/core/src/main/java/org/bouncycastle/util/test/SimpleTestResult.java
new file mode 100644
index 00000000..bd69fe8e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/test/SimpleTestResult.java
@@ -0,0 +1,80 @@
+package org.bouncycastle.util.test;
+
+public class SimpleTestResult implements TestResult
+{
+ private static final String SEPARATOR = System.getProperty("line.separator");
+
+ private boolean success;
+ private String message;
+ private Throwable exception;
+
+ public SimpleTestResult(boolean success, String message)
+ {
+ this.success = success;
+ this.message = message;
+ }
+
+ public SimpleTestResult(boolean success, String message, Throwable exception)
+ {
+ this.success = success;
+ this.message = message;
+ this.exception = exception;
+ }
+
+ public static TestResult successful(
+ Test test,
+ String message)
+ {
+ return new SimpleTestResult(true, test.getName() + ": " + message);
+ }
+
+ public static TestResult failed(
+ Test test,
+ String message)
+ {
+ return new SimpleTestResult(false, test.getName() + ": " + message);
+ }
+
+ public static TestResult failed(
+ Test test,
+ String message,
+ Throwable t)
+ {
+ return new SimpleTestResult(false, test.getName() + ": " + message, t);
+ }
+
+ public static TestResult failed(
+ Test test,
+ String message,
+ Object expected,
+ Object found)
+ {
+ return failed(test, message + SEPARATOR + "Expected: " + expected + SEPARATOR + "Found : " + found);
+ }
+
+ public static String failedMessage(String algorithm, String testName, String expected,
+ String actual)
+ {
+ StringBuffer sb = new StringBuffer(algorithm);
+ sb.append(" failing ").append(testName);
+ sb.append(SEPARATOR).append(" expected: ").append(expected);
+ sb.append(SEPARATOR).append(" got : ").append(actual);
+
+ return sb.toString();
+ }
+
+ public boolean isSuccessful()
+ {
+ return success;
+ }
+
+ public String toString()
+ {
+ return message;
+ }
+
+ public Throwable getException()
+ {
+ return exception;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/test/Test.java b/core/src/main/java/org/bouncycastle/util/test/Test.java
new file mode 100644
index 00000000..e631cd02
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/test/Test.java
@@ -0,0 +1,8 @@
+package org.bouncycastle.util.test;
+
+public interface Test
+{
+ String getName();
+
+ TestResult perform();
+}
diff --git a/core/src/main/java/org/bouncycastle/util/test/TestFailedException.java b/core/src/main/java/org/bouncycastle/util/test/TestFailedException.java
new file mode 100644
index 00000000..21e95d37
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/test/TestFailedException.java
@@ -0,0 +1,18 @@
+package org.bouncycastle.util.test;
+
+public class TestFailedException
+ extends RuntimeException
+{
+ private TestResult _result;
+
+ public TestFailedException(
+ TestResult result)
+ {
+ _result = result;
+ }
+
+ public TestResult getResult()
+ {
+ return _result;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/test/TestResult.java b/core/src/main/java/org/bouncycastle/util/test/TestResult.java
new file mode 100644
index 00000000..0a1885f0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/test/TestResult.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.util.test;
+
+public interface TestResult
+{
+ public boolean isSuccessful();
+
+ public Throwable getException();
+
+ public String toString();
+}
diff --git a/core/src/main/java/org/bouncycastle/util/test/UncloseableOutputStream.java b/core/src/main/java/org/bouncycastle/util/test/UncloseableOutputStream.java
new file mode 100644
index 00000000..89073d71
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/test/UncloseableOutputStream.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.util.test;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class UncloseableOutputStream extends FilterOutputStream
+{
+ public UncloseableOutputStream(OutputStream s)
+ {
+ super(s);
+ }
+
+ public void close()
+ {
+ throw new RuntimeException("close() called on UncloseableOutputStream");
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException
+ {
+ out.write(b, off, len);
+ }
+ }